@karimov-labs/backstage-plugin-devxp 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,7 +32,7 @@ Built for use with the [DevXP](https://devxp.net) developer analytics platform.
32
32
  | Dependency | Version |
33
33
  |---|---|
34
34
  | Backstage | >= 1.30 |
35
- | `@karimov-labs/backstage-plugin-devxp-backend` | `^1.0.0` |
35
+ | `@karimov-labs/backstage-plugin-devxp-backend` | `^1.1.0` |
36
36
  | React | `^18` |
37
37
 
38
38
  ---
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback, useEffect } from 'react';
1
+ import React__default, { useState, useCallback, useEffect } from 'react';
2
2
  import { Typography, Grid, Box, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Chip, TextField, Button } from '@material-ui/core';
3
3
  import { makeStyles } from '@material-ui/core/styles';
4
4
  import CheckCircleIcon from '@material-ui/icons/CheckCircle';
@@ -94,44 +94,44 @@ const DashboardContent = ({ api }) => {
94
94
  if (e.key === "Enter") handleUnmask();
95
95
  };
96
96
  if (loading) {
97
- return /* @__PURE__ */ React.createElement(Typography, null, "Loading configuration...");
97
+ return /* @__PURE__ */ React__default.createElement(Typography, null, "Loading configuration...");
98
98
  }
99
- return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Configuration Status" }, config ? /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
99
+ return /* @__PURE__ */ React__default.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Configuration Status" }, config ? /* @__PURE__ */ React__default.createElement(Box, null, /* @__PURE__ */ React__default.createElement(
100
100
  ConfigItem,
101
101
  {
102
102
  label: "Mode",
103
103
  value: config.masked ? "Masked" : "Unmasked",
104
104
  ok: true
105
105
  }
106
- ), /* @__PURE__ */ React.createElement(
106
+ ), /* @__PURE__ */ React__default.createElement(
107
107
  ConfigItem,
108
108
  {
109
109
  label: "Salt",
110
110
  value: config.saltConfigured ? "Configured" : "Not configured",
111
111
  ok: config.saltConfigured
112
112
  }
113
- ), /* @__PURE__ */ React.createElement(
113
+ ), /* @__PURE__ */ React__default.createElement(
114
114
  ConfigItem,
115
115
  {
116
116
  label: "API Endpoint",
117
117
  value: config.apiEndpointConfigured ? "Configured" : "Not configured",
118
118
  ok: config.apiEndpointConfigured
119
119
  }
120
- ), /* @__PURE__ */ React.createElement(
120
+ ), /* @__PURE__ */ React__default.createElement(
121
121
  ConfigItem,
122
122
  {
123
123
  label: "API Token",
124
124
  value: config.apiTokenConfigured ? "Configured" : "Not configured",
125
125
  ok: config.apiTokenConfigured
126
126
  }
127
- ), /* @__PURE__ */ React.createElement(
127
+ ), /* @__PURE__ */ React__default.createElement(
128
128
  ConfigItem,
129
129
  {
130
130
  label: "Project ID",
131
131
  value: config.projectIdConfigured ? "Configured" : "Not configured",
132
132
  ok: config.projectIdConfigured
133
133
  }
134
- )) : /* @__PURE__ */ React.createElement(Typography, { color: "error" }, "Unable to load configuration. Is the backend plugin installed?"))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Developer Mappings" }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(Typography, { variant: "h3" }, config?.mappingCount ?? 0), /* @__PURE__ */ React.createElement(
134
+ )) : /* @__PURE__ */ React__default.createElement(Typography, { color: "error" }, "Unable to load configuration. Is the backend plugin installed?"))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Developer Mappings" }, /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "h3" }, config?.mappingCount ?? 0), /* @__PURE__ */ React__default.createElement(
135
135
  Typography,
136
136
  {
137
137
  variant: "body1",
@@ -139,14 +139,14 @@ const DashboardContent = ({ api }) => {
139
139
  color: "textSecondary"
140
140
  },
141
141
  "developer name mappings stored"
142
- )), /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Upload developer names via CSV or configure GitHub auto-sync in the Settings tab to populate the mapping database.")))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "GitHub Auto-Sync Configurations" }, syncConfigsLoading ? /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Loading sync configurations...") : syncConfigs.length === 0 ? /* @__PURE__ */ React.createElement(Typography, { className: classes.emptySync, variant: "body2" }, "No GitHub sync configurations. Go to Settings to register a GitHub App for automatic member syncing.") : /* @__PURE__ */ React.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Organization"), /* @__PURE__ */ React.createElement(TableCell, null, "GitHub Host"), /* @__PURE__ */ React.createElement(TableCell, null, "Client ID"), /* @__PURE__ */ React.createElement(TableCell, null, "Status"), /* @__PURE__ */ React.createElement(TableCell, null, "Last Synced"), /* @__PURE__ */ React.createElement(TableCell, null, "Registered"))), /* @__PURE__ */ React.createElement(TableBody, null, syncConfigs.map((cfg) => /* @__PURE__ */ React.createElement(TableRow, { key: cfg.id }, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement("strong", null, cfg.org_name)), /* @__PURE__ */ React.createElement(TableCell, { style: { fontSize: "0.85em" } }, cfg.github_hostname ?? "github.com"), /* @__PURE__ */ React.createElement(TableCell, { style: { fontFamily: "monospace", fontSize: "0.85em" } }, cfg.app_client_id), /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(
142
+ )), /* @__PURE__ */ React__default.createElement(Box, { mt: 2 }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Upload developer names via CSV or configure GitHub auto-sync in the Settings tab to populate the mapping database.")))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "GitHub Auto-Sync Configurations" }, syncConfigsLoading ? /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Loading sync configurations...") : syncConfigs.length === 0 ? /* @__PURE__ */ React__default.createElement(Typography, { className: classes.emptySync, variant: "body2" }, "No GitHub sync configurations. Go to Settings to register a GitHub App for automatic member syncing.") : /* @__PURE__ */ React__default.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React__default.createElement(Table, { size: "small" }, /* @__PURE__ */ React__default.createElement(TableHead, null, /* @__PURE__ */ React__default.createElement(TableRow, null, /* @__PURE__ */ React__default.createElement(TableCell, null, "Organization"), /* @__PURE__ */ React__default.createElement(TableCell, null, "GitHub Host"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Client ID"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Status"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Last Synced"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Registered"))), /* @__PURE__ */ React__default.createElement(TableBody, null, syncConfigs.map((cfg) => /* @__PURE__ */ React__default.createElement(TableRow, { key: cfg.id }, /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement("strong", null, cfg.org_name)), /* @__PURE__ */ React__default.createElement(TableCell, { style: { fontSize: "0.85em" } }, cfg.github_hostname ?? "github.com"), /* @__PURE__ */ React__default.createElement(TableCell, { style: { fontFamily: "monospace", fontSize: "0.85em" } }, cfg.app_client_id), /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(
143
143
  Chip,
144
144
  {
145
145
  label: cfg.active ? "Active" : "Inactive",
146
146
  size: "small",
147
147
  className: cfg.active ? classes.activeChip : classes.inactiveChip
148
148
  }
149
- )), /* @__PURE__ */ React.createElement(TableCell, null, cfg.last_synced_at ? /* @__PURE__ */ React.createElement(Typography, { className: classes.lastSynced }, new Date(cfg.last_synced_at).toLocaleString()) : /* @__PURE__ */ React.createElement(Typography, { className: classes.lastSynced }, "Never")), /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { className: classes.lastSynced }, new Date(cfg.created_at).toLocaleDateString()))))))))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Unmask Developer Name" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, "Enter a masked developer username (16-character hex hash) to look up the original name from the mapping database."), /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "flex-start" }, /* @__PURE__ */ React.createElement(
149
+ )), /* @__PURE__ */ React__default.createElement(TableCell, null, cfg.last_synced_at ? /* @__PURE__ */ React__default.createElement(Typography, { className: classes.lastSynced }, new Date(cfg.last_synced_at).toLocaleString()) : /* @__PURE__ */ React__default.createElement(Typography, { className: classes.lastSynced }, "Never")), /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.lastSynced }, new Date(cfg.created_at).toLocaleDateString()))))))))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Unmask Developer Name" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, "Enter a masked developer username (16-character hex hash) to look up the original name from the mapping database."), /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "flex-start" }, /* @__PURE__ */ React__default.createElement(
150
150
  TextField,
151
151
  {
152
152
  label: "Masked username",
@@ -159,7 +159,7 @@ const DashboardContent = ({ api }) => {
159
159
  style: { minWidth: 300 },
160
160
  inputProps: { maxLength: 16 }
161
161
  }
162
- ), /* @__PURE__ */ React.createElement(
162
+ ), /* @__PURE__ */ React__default.createElement(
163
163
  Button,
164
164
  {
165
165
  variant: "contained",
@@ -169,28 +169,28 @@ const DashboardContent = ({ api }) => {
169
169
  style: { marginLeft: 12, height: 40 }
170
170
  },
171
171
  "Unmask"
172
- )), unmaskResult && /* @__PURE__ */ React.createElement(Box, { className: classes.unmaskResult }, /* @__PURE__ */ React.createElement(Typography, { className: classes.resultLabel, variant: "body2" }, "Masked:"), /* @__PURE__ */ React.createElement(Typography, { className: classes.resultValue }, unmaskResult.maskedName), /* @__PURE__ */ React.createElement(Box, { mt: 1 }, /* @__PURE__ */ React.createElement(Typography, { className: classes.resultLabel, variant: "body2" }, "Real Name:"), /* @__PURE__ */ React.createElement(Typography, { className: classes.resultValue }, unmaskResult.realName ? /* @__PURE__ */ React.createElement(
172
+ )), unmaskResult && /* @__PURE__ */ React__default.createElement(Box, { className: classes.unmaskResult }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.resultLabel, variant: "body2" }, "Masked:"), /* @__PURE__ */ React__default.createElement(Typography, { className: classes.resultValue }, unmaskResult.maskedName), /* @__PURE__ */ React__default.createElement(Box, { mt: 1 }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.resultLabel, variant: "body2" }, "Real Name:"), /* @__PURE__ */ React__default.createElement(Typography, { className: classes.resultValue }, unmaskResult.realName ? /* @__PURE__ */ React__default.createElement(
173
173
  Chip,
174
174
  {
175
175
  label: unmaskResult.realName,
176
176
  color: "primary",
177
177
  variant: "outlined"
178
178
  }
179
- ) : /* @__PURE__ */ React.createElement(
179
+ ) : /* @__PURE__ */ React__default.createElement(
180
180
  Chip,
181
181
  {
182
182
  label: "No mapping found",
183
183
  color: "default",
184
184
  variant: "outlined"
185
185
  }
186
- )))), unmaskError && /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(Typography, { color: "error" }, unmaskError)))));
186
+ )))), unmaskError && /* @__PURE__ */ React__default.createElement(Box, { mt: 2 }, /* @__PURE__ */ React__default.createElement(Typography, { color: "error" }, unmaskError)))));
187
187
  };
188
188
  function ConfigItem({
189
189
  label,
190
190
  value,
191
191
  ok
192
192
  }) {
193
- return /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", mb: 1 }, ok ? /* @__PURE__ */ React.createElement(CheckCircleIcon, { style: { color: "#4caf50", marginRight: 8 }, fontSize: "small" }) : /* @__PURE__ */ React.createElement(ErrorIcon, { style: { color: "#f44336", marginRight: 8 }, fontSize: "small" }), /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement("strong", null, label, ":"), " ", value));
193
+ return /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "center", mb: 1 }, ok ? /* @__PURE__ */ React__default.createElement(CheckCircleIcon, { style: { color: "#4caf50", marginRight: 8 }, fontSize: "small" }) : /* @__PURE__ */ React__default.createElement(ErrorIcon, { style: { color: "#f44336", marginRight: 8 }, fontSize: "small" }), /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React__default.createElement("strong", null, label, ":"), " ", value));
194
194
  }
195
195
 
196
196
  export { DashboardContent };
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardContent.esm.js","sources":["../../src/components/DashboardContent.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport {\n Typography,\n TextField,\n Button,\n Grid,\n Box,\n Chip,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport CheckCircleIcon from '@material-ui/icons/CheckCircle';\nimport ErrorIcon from '@material-ui/icons/Error';\nimport { InfoCard } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { DevxpConfig, UnmaskResult, GithubSyncConfig } from '../types';\n\nconst useStyles = makeStyles(theme => ({\n configGrid: {\n marginBottom: theme.spacing(3),\n },\n statusChip: {\n marginLeft: theme.spacing(1),\n },\n unmaskResult: {\n marginTop: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: theme.palette.background.default,\n borderRadius: theme.shape.borderRadius,\n },\n resultLabel: {\n color: theme.palette.text.secondary,\n marginBottom: theme.spacing(0.5),\n },\n resultValue: {\n fontFamily: 'monospace',\n fontSize: '1.1em',\n },\n activeChip: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n inactiveChip: {\n backgroundColor: '#f5f5f5',\n color: '#757575',\n },\n lastSynced: {\n fontSize: '0.75rem',\n color: theme.palette.text.secondary,\n },\n emptySync: {\n padding: theme.spacing(2),\n color: theme.palette.text.secondary,\n fontStyle: 'italic',\n },\n}));\n\ninterface DashboardContentProps {\n api: DevxpApi;\n}\n\nexport const DashboardContent = ({ api }: DashboardContentProps) => {\n const classes = useStyles();\n const [config, setConfig] = useState<DevxpConfig | null>(null);\n const [loading, setLoading] = useState(true);\n const [maskedInput, setMaskedInput] = useState('');\n const [unmaskResult, setUnmaskResult] = useState<UnmaskResult | null>(null);\n const [unmaskError, setUnmaskError] = useState('');\n const [syncConfigs, setSyncConfigs] = useState<GithubSyncConfig[]>([]);\n const [syncConfigsLoading, setSyncConfigsLoading] = useState(true);\n\n const loadConfig = useCallback(async () => {\n try {\n setLoading(true);\n const cfg = await api.getConfig();\n setConfig(cfg);\n } catch (e) {\n // Config not available\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n const loadSyncConfigs = useCallback(async () => {\n try {\n setSyncConfigsLoading(true);\n const result = await api.getGithubSyncConfigs();\n setSyncConfigs(result.configs);\n } catch {\n // failed silently\n } finally {\n setSyncConfigsLoading(false);\n }\n }, [api]);\n\n useEffect(() => {\n loadConfig();\n loadSyncConfigs();\n // Trigger auto-sync on page load (throttled to 24h server-side)\n api.triggerAutoSync().catch(() => {/* silent */});\n }, [loadConfig, loadSyncConfigs, api]);\n\n const handleUnmask = async () => {\n if (!maskedInput.trim()) return;\n setUnmaskError('');\n setUnmaskResult(null);\n try {\n const result = await api.unmask(maskedInput.trim());\n setUnmaskResult(result);\n } catch (e: any) {\n setUnmaskError(e.message || 'Failed to unmask');\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') handleUnmask();\n };\n\n if (loading) {\n return <Typography>Loading configuration...</Typography>;\n }\n\n return (\n <Grid container spacing={3}>\n {/* Configuration Status */}\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Configuration Status\">\n {config ? (\n <Box>\n <ConfigItem\n label=\"Mode\"\n value={config.masked ? 'Masked' : 'Unmasked'}\n ok\n />\n <ConfigItem\n label=\"Salt\"\n value={config.saltConfigured ? 'Configured' : 'Not configured'}\n ok={config.saltConfigured}\n />\n <ConfigItem\n label=\"API Endpoint\"\n value={config.apiEndpointConfigured ? 'Configured' : 'Not configured'}\n ok={config.apiEndpointConfigured}\n />\n <ConfigItem\n label=\"API Token\"\n value={config.apiTokenConfigured ? 'Configured' : 'Not configured'}\n ok={config.apiTokenConfigured}\n />\n <ConfigItem\n label=\"Project ID\"\n value={config.projectIdConfigured ? 'Configured' : 'Not configured'}\n ok={config.projectIdConfigured}\n />\n </Box>\n ) : (\n <Typography color=\"error\">\n Unable to load configuration. Is the backend plugin installed?\n </Typography>\n )}\n </InfoCard>\n </Grid>\n\n {/* Statistics */}\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Developer Mappings\">\n <Box display=\"flex\" alignItems=\"center\">\n <Typography variant=\"h3\">\n {config?.mappingCount ?? 0}\n </Typography>\n <Typography\n variant=\"body1\"\n style={{ marginLeft: 8 }}\n color=\"textSecondary\"\n >\n developer name mappings stored\n </Typography>\n </Box>\n <Box mt={2}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Upload developer names via CSV or configure GitHub auto-sync in the\n Settings tab to populate the mapping database.\n </Typography>\n </Box>\n </InfoCard>\n </Grid>\n\n {/* GitHub Auto-Sync Configurations */}\n <Grid item xs={12}>\n <InfoCard title=\"GitHub Auto-Sync Configurations\">\n {syncConfigsLoading ? (\n <Typography variant=\"body2\" color=\"textSecondary\">\n Loading sync configurations...\n </Typography>\n ) : syncConfigs.length === 0 ? (\n <Typography className={classes.emptySync} variant=\"body2\">\n No GitHub sync configurations. Go to Settings to register a GitHub App for automatic member syncing.\n </Typography>\n ) : (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Organization</TableCell>\n <TableCell>GitHub Host</TableCell>\n <TableCell>Client ID</TableCell>\n <TableCell>Status</TableCell>\n <TableCell>Last Synced</TableCell>\n <TableCell>Registered</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {syncConfigs.map(cfg => (\n <TableRow key={cfg.id}>\n <TableCell>\n <strong>{cfg.org_name}</strong>\n </TableCell>\n <TableCell style={{ fontSize: '0.85em' }}>\n {cfg.github_hostname ?? 'github.com'}\n </TableCell>\n <TableCell style={{ fontFamily: 'monospace', fontSize: '0.85em' }}>\n {cfg.app_client_id}\n </TableCell>\n <TableCell>\n <Chip\n label={cfg.active ? 'Active' : 'Inactive'}\n size=\"small\"\n className={cfg.active ? classes.activeChip : classes.inactiveChip}\n />\n </TableCell>\n <TableCell>\n {cfg.last_synced_at ? (\n <Typography className={classes.lastSynced}>\n {new Date(cfg.last_synced_at).toLocaleString()}\n </Typography>\n ) : (\n <Typography className={classes.lastSynced}>Never</Typography>\n )}\n </TableCell>\n <TableCell>\n <Typography className={classes.lastSynced}>\n {new Date(cfg.created_at).toLocaleDateString()}\n </Typography>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n )}\n </InfoCard>\n </Grid>\n\n {/* Unmask Tester */}\n <Grid item xs={12}>\n <InfoCard title=\"Unmask Developer Name\">\n <Typography variant=\"body2\" color=\"textSecondary\" paragraph>\n Enter a masked developer username (16-character hex hash) to look up\n the original name from the mapping database.\n </Typography>\n <Box display=\"flex\" alignItems=\"flex-start\">\n <TextField\n label=\"Masked username\"\n value={maskedInput}\n onChange={e => setMaskedInput(e.target.value)}\n onKeyDown={handleKeyDown}\n variant=\"outlined\"\n size=\"small\"\n placeholder=\"e.g. a1b2c3d4e5f67890\"\n style={{ minWidth: 300 }}\n inputProps={{ maxLength: 16 }}\n />\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleUnmask}\n disabled={!maskedInput.trim()}\n style={{ marginLeft: 12, height: 40 }}\n >\n Unmask\n </Button>\n </Box>\n {unmaskResult && (\n <Box className={classes.unmaskResult}>\n <Typography className={classes.resultLabel} variant=\"body2\">\n Masked:\n </Typography>\n <Typography className={classes.resultValue}>\n {unmaskResult.maskedName}\n </Typography>\n <Box mt={1}>\n <Typography className={classes.resultLabel} variant=\"body2\">\n Real Name:\n </Typography>\n <Typography className={classes.resultValue}>\n {unmaskResult.realName ? (\n <Chip\n label={unmaskResult.realName}\n color=\"primary\"\n variant=\"outlined\"\n />\n ) : (\n <Chip\n label=\"No mapping found\"\n color=\"default\"\n variant=\"outlined\"\n />\n )}\n </Typography>\n </Box>\n </Box>\n )}\n {unmaskError && (\n <Box mt={2}>\n <Typography color=\"error\">{unmaskError}</Typography>\n </Box>\n )}\n </InfoCard>\n </Grid>\n </Grid>\n );\n};\n\nfunction ConfigItem({\n label,\n value,\n ok,\n}: {\n label: string;\n value: string;\n ok: boolean;\n}) {\n return (\n <Box display=\"flex\" alignItems=\"center\" mb={1}>\n {ok ? (\n <CheckCircleIcon style={{ color: '#4caf50', marginRight: 8 }} fontSize=\"small\" />\n ) : (\n <ErrorIcon style={{ color: '#f44336', marginRight: 8 }} fontSize=\"small\" />\n )}\n <Typography variant=\"body2\">\n <strong>{label}:</strong> {value}\n </Typography>\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DashboardContent.esm.js","sources":["../../src/components/DashboardContent.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport {\n Typography,\n TextField,\n Button,\n Grid,\n Box,\n Chip,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport CheckCircleIcon from '@material-ui/icons/CheckCircle';\nimport ErrorIcon from '@material-ui/icons/Error';\nimport { InfoCard } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { DevxpConfig, UnmaskResult, GithubSyncConfig } from '../types';\n\nconst useStyles = makeStyles(theme => ({\n configGrid: {\n marginBottom: theme.spacing(3),\n },\n statusChip: {\n marginLeft: theme.spacing(1),\n },\n unmaskResult: {\n marginTop: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: theme.palette.background.default,\n borderRadius: theme.shape.borderRadius,\n },\n resultLabel: {\n color: theme.palette.text.secondary,\n marginBottom: theme.spacing(0.5),\n },\n resultValue: {\n fontFamily: 'monospace',\n fontSize: '1.1em',\n },\n activeChip: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n inactiveChip: {\n backgroundColor: '#f5f5f5',\n color: '#757575',\n },\n lastSynced: {\n fontSize: '0.75rem',\n color: theme.palette.text.secondary,\n },\n emptySync: {\n padding: theme.spacing(2),\n color: theme.palette.text.secondary,\n fontStyle: 'italic',\n },\n}));\n\ninterface DashboardContentProps {\n api: DevxpApi;\n}\n\nexport const DashboardContent = ({ api }: DashboardContentProps) => {\n const classes = useStyles();\n const [config, setConfig] = useState<DevxpConfig | null>(null);\n const [loading, setLoading] = useState(true);\n const [maskedInput, setMaskedInput] = useState('');\n const [unmaskResult, setUnmaskResult] = useState<UnmaskResult | null>(null);\n const [unmaskError, setUnmaskError] = useState('');\n const [syncConfigs, setSyncConfigs] = useState<GithubSyncConfig[]>([]);\n const [syncConfigsLoading, setSyncConfigsLoading] = useState(true);\n\n const loadConfig = useCallback(async () => {\n try {\n setLoading(true);\n const cfg = await api.getConfig();\n setConfig(cfg);\n } catch (e) {\n // Config not available\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n const loadSyncConfigs = useCallback(async () => {\n try {\n setSyncConfigsLoading(true);\n const result = await api.getGithubSyncConfigs();\n setSyncConfigs(result.configs);\n } catch {\n // failed silently\n } finally {\n setSyncConfigsLoading(false);\n }\n }, [api]);\n\n useEffect(() => {\n loadConfig();\n loadSyncConfigs();\n // Trigger auto-sync on page load (throttled to 24h server-side)\n api.triggerAutoSync().catch(() => {/* silent */});\n }, [loadConfig, loadSyncConfigs, api]);\n\n const handleUnmask = async () => {\n if (!maskedInput.trim()) return;\n setUnmaskError('');\n setUnmaskResult(null);\n try {\n const result = await api.unmask(maskedInput.trim());\n setUnmaskResult(result);\n } catch (e: any) {\n setUnmaskError(e.message || 'Failed to unmask');\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter') handleUnmask();\n };\n\n if (loading) {\n return <Typography>Loading configuration...</Typography>;\n }\n\n return (\n <Grid container spacing={3}>\n {/* Configuration Status */}\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Configuration Status\">\n {config ? (\n <Box>\n <ConfigItem\n label=\"Mode\"\n value={config.masked ? 'Masked' : 'Unmasked'}\n ok\n />\n <ConfigItem\n label=\"Salt\"\n value={config.saltConfigured ? 'Configured' : 'Not configured'}\n ok={config.saltConfigured}\n />\n <ConfigItem\n label=\"API Endpoint\"\n value={config.apiEndpointConfigured ? 'Configured' : 'Not configured'}\n ok={config.apiEndpointConfigured}\n />\n <ConfigItem\n label=\"API Token\"\n value={config.apiTokenConfigured ? 'Configured' : 'Not configured'}\n ok={config.apiTokenConfigured}\n />\n <ConfigItem\n label=\"Project ID\"\n value={config.projectIdConfigured ? 'Configured' : 'Not configured'}\n ok={config.projectIdConfigured}\n />\n </Box>\n ) : (\n <Typography color=\"error\">\n Unable to load configuration. Is the backend plugin installed?\n </Typography>\n )}\n </InfoCard>\n </Grid>\n\n {/* Statistics */}\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Developer Mappings\">\n <Box display=\"flex\" alignItems=\"center\">\n <Typography variant=\"h3\">\n {config?.mappingCount ?? 0}\n </Typography>\n <Typography\n variant=\"body1\"\n style={{ marginLeft: 8 }}\n color=\"textSecondary\"\n >\n developer name mappings stored\n </Typography>\n </Box>\n <Box mt={2}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Upload developer names via CSV or configure GitHub auto-sync in the\n Settings tab to populate the mapping database.\n </Typography>\n </Box>\n </InfoCard>\n </Grid>\n\n {/* GitHub Auto-Sync Configurations */}\n <Grid item xs={12}>\n <InfoCard title=\"GitHub Auto-Sync Configurations\">\n {syncConfigsLoading ? (\n <Typography variant=\"body2\" color=\"textSecondary\">\n Loading sync configurations...\n </Typography>\n ) : syncConfigs.length === 0 ? (\n <Typography className={classes.emptySync} variant=\"body2\">\n No GitHub sync configurations. Go to Settings to register a GitHub App for automatic member syncing.\n </Typography>\n ) : (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Organization</TableCell>\n <TableCell>GitHub Host</TableCell>\n <TableCell>Client ID</TableCell>\n <TableCell>Status</TableCell>\n <TableCell>Last Synced</TableCell>\n <TableCell>Registered</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {syncConfigs.map(cfg => (\n <TableRow key={cfg.id}>\n <TableCell>\n <strong>{cfg.org_name}</strong>\n </TableCell>\n <TableCell style={{ fontSize: '0.85em' }}>\n {cfg.github_hostname ?? 'github.com'}\n </TableCell>\n <TableCell style={{ fontFamily: 'monospace', fontSize: '0.85em' }}>\n {cfg.app_client_id}\n </TableCell>\n <TableCell>\n <Chip\n label={cfg.active ? 'Active' : 'Inactive'}\n size=\"small\"\n className={cfg.active ? classes.activeChip : classes.inactiveChip}\n />\n </TableCell>\n <TableCell>\n {cfg.last_synced_at ? (\n <Typography className={classes.lastSynced}>\n {new Date(cfg.last_synced_at).toLocaleString()}\n </Typography>\n ) : (\n <Typography className={classes.lastSynced}>Never</Typography>\n )}\n </TableCell>\n <TableCell>\n <Typography className={classes.lastSynced}>\n {new Date(cfg.created_at).toLocaleDateString()}\n </Typography>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n )}\n </InfoCard>\n </Grid>\n\n {/* Unmask Tester */}\n <Grid item xs={12}>\n <InfoCard title=\"Unmask Developer Name\">\n <Typography variant=\"body2\" color=\"textSecondary\" paragraph>\n Enter a masked developer username (16-character hex hash) to look up\n the original name from the mapping database.\n </Typography>\n <Box display=\"flex\" alignItems=\"flex-start\">\n <TextField\n label=\"Masked username\"\n value={maskedInput}\n onChange={e => setMaskedInput(e.target.value)}\n onKeyDown={handleKeyDown}\n variant=\"outlined\"\n size=\"small\"\n placeholder=\"e.g. a1b2c3d4e5f67890\"\n style={{ minWidth: 300 }}\n inputProps={{ maxLength: 16 }}\n />\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleUnmask}\n disabled={!maskedInput.trim()}\n style={{ marginLeft: 12, height: 40 }}\n >\n Unmask\n </Button>\n </Box>\n {unmaskResult && (\n <Box className={classes.unmaskResult}>\n <Typography className={classes.resultLabel} variant=\"body2\">\n Masked:\n </Typography>\n <Typography className={classes.resultValue}>\n {unmaskResult.maskedName}\n </Typography>\n <Box mt={1}>\n <Typography className={classes.resultLabel} variant=\"body2\">\n Real Name:\n </Typography>\n <Typography className={classes.resultValue}>\n {unmaskResult.realName ? (\n <Chip\n label={unmaskResult.realName}\n color=\"primary\"\n variant=\"outlined\"\n />\n ) : (\n <Chip\n label=\"No mapping found\"\n color=\"default\"\n variant=\"outlined\"\n />\n )}\n </Typography>\n </Box>\n </Box>\n )}\n {unmaskError && (\n <Box mt={2}>\n <Typography color=\"error\">{unmaskError}</Typography>\n </Box>\n )}\n </InfoCard>\n </Grid>\n </Grid>\n );\n};\n\nfunction ConfigItem({\n label,\n value,\n ok,\n}: {\n label: string;\n value: string;\n ok: boolean;\n}) {\n return (\n <Box display=\"flex\" alignItems=\"center\" mb={1}>\n {ok ? (\n <CheckCircleIcon style={{ color: '#4caf50', marginRight: 8 }} fontSize=\"small\" />\n ) : (\n <ErrorIcon style={{ color: '#f44336', marginRight: 8 }} fontSize=\"small\" />\n )}\n <Typography variant=\"body2\">\n <strong>{label}:</strong> {value}\n </Typography>\n </Box>\n );\n}\n"],"names":["React"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { useState, useMemo } from 'react';
1
+ import * as React from 'react';
2
2
  import { Page, Header, Content } from '@backstage/core-components';
3
3
  import { useApi, fetchApiRef, discoveryApiRef } from '@backstage/core-plugin-api';
4
4
  import { Tabs, Tab, Box } from '@material-ui/core';
@@ -7,10 +7,10 @@ import { DashboardContent } from './DashboardContent.esm.js';
7
7
  import { SettingsContent } from './SettingsContent.esm.js';
8
8
 
9
9
  const DevxpPage = () => {
10
- const [tab, setTab] = useState(0);
10
+ const [tab, setTab] = React.useState(0);
11
11
  const fetchApi = useApi(fetchApiRef);
12
12
  const discoveryApi = useApi(discoveryApiRef);
13
- const api = useMemo(
13
+ const api = React.useMemo(
14
14
  () => new DevxpClient({ fetchApi, discoveryApi }),
15
15
  [fetchApi, discoveryApi]
16
16
  );
@@ -1 +1 @@
1
- {"version":3,"file":"DevxpPage.esm.js","sources":["../../src/components/DevxpPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { Header, Page, Content } from '@backstage/core-components';\nimport {\n useApi,\n fetchApiRef,\n discoveryApiRef,\n} from '@backstage/core-plugin-api';\nimport { Tabs, Tab, Box } from '@material-ui/core';\nimport { DevxpClient } from '../api';\nimport { DashboardContent } from './DashboardContent';\nimport { SettingsContent } from './SettingsContent';\n\nexport const DevxpPage = () => {\n const [tab, setTab] = useState(0);\n const fetchApi = useApi(fetchApiRef);\n const discoveryApi = useApi(discoveryApiRef);\n\n const api = useMemo(\n () => new DevxpClient({ fetchApi, discoveryApi }),\n [fetchApi, discoveryApi],\n );\n\n return (\n <Page themeId=\"tool\">\n <Header\n title=\"Developer Intelligence\"\n subtitle=\"DevXP Analytics & Developer Name Mapping\"\n />\n <Content>\n <Tabs\n value={tab}\n onChange={(_, v) => setTab(v)}\n indicatorColor=\"primary\"\n textColor=\"primary\"\n >\n <Tab label=\"Dashboard\" />\n <Tab label=\"Settings\" />\n </Tabs>\n <Box mt={3}>\n {tab === 0 && <DashboardContent api={api} />}\n {tab === 1 && <SettingsContent api={api} />}\n </Box>\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AAYO,MAAM,YAAY,MAAM;AAC7B,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAI,SAAS,CAAC,CAAA;AAChC,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAA,MAAM,YAAA,GAAe,OAAO,eAAe,CAAA;AAE3C,EAAA,MAAM,GAAA,GAAM,OAAA;AAAA,IACV,MAAM,IAAI,WAAA,CAAY,EAAE,QAAA,EAAU,cAAc,CAAA;AAAA,IAChD,CAAC,UAAU,YAAY;AAAA,GACzB;AAEA,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,OAAA,EAAQ,MAAA,EAAA,kBACZ,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,wBAAA;AAAA,MACN,QAAA,EAAS;AAAA;AAAA,GACX,sCACC,OAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,GAAA;AAAA,MACP,QAAA,EAAU,CAAC,CAAA,EAAG,CAAA,KAAM,OAAO,CAAC,CAAA;AAAA,MAC5B,cAAA,EAAe,SAAA;AAAA,MACf,SAAA,EAAU;AAAA,KAAA;AAAA,oBAEV,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,KAAA,EAAM,WAAA,EAAY,CAAA;AAAA,oBACvB,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,KAAA,EAAM,UAAA,EAAW;AAAA,qBAExB,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,IAAI,CAAA,EAAA,EACN,GAAA,KAAQ,qBAAK,KAAA,CAAA,aAAA,CAAC,gBAAA,EAAA,EAAiB,GAAA,EAAU,CAAA,EACzC,QAAQ,CAAA,oBAAK,KAAA,CAAA,aAAA,CAAC,mBAAgB,GAAA,EAAU,CAC3C,CACF,CACF,CAAA;AAEJ;;;;"}
1
+ {"version":3,"file":"DevxpPage.esm.js","sources":["../../src/components/DevxpPage.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Header, Page, Content } from '@backstage/core-components';\nimport {\n useApi,\n fetchApiRef,\n discoveryApiRef,\n} from '@backstage/core-plugin-api';\nimport { Tabs, Tab, Box } from '@material-ui/core';\nimport { DevxpClient } from '../api';\nimport { DashboardContent } from './DashboardContent';\nimport { SettingsContent } from './SettingsContent';\n\nexport const DevxpPage = () => {\n const [tab, setTab] = React.useState(0);\n const fetchApi = useApi(fetchApiRef);\n const discoveryApi = useApi(discoveryApiRef);\n\n const api = React.useMemo(\n () => new DevxpClient({ fetchApi, discoveryApi }),\n [fetchApi, discoveryApi],\n );\n\n return (\n <Page themeId=\"tool\">\n <Header\n title=\"Developer Intelligence\"\n subtitle=\"DevXP Analytics & Developer Name Mapping\"\n />\n <Content>\n <Tabs\n value={tab}\n onChange={(_, v) => setTab(v)}\n indicatorColor=\"primary\"\n textColor=\"primary\"\n >\n <Tab label=\"Dashboard\" />\n <Tab label=\"Settings\" />\n </Tabs>\n <Box mt={3}>\n {tab === 0 && <DashboardContent api={api} />}\n {tab === 1 && <SettingsContent api={api} />}\n </Box>\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AAYO,MAAM,YAAY,MAAM;AAC7B,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAI,KAAA,CAAM,SAAS,CAAC,CAAA;AACtC,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAA,MAAM,YAAA,GAAe,OAAO,eAAe,CAAA;AAE3C,EAAA,MAAM,MAAM,KAAA,CAAM,OAAA;AAAA,IAChB,MAAM,IAAI,WAAA,CAAY,EAAE,QAAA,EAAU,cAAc,CAAA;AAAA,IAChD,CAAC,UAAU,YAAY;AAAA,GACzB;AAEA,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,OAAA,EAAQ,MAAA,EAAA,kBACZ,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,wBAAA;AAAA,MACN,QAAA,EAAS;AAAA;AAAA,GACX,sCACC,OAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,GAAA;AAAA,MACP,QAAA,EAAU,CAAC,CAAA,EAAG,CAAA,KAAM,OAAO,CAAC,CAAA;AAAA,MAC5B,cAAA,EAAe,SAAA;AAAA,MACf,SAAA,EAAU;AAAA,KAAA;AAAA,oBAEV,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,KAAA,EAAM,WAAA,EAAY,CAAA;AAAA,oBACvB,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,KAAA,EAAM,UAAA,EAAW;AAAA,qBAExB,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,IAAI,CAAA,EAAA,EACN,GAAA,KAAQ,qBAAK,KAAA,CAAA,aAAA,CAAC,gBAAA,EAAA,EAAiB,GAAA,EAAU,CAAA,EACzC,QAAQ,CAAA,oBAAK,KAAA,CAAA,aAAA,CAAC,mBAAgB,GAAA,EAAU,CAC3C,CACF,CACF,CAAA;AAEJ;;;;"}
@@ -1,4 +1,4 @@
1
- import { useState, useCallback, useEffect } from 'react';
1
+ import * as React from 'react';
2
2
  import { Box, Typography, Collapse, Link, Button, TextField, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Chip, Tooltip, IconButton } from '@material-ui/core';
3
3
  import { makeStyles } from '@material-ui/core/styles';
4
4
  import DeleteIcon from '@material-ui/icons/Delete';
@@ -77,18 +77,18 @@ const useStyles = makeStyles((theme) => ({
77
77
  }));
78
78
  const GithubSyncSection = ({ api, onSyncComplete }) => {
79
79
  const classes = useStyles();
80
- const [configs, setConfigs] = useState([]);
81
- const [loading, setLoading] = useState(true);
82
- const [showForm, setShowForm] = useState(false);
83
- const [showGuide, setShowGuide] = useState(false);
84
- const [syncingIds, setSyncingIds] = useState(/* @__PURE__ */ new Set());
85
- const [statusMessage, setStatusMessage] = useState(null);
86
- const [orgName, setOrgName] = useState("");
87
- const [githubHostname, setGithubHostname] = useState("github.com");
88
- const [appClientId, setAppClientId] = useState("");
89
- const [appPrivateKey, setAppPrivateKey] = useState("");
90
- const [submitting, setSubmitting] = useState(false);
91
- const loadConfigs = useCallback(async () => {
80
+ const [configs, setConfigs] = React.useState([]);
81
+ const [loading, setLoading] = React.useState(true);
82
+ const [showForm, setShowForm] = React.useState(false);
83
+ const [showGuide, setShowGuide] = React.useState(false);
84
+ const [syncingIds, setSyncingIds] = React.useState(/* @__PURE__ */ new Set());
85
+ const [statusMessage, setStatusMessage] = React.useState(null);
86
+ const [orgName, setOrgName] = React.useState("");
87
+ const [githubHostname, setGithubHostname] = React.useState("github.com");
88
+ const [appClientId, setAppClientId] = React.useState("");
89
+ const [appPrivateKey, setAppPrivateKey] = React.useState("");
90
+ const [submitting, setSubmitting] = React.useState(false);
91
+ const loadConfigs = React.useCallback(async () => {
92
92
  try {
93
93
  setLoading(true);
94
94
  const result = await api.getGithubSyncConfigs();
@@ -98,7 +98,7 @@ const GithubSyncSection = ({ api, onSyncComplete }) => {
98
98
  setLoading(false);
99
99
  }
100
100
  }, [api]);
101
- useEffect(() => {
101
+ React.useEffect(() => {
102
102
  loadConfigs();
103
103
  }, [loadConfigs]);
104
104
  const handleCreate = async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"GithubSyncSection.esm.js","sources":["../../src/components/GithubSyncSection.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport {\n Typography,\n Button,\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n IconButton,\n TextField,\n Chip,\n Tooltip,\n Collapse,\n Link,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport SyncIcon from '@material-ui/icons/Sync';\nimport AddIcon from '@material-ui/icons/Add';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport ExpandLessIcon from '@material-ui/icons/ExpandLess';\nimport { InfoCard } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { GithubSyncConfig } from '../types';\n\nconst useStyles = makeStyles(theme => ({\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(2),\n marginTop: theme.spacing(2),\n },\n formRow: {\n display: 'flex',\n gap: theme.spacing(2),\n alignItems: 'flex-start',\n flexWrap: 'wrap',\n },\n textField: {\n flex: 1,\n minWidth: 200,\n },\n privateKeyField: {\n width: '100%',\n },\n statusMessage: {\n marginTop: theme.spacing(2),\n padding: theme.spacing(1.5),\n borderRadius: theme.shape.borderRadius,\n },\n success: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n error: {\n backgroundColor: '#ffebee',\n color: '#c62828',\n },\n activeChip: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n inactiveChip: {\n backgroundColor: '#f5f5f5',\n color: '#757575',\n },\n guideBox: {\n backgroundColor: theme.palette.background.default,\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: theme.shape.borderRadius,\n padding: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n guideHeader: {\n display: 'flex',\n alignItems: 'center',\n cursor: 'pointer',\n userSelect: 'none',\n },\n syncingRow: {\n opacity: 0.6,\n },\n lastSynced: {\n fontSize: '0.75rem',\n color: theme.palette.text.secondary,\n },\n emptyState: {\n textAlign: 'center',\n padding: theme.spacing(3),\n color: theme.palette.text.secondary,\n },\n}));\n\ninterface GithubSyncSectionProps {\n api: DevxpApi;\n onSyncComplete?: () => void;\n}\n\nexport const GithubSyncSection = ({ api, onSyncComplete }: GithubSyncSectionProps) => {\n const classes = useStyles();\n const [configs, setConfigs] = useState<GithubSyncConfig[]>([]);\n const [loading, setLoading] = useState(true);\n const [showForm, setShowForm] = useState(false);\n const [showGuide, setShowGuide] = useState(false);\n const [syncingIds, setSyncingIds] = useState<Set<number>>(new Set());\n const [statusMessage, setStatusMessage] = useState<{ text: string; isError: boolean } | null>(null);\n\n // Form state\n const [orgName, setOrgName] = useState('');\n const [githubHostname, setGithubHostname] = useState('github.com');\n const [appClientId, setAppClientId] = useState('');\n const [appPrivateKey, setAppPrivateKey] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n const loadConfigs = useCallback(async () => {\n try {\n setLoading(true);\n const result = await api.getGithubSyncConfigs();\n setConfigs(result.configs);\n } catch {\n // failed silently\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n useEffect(() => {\n loadConfigs();\n }, [loadConfigs]);\n\n const handleCreate = async () => {\n if (!orgName.trim() || !appClientId.trim() || !appPrivateKey.trim()) return;\n setSubmitting(true);\n setStatusMessage(null);\n try {\n const hostname = githubHostname.trim() || 'github.com';\n await api.createGithubSyncConfig(orgName.trim(), hostname, appClientId.trim(), appPrivateKey.trim());\n setStatusMessage({ text: `GitHub sync registered for org \"${orgName}\" on \"${hostname}\"`, isError: false });\n setOrgName('');\n setGithubHostname('github.com');\n setAppClientId('');\n setAppPrivateKey('');\n setShowForm(false);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to register sync config', isError: true });\n } finally {\n setSubmitting(false);\n }\n };\n\n const handleToggle = async (config: GithubSyncConfig) => {\n try {\n await api.toggleGithubSyncConfig(config.id, !config.active);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to update config', isError: true });\n }\n };\n\n const handleDelete = async (id: number) => {\n try {\n await api.deleteGithubSyncConfig(id);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to delete config', isError: true });\n }\n };\n\n const handleSync = async (id: number) => {\n setSyncingIds(prev => new Set(prev).add(id));\n setStatusMessage(null);\n try {\n const result = await api.syncGithubConfig(id);\n if (result.error) {\n setStatusMessage({ text: result.error, isError: true });\n } else {\n setStatusMessage({\n text: `Synced ${result.count} members from \"${result.orgName}\"`,\n isError: false,\n });\n await loadConfigs();\n onSyncComplete?.();\n }\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Sync failed', isError: true });\n } finally {\n setSyncingIds(prev => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n };\n\n // Preconfigured GitHub App creation URL with required permissions\n const githubAppCreateUrl =\n 'https://github.com/settings/apps/new?' +\n 'name=devxp-member-sync&' +\n 'url=https%3A%2F%2Fdevxp.net&' +\n 'public=false&' +\n 'members=read';\n\n return (\n <InfoCard title=\"GitHub Organization Auto-Sync\">\n {/* Setup Guide */}\n <Box\n className={classes.guideBox}\n onClick={() => setShowGuide(v => !v)}\n >\n <Box className={classes.guideHeader}>\n {showGuide ? <ExpandLessIcon fontSize=\"small\" /> : <ExpandMoreIcon fontSize=\"small\" />}\n <Typography variant=\"body2\" style={{ marginLeft: 4, fontWeight: 600 }}>\n How to set up a GitHub App for member sync\n </Typography>\n </Box>\n <Collapse in={showGuide}>\n <Box mt={1}>\n <Typography variant=\"body2\" paragraph>\n DevXP syncs GitHub organization members automatically using a{' '}\n <Link href=\"https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps\" target=\"_blank\" rel=\"noopener noreferrer\">\n GitHub App\n </Link>\n . The app needs <strong>Read-only</strong> access to <strong>Organization members</strong>.\n </Typography>\n <Typography variant=\"body2\" component=\"div\">\n <strong>Steps:</strong>\n <ol>\n <li>\n <Link href={githubAppCreateUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n Click here to pre-configure and create your GitHub App\n </Link>\n {' '}— this link pre-fills the name and required permissions.\n </li>\n <li>\n On the creation page, scroll to <em>Organization permissions</em> and verify{' '}\n <strong>Members → Read-only</strong> is set.\n </li>\n <li>\n After creating the app, note down the <strong>Client ID</strong> from the app's{' '}\n <em>General</em> tab.\n </li>\n <li>\n Scroll to <em>Private keys</em> and click <strong>Generate a private key</strong>.\n Download the <code>.pem</code> file and paste its full contents below.\n </li>\n <li>\n <Link href=\"https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app\" target=\"_blank\" rel=\"noopener noreferrer\">\n Install the app\n </Link>\n {' '}in your GitHub organization (Settings → Developer settings → GitHub Apps → Install).\n </li>\n </ol>\n </Typography>\n <Typography variant=\"body2\">\n <Link href=\"https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app\" target=\"_blank\" rel=\"noopener noreferrer\">\n Full GitHub App documentation →\n </Link>\n </Typography>\n </Box>\n </Collapse>\n </Box>\n\n {/* Status message */}\n {statusMessage && (\n <Box\n className={`${classes.statusMessage} ${statusMessage.isError ? classes.error : classes.success}`}\n mb={2}\n >\n <Typography variant=\"body2\">{statusMessage.text}</Typography>\n </Box>\n )}\n\n {/* Add new config button / form */}\n {!showForm ? (\n <Button\n variant=\"outlined\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => { setShowForm(true); setStatusMessage(null); }}\n size=\"small\"\n >\n Register GitHub App\n </Button>\n ) : (\n <Box className={classes.form}>\n <Typography variant=\"subtitle2\">Register new GitHub App sync</Typography>\n <Box className={classes.formRow}>\n <TextField\n label=\"GitHub Org Name\"\n value={orgName}\n onChange={e => setOrgName(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"my-organization\"\n helperText=\"Your GitHub organization's login name\"\n />\n <TextField\n label=\"GitHub Hostname\"\n value={githubHostname}\n onChange={e => setGithubHostname(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"github.com\"\n helperText='Use \"github.com\" or your GitHub Enterprise Server hostname (e.g. github.acme.com)'\n />\n </Box>\n <Box className={classes.formRow}>\n <TextField\n label=\"App Client ID\"\n value={appClientId}\n onChange={e => setAppClientId(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"Iv1.a1b2c3d4e5f67890\"\n helperText=\"Found on the GitHub App's General tab\"\n />\n </Box>\n <TextField\n label=\"App Private Key (PEM)\"\n value={appPrivateKey}\n onChange={e => setAppPrivateKey(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n multiline\n rows={6}\n className={classes.privateKeyField}\n placeholder=\"-----BEGIN RSA PRIVATE KEY-----&#10;...&#10;-----END RSA PRIVATE KEY-----\"\n helperText=\"Paste the full contents of the .pem file downloaded from GitHub\"\n />\n <Box style={{ display: 'flex', gap: 8 }}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleCreate}\n disabled={submitting || !orgName.trim() || !appClientId.trim() || !appPrivateKey.trim()}\n >\n {submitting ? 'Registering...' : 'Register'}\n </Button>\n <Button\n variant=\"outlined\"\n onClick={() => { setShowForm(false); setOrgName(''); setGithubHostname('github.com'); setAppClientId(''); setAppPrivateKey(''); }}\n disabled={submitting}\n style={{ marginLeft: 8 }}\n >\n Cancel\n </Button>\n </Box>\n </Box>\n )}\n\n\n\n {/* Configs table */}\n <Box mt={3}>\n {loading ? (\n <Typography variant=\"body2\" color=\"textSecondary\">Loading configurations...</Typography>\n ) : configs.length === 0 ? (\n <Box className={classes.emptyState}>\n <Typography variant=\"body2\">\n No GitHub sync configurations yet. Register a GitHub App above to enable automatic member syncing.\n </Typography>\n </Box>\n ) : (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Organization</TableCell>\n <TableCell>GitHub Host</TableCell>\n <TableCell>Client ID</TableCell>\n <TableCell>Status</TableCell>\n <TableCell>Last Synced</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {configs.map(cfg => {\n const isSyncing = syncingIds.has(cfg.id);\n return (\n <TableRow key={cfg.id} className={isSyncing ? classes.syncingRow : undefined}>\n <TableCell>\n <strong>{cfg.org_name}</strong>\n </TableCell>\n <TableCell style={{ fontSize: '0.85em' }}>\n {cfg.github_hostname ?? 'github.com'}\n </TableCell>\n <TableCell style={{ fontFamily: 'monospace', fontSize: '0.85em' }}>\n {cfg.app_client_id}\n </TableCell>\n <TableCell>\n <Chip\n label={cfg.active ? 'Active' : 'Inactive'}\n size=\"small\"\n className={cfg.active ? classes.activeChip : classes.inactiveChip}\n onClick={() => handleToggle(cfg)}\n clickable\n />\n </TableCell>\n <TableCell>\n {cfg.last_synced_at ? (\n <Typography className={classes.lastSynced}>\n {new Date(cfg.last_synced_at).toLocaleString()}\n </Typography>\n ) : (\n <Typography className={classes.lastSynced}>Never</Typography>\n )}\n </TableCell>\n <TableCell align=\"right\">\n <Tooltip title=\"Sync now\">\n <span>\n <IconButton\n size=\"small\"\n onClick={() => handleSync(cfg.id)}\n disabled={isSyncing}\n aria-label=\"Sync now\"\n >\n <SyncIcon\n fontSize=\"small\"\n style={isSyncing ? { animation: 'spin 1s linear infinite' } : undefined}\n />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Delete configuration\">\n <IconButton\n size=\"small\"\n onClick={() => handleDelete(cfg.id)}\n disabled={isSyncing}\n aria-label=\"Delete config\"\n >\n <DeleteIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </TableContainer>\n )}\n </Box>\n\n <style>{`\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n `}</style>\n </InfoCard>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GithubSyncSection.esm.js","sources":["../../src/components/GithubSyncSection.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n Typography,\n Button,\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n IconButton,\n TextField,\n Chip,\n Tooltip,\n Collapse,\n Link,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport SyncIcon from '@material-ui/icons/Sync';\nimport AddIcon from '@material-ui/icons/Add';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport ExpandLessIcon from '@material-ui/icons/ExpandLess';\nimport { InfoCard } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { GithubSyncConfig } from '../types';\n\nconst useStyles = makeStyles(theme => ({\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(2),\n marginTop: theme.spacing(2),\n },\n formRow: {\n display: 'flex',\n gap: theme.spacing(2),\n alignItems: 'flex-start',\n flexWrap: 'wrap',\n },\n textField: {\n flex: 1,\n minWidth: 200,\n },\n privateKeyField: {\n width: '100%',\n },\n statusMessage: {\n marginTop: theme.spacing(2),\n padding: theme.spacing(1.5),\n borderRadius: theme.shape.borderRadius,\n },\n success: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n error: {\n backgroundColor: '#ffebee',\n color: '#c62828',\n },\n activeChip: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n inactiveChip: {\n backgroundColor: '#f5f5f5',\n color: '#757575',\n },\n guideBox: {\n backgroundColor: theme.palette.background.default,\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: theme.shape.borderRadius,\n padding: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n guideHeader: {\n display: 'flex',\n alignItems: 'center',\n cursor: 'pointer',\n userSelect: 'none',\n },\n syncingRow: {\n opacity: 0.6,\n },\n lastSynced: {\n fontSize: '0.75rem',\n color: theme.palette.text.secondary,\n },\n emptyState: {\n textAlign: 'center',\n padding: theme.spacing(3),\n color: theme.palette.text.secondary,\n },\n}));\n\ninterface GithubSyncSectionProps {\n api: DevxpApi;\n onSyncComplete?: () => void;\n}\n\nexport const GithubSyncSection = ({ api, onSyncComplete }: GithubSyncSectionProps) => {\n const classes = useStyles();\n const [configs, setConfigs] = React.useState<GithubSyncConfig[]>([]);\n const [loading, setLoading] = React.useState(true);\n const [showForm, setShowForm] = React.useState(false);\n const [showGuide, setShowGuide] = React.useState(false);\n const [syncingIds, setSyncingIds] = React.useState<Set<number>>(new Set());\n const [statusMessage, setStatusMessage] = React.useState<{ text: string; isError: boolean } | null>(null);\n\n // Form state\n const [orgName, setOrgName] = React.useState('');\n const [githubHostname, setGithubHostname] = React.useState('github.com');\n const [appClientId, setAppClientId] = React.useState('');\n const [appPrivateKey, setAppPrivateKey] = React.useState('');\n const [submitting, setSubmitting] = React.useState(false);\n\n const loadConfigs = React.useCallback(async () => {\n try {\n setLoading(true);\n const result = await api.getGithubSyncConfigs();\n setConfigs(result.configs);\n } catch {\n // failed silently\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n React.useEffect(() => {\n loadConfigs();\n }, [loadConfigs]);\n\n const handleCreate = async () => {\n if (!orgName.trim() || !appClientId.trim() || !appPrivateKey.trim()) return;\n setSubmitting(true);\n setStatusMessage(null);\n try {\n const hostname = githubHostname.trim() || 'github.com';\n await api.createGithubSyncConfig(orgName.trim(), hostname, appClientId.trim(), appPrivateKey.trim());\n setStatusMessage({ text: `GitHub sync registered for org \"${orgName}\" on \"${hostname}\"`, isError: false });\n setOrgName('');\n setGithubHostname('github.com');\n setAppClientId('');\n setAppPrivateKey('');\n setShowForm(false);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to register sync config', isError: true });\n } finally {\n setSubmitting(false);\n }\n };\n\n const handleToggle = async (config: GithubSyncConfig) => {\n try {\n await api.toggleGithubSyncConfig(config.id, !config.active);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to update config', isError: true });\n }\n };\n\n const handleDelete = async (id: number) => {\n try {\n await api.deleteGithubSyncConfig(id);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to delete config', isError: true });\n }\n };\n\n const handleSync = async (id: number) => {\n setSyncingIds(prev => new Set(prev).add(id));\n setStatusMessage(null);\n try {\n const result = await api.syncGithubConfig(id);\n if (result.error) {\n setStatusMessage({ text: result.error, isError: true });\n } else {\n setStatusMessage({\n text: `Synced ${result.count} members from \"${result.orgName}\"`,\n isError: false,\n });\n await loadConfigs();\n onSyncComplete?.();\n }\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Sync failed', isError: true });\n } finally {\n setSyncingIds(prev => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n };\n\n // Preconfigured GitHub App creation URL with required permissions\n const githubAppCreateUrl =\n 'https://github.com/settings/apps/new?' +\n 'name=devxp-member-sync&' +\n 'url=https%3A%2F%2Fdevxp.net&' +\n 'public=false&' +\n 'members=read';\n\n return (\n <InfoCard title=\"GitHub Organization Auto-Sync\">\n {/* Setup Guide */}\n <Box\n className={classes.guideBox}\n onClick={() => setShowGuide(v => !v)}\n >\n <Box className={classes.guideHeader}>\n {showGuide ? <ExpandLessIcon fontSize=\"small\" /> : <ExpandMoreIcon fontSize=\"small\" />}\n <Typography variant=\"body2\" style={{ marginLeft: 4, fontWeight: 600 }}>\n How to set up a GitHub App for member sync\n </Typography>\n </Box>\n <Collapse in={showGuide}>\n <Box mt={1}>\n <Typography variant=\"body2\" paragraph>\n DevXP syncs GitHub organization members automatically using a{' '}\n <Link href=\"https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps\" target=\"_blank\" rel=\"noopener noreferrer\">\n GitHub App\n </Link>\n . The app needs <strong>Read-only</strong> access to <strong>Organization members</strong>.\n </Typography>\n <Typography variant=\"body2\" component=\"div\">\n <strong>Steps:</strong>\n <ol>\n <li>\n <Link href={githubAppCreateUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n Click here to pre-configure and create your GitHub App\n </Link>\n {' '}— this link pre-fills the name and required permissions.\n </li>\n <li>\n On the creation page, scroll to <em>Organization permissions</em> and verify{' '}\n <strong>Members → Read-only</strong> is set.\n </li>\n <li>\n After creating the app, note down the <strong>Client ID</strong> from the app's{' '}\n <em>General</em> tab.\n </li>\n <li>\n Scroll to <em>Private keys</em> and click <strong>Generate a private key</strong>.\n Download the <code>.pem</code> file and paste its full contents below.\n </li>\n <li>\n <Link href=\"https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app\" target=\"_blank\" rel=\"noopener noreferrer\">\n Install the app\n </Link>\n {' '}in your GitHub organization (Settings → Developer settings → GitHub Apps → Install).\n </li>\n </ol>\n </Typography>\n <Typography variant=\"body2\">\n <Link href=\"https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app\" target=\"_blank\" rel=\"noopener noreferrer\">\n Full GitHub App documentation →\n </Link>\n </Typography>\n </Box>\n </Collapse>\n </Box>\n\n {/* Status message */}\n {statusMessage && (\n <Box\n className={`${classes.statusMessage} ${statusMessage.isError ? classes.error : classes.success}`}\n mb={2}\n >\n <Typography variant=\"body2\">{statusMessage.text}</Typography>\n </Box>\n )}\n\n {/* Add new config button / form */}\n {!showForm ? (\n <Button\n variant=\"outlined\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => { setShowForm(true); setStatusMessage(null); }}\n size=\"small\"\n >\n Register GitHub App\n </Button>\n ) : (\n <Box className={classes.form}>\n <Typography variant=\"subtitle2\">Register new GitHub App sync</Typography>\n <Box className={classes.formRow}>\n <TextField\n label=\"GitHub Org Name\"\n value={orgName}\n onChange={e => setOrgName(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"my-organization\"\n helperText=\"Your GitHub organization's login name\"\n />\n <TextField\n label=\"GitHub Hostname\"\n value={githubHostname}\n onChange={e => setGithubHostname(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"github.com\"\n helperText='Use \"github.com\" or your GitHub Enterprise Server hostname (e.g. github.acme.com)'\n />\n </Box>\n <Box className={classes.formRow}>\n <TextField\n label=\"App Client ID\"\n value={appClientId}\n onChange={e => setAppClientId(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"Iv1.a1b2c3d4e5f67890\"\n helperText=\"Found on the GitHub App's General tab\"\n />\n </Box>\n <TextField\n label=\"App Private Key (PEM)\"\n value={appPrivateKey}\n onChange={e => setAppPrivateKey(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n multiline\n rows={6}\n className={classes.privateKeyField}\n placeholder=\"-----BEGIN RSA PRIVATE KEY-----&#10;...&#10;-----END RSA PRIVATE KEY-----\"\n helperText=\"Paste the full contents of the .pem file downloaded from GitHub\"\n />\n <Box style={{ display: 'flex', gap: 8 }}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleCreate}\n disabled={submitting || !orgName.trim() || !appClientId.trim() || !appPrivateKey.trim()}\n >\n {submitting ? 'Registering...' : 'Register'}\n </Button>\n <Button\n variant=\"outlined\"\n onClick={() => { setShowForm(false); setOrgName(''); setGithubHostname('github.com'); setAppClientId(''); setAppPrivateKey(''); }}\n disabled={submitting}\n style={{ marginLeft: 8 }}\n >\n Cancel\n </Button>\n </Box>\n </Box>\n )}\n\n\n\n {/* Configs table */}\n <Box mt={3}>\n {loading ? (\n <Typography variant=\"body2\" color=\"textSecondary\">Loading configurations...</Typography>\n ) : configs.length === 0 ? (\n <Box className={classes.emptyState}>\n <Typography variant=\"body2\">\n No GitHub sync configurations yet. Register a GitHub App above to enable automatic member syncing.\n </Typography>\n </Box>\n ) : (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Organization</TableCell>\n <TableCell>GitHub Host</TableCell>\n <TableCell>Client ID</TableCell>\n <TableCell>Status</TableCell>\n <TableCell>Last Synced</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {configs.map(cfg => {\n const isSyncing = syncingIds.has(cfg.id);\n return (\n <TableRow key={cfg.id} className={isSyncing ? classes.syncingRow : undefined}>\n <TableCell>\n <strong>{cfg.org_name}</strong>\n </TableCell>\n <TableCell style={{ fontSize: '0.85em' }}>\n {cfg.github_hostname ?? 'github.com'}\n </TableCell>\n <TableCell style={{ fontFamily: 'monospace', fontSize: '0.85em' }}>\n {cfg.app_client_id}\n </TableCell>\n <TableCell>\n <Chip\n label={cfg.active ? 'Active' : 'Inactive'}\n size=\"small\"\n className={cfg.active ? classes.activeChip : classes.inactiveChip}\n onClick={() => handleToggle(cfg)}\n clickable\n />\n </TableCell>\n <TableCell>\n {cfg.last_synced_at ? (\n <Typography className={classes.lastSynced}>\n {new Date(cfg.last_synced_at).toLocaleString()}\n </Typography>\n ) : (\n <Typography className={classes.lastSynced}>Never</Typography>\n )}\n </TableCell>\n <TableCell align=\"right\">\n <Tooltip title=\"Sync now\">\n <span>\n <IconButton\n size=\"small\"\n onClick={() => handleSync(cfg.id)}\n disabled={isSyncing}\n aria-label=\"Sync now\"\n >\n <SyncIcon\n fontSize=\"small\"\n style={isSyncing ? { animation: 'spin 1s linear infinite' } : undefined}\n />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Delete configuration\">\n <IconButton\n size=\"small\"\n onClick={() => handleDelete(cfg.id)}\n disabled={isSyncing}\n aria-label=\"Delete config\"\n >\n <DeleteIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </TableContainer>\n )}\n </Box>\n\n <style>{`\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n `}</style>\n </InfoCard>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;AA6BA,MAAM,SAAA,GAAY,WAAW,CAAA,KAAA,MAAU;AAAA,EACrC,IAAA,EAAM;AAAA,IACJ,OAAA,EAAS,MAAA;AAAA,IACT,aAAA,EAAe,QAAA;AAAA,IACf,GAAA,EAAK,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,SAAA,EAAW,KAAA,CAAM,OAAA,CAAQ,CAAC;AAAA,GAC5B;AAAA,EACA,OAAA,EAAS;AAAA,IACP,OAAA,EAAS,MAAA;AAAA,IACT,GAAA,EAAK,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IACpB,UAAA,EAAY,YAAA;AAAA,IACZ,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,SAAA,EAAW;AAAA,IACT,IAAA,EAAM,CAAA;AAAA,IACN,QAAA,EAAU;AAAA,GACZ;AAAA,EACA,eAAA,EAAiB;AAAA,IACf,KAAA,EAAO;AAAA,GACT;AAAA,EACA,aAAA,EAAe;AAAA,IACb,SAAA,EAAW,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IAC1B,OAAA,EAAS,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AAAA,IAC1B,YAAA,EAAc,MAAM,KAAA,CAAM;AAAA,GAC5B;AAAA,EACA,OAAA,EAAS;AAAA,IACP,eAAA,EAAiB,SAAA;AAAA,IACjB,KAAA,EAAO;AAAA,GACT;AAAA,EACA,KAAA,EAAO;AAAA,IACL,eAAA,EAAiB,SAAA;AAAA,IACjB,KAAA,EAAO;AAAA,GACT;AAAA,EACA,UAAA,EAAY;AAAA,IACV,eAAA,EAAiB,SAAA;AAAA,IACjB,KAAA,EAAO;AAAA,GACT;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,eAAA,EAAiB,SAAA;AAAA,IACjB,KAAA,EAAO;AAAA,GACT;AAAA,EACA,QAAA,EAAU;AAAA,IACR,eAAA,EAAiB,KAAA,CAAM,OAAA,CAAQ,UAAA,CAAW,OAAA;AAAA,IAC1C,MAAA,EAAQ,CAAA,UAAA,EAAa,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,CAAA;AAAA,IAC1C,YAAA,EAAc,MAAM,KAAA,CAAM,YAAA;AAAA,IAC1B,OAAA,EAAS,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IACxB,YAAA,EAAc,KAAA,CAAM,OAAA,CAAQ,CAAC;AAAA,GAC/B;AAAA,EACA,WAAA,EAAa;AAAA,IACX,OAAA,EAAS,MAAA;AAAA,IACT,UAAA,EAAY,QAAA;AAAA,IACZ,MAAA,EAAQ,SAAA;AAAA,IACR,UAAA,EAAY;AAAA,GACd;AAAA,EACA,UAAA,EAAY;AAAA,IACV,OAAA,EAAS;AAAA,GACX;AAAA,EACA,UAAA,EAAY;AAAA,IACV,QAAA,EAAU,SAAA;AAAA,IACV,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK;AAAA,GAC5B;AAAA,EACA,UAAA,EAAY;AAAA,IACV,SAAA,EAAW,QAAA;AAAA,IACX,OAAA,EAAS,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA;AAAA,IACxB,KAAA,EAAO,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK;AAAA;AAE9B,CAAA,CAAE,CAAA;AAOK,MAAM,iBAAA,GAAoB,CAAC,EAAE,GAAA,EAAK,gBAAe,KAA8B;AACpF,EAAA,MAAM,UAAU,SAAA,EAAU;AAC1B,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,IAAI,KAAA,CAAM,QAAA,CAA6B,EAAE,CAAA;AACnE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,KAAA,CAAM,SAAS,IAAI,CAAA;AACjD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,KAAA,CAAM,SAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,KAAA,CAAM,SAAS,KAAK,CAAA;AACtD,EAAA,MAAM,CAAC,YAAY,aAAa,CAAA,GAAI,MAAM,QAAA,iBAAsB,IAAI,KAAK,CAAA;AACzE,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,KAAA,CAAM,SAAoD,IAAI,CAAA;AAGxG,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,KAAA,CAAM,SAAS,EAAE,CAAA;AAC/C,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,KAAA,CAAM,SAAS,YAAY,CAAA;AACvE,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,KAAA,CAAM,SAAS,EAAE,CAAA;AACvD,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,KAAA,CAAM,SAAS,EAAE,CAAA;AAC3D,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,KAAA,CAAM,SAAS,KAAK,CAAA;AAExD,EAAA,MAAM,WAAA,GAAc,KAAA,CAAM,WAAA,CAAY,YAAY;AAChD,IAAA,IAAI;AACF,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,MAAM,MAAA,GAAS,MAAM,GAAA,CAAI,oBAAA,EAAqB;AAC9C,MAAA,UAAA,CAAW,OAAO,OAAO,CAAA;AAAA,IAC3B,CAAA,CAAA,MAAQ;AAAA,IAER,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG,CAAC,GAAG,CAAC,CAAA;AAER,EAAA,KAAA,CAAM,UAAU,MAAM;AACpB,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAA,MAAM,eAAe,YAAY;AAC/B,IAAA,IAAI,CAAC,OAAA,CAAQ,IAAA,EAAK,IAAK,CAAC,WAAA,CAAY,IAAA,EAAK,IAAK,CAAC,aAAA,CAAc,IAAA,EAAK,EAAG;AACrE,IAAA,aAAA,CAAc,IAAI,CAAA;AAClB,IAAA,gBAAA,CAAiB,IAAI,CAAA;AACrB,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,cAAA,CAAe,IAAA,EAAK,IAAK,YAAA;AAC1C,MAAA,MAAM,GAAA,CAAI,sBAAA,CAAuB,OAAA,CAAQ,IAAA,EAAK,EAAG,QAAA,EAAU,WAAA,CAAY,IAAA,EAAK,EAAG,aAAA,CAAc,IAAA,EAAM,CAAA;AACnG,MAAA,gBAAA,CAAiB,EAAE,MAAM,CAAA,gCAAA,EAAmC,OAAO,SAAS,QAAQ,CAAA,CAAA,CAAA,EAAK,OAAA,EAAS,KAAA,EAAO,CAAA;AACzG,MAAA,UAAA,CAAW,EAAE,CAAA;AACb,MAAA,iBAAA,CAAkB,YAAY,CAAA;AAC9B,MAAA,cAAA,CAAe,EAAE,CAAA;AACjB,MAAA,gBAAA,CAAiB,EAAE,CAAA;AACnB,MAAA,WAAA,CAAY,KAAK,CAAA;AACjB,MAAA,MAAM,WAAA,EAAY;AAAA,IACpB,SAAS,CAAA,EAAQ;AACf,MAAA,gBAAA,CAAiB,EAAE,IAAA,EAAM,CAAA,CAAE,WAAW,gCAAA,EAAkC,OAAA,EAAS,MAAM,CAAA;AAAA,IACzF,CAAA,SAAE;AACA,MAAA,aAAA,CAAc,KAAK,CAAA;AAAA,IACrB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,OAAO,MAAA,KAA6B;AACvD,IAAA,IAAI;AACF,MAAA,MAAM,IAAI,sBAAA,CAAuB,MAAA,CAAO,EAAA,EAAI,CAAC,OAAO,MAAM,CAAA;AAC1D,MAAA,MAAM,WAAA,EAAY;AAAA,IACpB,SAAS,CAAA,EAAQ;AACf,MAAA,gBAAA,CAAiB,EAAE,IAAA,EAAM,CAAA,CAAE,WAAW,yBAAA,EAA2B,OAAA,EAAS,MAAM,CAAA;AAAA,IAClF;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,OAAO,EAAA,KAAe;AACzC,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,CAAI,uBAAuB,EAAE,CAAA;AACnC,MAAA,MAAM,WAAA,EAAY;AAAA,IACpB,SAAS,CAAA,EAAQ;AACf,MAAA,gBAAA,CAAiB,EAAE,IAAA,EAAM,CAAA,CAAE,WAAW,yBAAA,EAA2B,OAAA,EAAS,MAAM,CAAA;AAAA,IAClF;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,OAAO,EAAA,KAAe;AACvC,IAAA,aAAA,CAAc,UAAQ,IAAI,GAAA,CAAI,IAAI,CAAA,CAAE,GAAA,CAAI,EAAE,CAAC,CAAA;AAC3C,IAAA,gBAAA,CAAiB,IAAI,CAAA;AACrB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,GAAA,CAAI,gBAAA,CAAiB,EAAE,CAAA;AAC5C,MAAA,IAAI,OAAO,KAAA,EAAO;AAChB,QAAA,gBAAA,CAAiB,EAAE,IAAA,EAAM,MAAA,CAAO,KAAA,EAAO,OAAA,EAAS,MAAM,CAAA;AAAA,MACxD,CAAA,MAAO;AACL,QAAA,gBAAA,CAAiB;AAAA,UACf,MAAM,CAAA,OAAA,EAAU,MAAA,CAAO,KAAK,CAAA,eAAA,EAAkB,OAAO,OAAO,CAAA,CAAA,CAAA;AAAA,UAC5D,OAAA,EAAS;AAAA,SACV,CAAA;AACD,QAAA,MAAM,WAAA,EAAY;AAClB,QAAA,cAAA,IAAiB;AAAA,MACnB;AAAA,IACF,SAAS,CAAA,EAAQ;AACf,MAAA,gBAAA,CAAiB,EAAE,IAAA,EAAM,CAAA,CAAE,WAAW,aAAA,EAAe,OAAA,EAAS,MAAM,CAAA;AAAA,IACtE,CAAA,SAAE;AACA,MAAA,aAAA,CAAc,CAAA,IAAA,KAAQ;AACpB,QAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,IAAI,CAAA;AACzB,QAAA,IAAA,CAAK,OAAO,EAAE,CAAA;AACd,QAAA,OAAO,IAAA;AAAA,MACT,CAAC,CAAA;AAAA,IACH;AAAA,EACF,CAAA;AAGA,EAAA,MAAM,kBAAA,GACJ,mHAAA;AAMF,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAM,+BAAA,EAAA,kBAEd,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,WAAW,OAAA,CAAQ,QAAA;AAAA,MACnB,OAAA,EAAS,MAAM,YAAA,CAAa,CAAA,CAAA,KAAK,CAAC,CAAC;AAAA,KAAA;AAAA,oBAEnC,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,WAAA,EAAA,EACrB,SAAA,mBAAY,KAAA,CAAA,aAAA,CAAC,cAAA,EAAA,EAAe,QAAA,EAAS,OAAA,EAAQ,CAAA,mBAAK,KAAA,CAAA,aAAA,CAAC,cAAA,EAAA,EAAe,QAAA,EAAS,OAAA,EAAQ,CAAA,kBACpF,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,KAAA,EAAO,EAAE,UAAA,EAAY,CAAA,EAAG,UAAA,EAAY,GAAA,EAAI,EAAA,EAAG,4CAEvE,CACF,CAAA;AAAA,oBACA,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAS,EAAA,EAAI,SAAA,EAAA,sCACX,GAAA,EAAA,EAAI,EAAA,EAAI,CAAA,EAAA,kBACP,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,SAAA,EAAS,QAAC,+DAAA,EAC0B,GAAA,kBAC9D,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAK,4GAAA,EAA6G,MAAA,EAAO,QAAA,EAAS,KAAI,qBAAA,EAAA,EAAsB,YAElK,CAAA,EAAO,kBAAA,sCACU,QAAA,EAAA,IAAA,EAAO,WAAS,CAAA,EAAS,aAAA,sCAAY,QAAA,EAAA,IAAA,EAAO,sBAAoB,CAAA,EAAS,GAC5F,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,SAAQ,SAAA,EAAU,KAAA,EAAA,kBACpC,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,IAAA,EAAO,QAAM,CAAA,kBACd,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,4BACC,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAM,kBAAA,EAAoB,MAAA,EAAO,QAAA,EAAS,GAAA,EAAI,qBAAA,EAAA,EAAsB,wDAE1E,CAAA,EACC,GAAA,EAAI,+DACP,CAAA,sCACC,IAAA,EAAA,IAAA,EAAG,kCAAA,kBAC8B,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,0BAAwB,CAAA,EAAK,aAAA,EAAY,GAAA,kBAC7E,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,IAAA,EAAO,0BAAmB,CAAA,EAAS,UACtC,mBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,wCAAA,kBACoC,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,IAAA,EAAO,WAAS,CAAA,EAAS,iBAAA,EAAgB,qBAChF,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,SAAO,CAAA,EAAK,OAClB,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,8BACQ,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,EAAG,cAAY,CAAA,EAAK,+BAAW,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,IAAA,EAAO,wBAAsB,CAAA,EAAS,mCACpE,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAA,EAAK,MAAI,CAAA,EAAO,0CAChC,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAA,sCACE,IAAA,EAAA,EAAK,IAAA,EAAK,kFAAA,EAAmF,MAAA,EAAO,UAAS,GAAA,EAAI,qBAAA,EAAA,EAAsB,iBAExI,CAAA,EACC,KAAI,qGACP,CACF,CACF,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAA,sCACjB,IAAA,EAAA,EAAK,IAAA,EAAK,wGAAA,EAAyG,MAAA,EAAO,UAAS,GAAA,EAAI,qBAAA,EAAA,EAAsB,sCAE9J,CACF,CACF,CACF;AAAA,KAID,aAAA,oBACC,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,CAAA,EAAG,OAAA,CAAQ,aAAa,CAAA,CAAA,EAAI,cAAc,OAAA,GAAU,OAAA,CAAQ,KAAA,GAAQ,OAAA,CAAQ,OAAO,CAAA,CAAA;AAAA,MAC9F,EAAA,EAAI;AAAA,KAAA;AAAA,oBAEJ,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAA,EAAS,cAAc,IAAK;AAAA,GAClD,EAID,CAAC,QAAA,mBACA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,UAAA;AAAA,MACR,KAAA,EAAM,SAAA;AAAA,MACN,SAAA,sCAAY,OAAA,EAAA,IAAQ,CAAA;AAAA,MACpB,SAAS,MAAM;AAAE,QAAA,WAAA,CAAY,IAAI,CAAA;AAAG,QAAA,gBAAA,CAAiB,IAAI,CAAA;AAAA,MAAG,CAAA;AAAA,MAC5D,IAAA,EAAK;AAAA,KAAA;AAAA,IACN;AAAA,sBAID,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAW,OAAA,CAAQ,wBACtB,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,WAAA,EAAA,EAAY,8BAA4B,CAAA,kBAC5D,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAW,QAAQ,OAAA,EAAA,kBACtB,KAAA,CAAA,aAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,iBAAA;AAAA,MACN,KAAA,EAAO,OAAA;AAAA,MACP,QAAA,EAAU,CAAA,CAAA,KAAK,UAAA,CAAW,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,MACxC,OAAA,EAAQ,UAAA;AAAA,MACR,IAAA,EAAK,OAAA;AAAA,MACL,WAAW,OAAA,CAAQ,SAAA;AAAA,MACnB,WAAA,EAAY,iBAAA;AAAA,MACZ,UAAA,EAAW;AAAA;AAAA,GACb,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,iBAAA;AAAA,MACN,KAAA,EAAO,cAAA;AAAA,MACP,QAAA,EAAU,CAAA,CAAA,KAAK,iBAAA,CAAkB,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,MAC/C,OAAA,EAAQ,UAAA;AAAA,MACR,IAAA,EAAK,OAAA;AAAA,MACL,WAAW,OAAA,CAAQ,SAAA;AAAA,MACnB,WAAA,EAAY,YAAA;AAAA,MACZ,UAAA,EAAW;AAAA;AAAA,GAEf,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,SAAA,EAAW,QAAQ,OAAA,EAAA,kBACtB,KAAA,CAAA,aAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,eAAA;AAAA,MACN,KAAA,EAAO,WAAA;AAAA,MACP,QAAA,EAAU,CAAA,CAAA,KAAK,cAAA,CAAe,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,MAC5C,OAAA,EAAQ,UAAA;AAAA,MACR,IAAA,EAAK,OAAA;AAAA,MACL,WAAW,OAAA,CAAQ,SAAA;AAAA,MACnB,WAAA,EAAY,sBAAA;AAAA,MACZ,UAAA,EAAW;AAAA;AAAA,GAEf,CAAA,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAM,uBAAA;AAAA,MACN,KAAA,EAAO,aAAA;AAAA,MACP,QAAA,EAAU,CAAA,CAAA,KAAK,gBAAA,CAAiB,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,MAC9C,OAAA,EAAQ,UAAA;AAAA,MACR,IAAA,EAAK,OAAA;AAAA,MACL,SAAA,EAAS,IAAA;AAAA,MACT,IAAA,EAAM,CAAA;AAAA,MACN,WAAW,OAAA,CAAQ,eAAA;AAAA,MACnB,WAAA,EAAY,qEAAA;AAAA,MACZ,UAAA,EAAW;AAAA;AAAA,GACb,sCACC,GAAA,EAAA,EAAI,KAAA,EAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,GAAA,EAAK,CAAA,EAAE,EAAA,kBACpC,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,WAAA;AAAA,MACR,KAAA,EAAM,SAAA;AAAA,MACN,OAAA,EAAS,YAAA;AAAA,MACT,QAAA,EAAU,UAAA,IAAc,CAAC,OAAA,CAAQ,IAAA,EAAK,IAAK,CAAC,WAAA,CAAY,IAAA,EAAK,IAAK,CAAC,aAAA,CAAc,IAAA;AAAK,KAAA;AAAA,IAErF,aAAa,gBAAA,GAAmB;AAAA,GACnC,kBACA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,UAAA;AAAA,MACR,SAAS,MAAM;AAAE,QAAA,WAAA,CAAY,KAAK,CAAA;AAAG,QAAA,UAAA,CAAW,EAAE,CAAA;AAAG,QAAA,iBAAA,CAAkB,YAAY,CAAA;AAAG,QAAA,cAAA,CAAe,EAAE,CAAA;AAAG,QAAA,gBAAA,CAAiB,EAAE,CAAA;AAAA,MAAG,CAAA;AAAA,MAChI,QAAA,EAAU,UAAA;AAAA,MACV,KAAA,EAAO,EAAE,UAAA,EAAY,CAAA;AAAE,KAAA;AAAA,IACxB;AAAA,GAGH,CACF,CAAA,kBAMF,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,IAAI,CAAA,EAAA,EACN,OAAA,mBACC,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,SAAQ,OAAA,EAAQ,KAAA,EAAM,eAAA,EAAA,EAAgB,2BAAyB,IACzE,OAAA,CAAQ,MAAA,KAAW,CAAA,mBACrB,KAAA,CAAA,aAAA,CAAC,OAAI,SAAA,EAAW,OAAA,CAAQ,UAAA,EAAA,kBACtB,KAAA,CAAA,aAAA,CAAC,cAAW,OAAA,EAAQ,OAAA,EAAA,EAAQ,oGAE5B,CACF,oBAEA,KAAA,CAAA,aAAA,CAAC,cAAA,EAAA,EAAe,SAAA,EAAW,KAAA,EAAO,SAAQ,UAAA,EAAA,kBACxC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAM,IAAA,EAAK,2BACV,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,IAAA,sCACE,SAAA,EAAA,IAAA,EAAU,cAAY,CAAA,kBACvB,KAAA,CAAA,aAAA,CAAC,iBAAU,aAAW,CAAA,kBACtB,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,EAAU,WAAS,CAAA,kBACpB,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,EAAU,QAAM,mBACjB,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,EAAU,aAAW,CAAA,sCACrB,SAAA,EAAA,EAAU,KAAA,EAAM,OAAA,EAAA,EAAQ,SAAO,CAClC,CACF,CAAA,sCACC,SAAA,EAAA,IAAA,EACE,OAAA,CAAQ,IAAI,CAAA,GAAA,KAAO;AAClB,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AACvC,IAAA,2CACG,QAAA,EAAA,EAAS,GAAA,EAAK,IAAI,EAAA,EAAI,SAAA,EAAW,YAAY,OAAA,CAAQ,UAAA,GAAa,0BACjE,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,sCACE,QAAA,EAAA,IAAA,EAAQ,GAAA,CAAI,QAAS,CACxB,CAAA,sCACC,SAAA,EAAA,EAAU,KAAA,EAAO,EAAE,QAAA,EAAU,UAAS,EAAA,EACpC,GAAA,CAAI,mBAAmB,YAC1B,CAAA,sCACC,SAAA,EAAA,EAAU,KAAA,EAAO,EAAE,UAAA,EAAY,WAAA,EAAa,UAAU,QAAA,EAAS,EAAA,EAC7D,IAAI,aACP,CAAA,sCACC,SAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,MAAC,IAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO,GAAA,CAAI,MAAA,GAAS,QAAA,GAAW,UAAA;AAAA,QAC/B,IAAA,EAAK,OAAA;AAAA,QACL,SAAA,EAAW,GAAA,CAAI,MAAA,GAAS,OAAA,CAAQ,aAAa,OAAA,CAAQ,YAAA;AAAA,QACrD,OAAA,EAAS,MAAM,YAAA,CAAa,GAAG,CAAA;AAAA,QAC/B,SAAA,EAAS;AAAA;AAAA,KAEb,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,IAAA,EACE,GAAA,CAAI,iCACH,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,SAAA,EAAW,OAAA,CAAQ,cAC5B,IAAI,IAAA,CAAK,GAAA,CAAI,cAAc,EAAE,cAAA,EAChC,CAAA,mBAEA,KAAA,CAAA,aAAA,CAAC,cAAW,SAAA,EAAW,OAAA,CAAQ,UAAA,EAAA,EAAY,OAAK,CAEpD,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,EAAU,KAAA,EAAM,2BACf,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,UAAA,EAAA,sCACZ,MAAA,EAAA,IAAA,kBACC,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,OAAA;AAAA,QACL,OAAA,EAAS,MAAM,UAAA,CAAW,GAAA,CAAI,EAAE,CAAA;AAAA,QAChC,QAAA,EAAU,SAAA;AAAA,QACV,YAAA,EAAW;AAAA,OAAA;AAAA,sBAEX,KAAA,CAAA,aAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UACC,QAAA,EAAS,OAAA;AAAA,UACT,KAAA,EAAO,SAAA,GAAY,EAAE,SAAA,EAAW,2BAA0B,GAAI;AAAA;AAAA;AAChE,KAEJ,CACF,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAQ,OAAM,sBAAA,EAAA,kBACb,KAAA,CAAA,aAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,OAAA;AAAA,QACL,OAAA,EAAS,MAAM,YAAA,CAAa,GAAA,CAAI,EAAE,CAAA;AAAA,QAClC,QAAA,EAAU,SAAA;AAAA,QACV,YAAA,EAAW;AAAA,OAAA;AAAA,sBAEX,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,QAAA,EAAS,OAAA,EAAQ;AAAA,KAEjC,CACF,CACF,CAAA;AAAA,EAEJ,CAAC,CACH,CACF,CACF,CAEJ,CAAA,sCAEC,OAAA,EAAA,IAAA,EAAO;AAAA;AAAA,MAAA,CAEN,CACJ,CAAA;AAEJ;;;;"}
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState, useCallback, useEffect } from 'react';
1
+ import React__default, { useRef, useState, useCallback, useEffect } from 'react';
2
2
  import { Grid, Typography, Box, Button, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, IconButton } from '@material-ui/core';
3
3
  import { makeStyles } from '@material-ui/core/styles';
4
4
  import DeleteIcon from '@material-ui/icons/Delete';
@@ -101,7 +101,7 @@ const SettingsContent = ({ api }) => {
101
101
  } catch {
102
102
  }
103
103
  };
104
- return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(GithubSyncSection, { api, onSyncComplete: loadMappings })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Upload Developer Names (CSV)" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, "Upload a CSV file with developer names (one name per line). The system will compute SHA-256 hashes using the configured salt and store the masked-to-real name mappings. Duplicate names will be updated."), /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(
104
+ return /* @__PURE__ */ React__default.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(GithubSyncSection, { api, onSyncComplete: loadMappings })), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Upload Developer Names (CSV)" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, "Upload a CSV file with developer names (one name per line). The system will compute SHA-256 hashes using the configured salt and store the masked-to-real name mappings. Duplicate names will be updated."), /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React__default.createElement(
105
105
  "input",
106
106
  {
107
107
  ref: fileInputRef,
@@ -111,31 +111,31 @@ const SettingsContent = ({ api }) => {
111
111
  onChange: handleFileSelect,
112
112
  id: "devxp-csv-upload"
113
113
  }
114
- ), /* @__PURE__ */ React.createElement("label", { htmlFor: "devxp-csv-upload" }, /* @__PURE__ */ React.createElement(Button, { variant: "outlined", component: "span" }, "Choose CSV File")), selectedFileName && /* @__PURE__ */ React.createElement(Typography, { className: classes.fileName, variant: "body2" }, selectedFileName), /* @__PURE__ */ React.createElement(
114
+ ), /* @__PURE__ */ React__default.createElement("label", { htmlFor: "devxp-csv-upload" }, /* @__PURE__ */ React__default.createElement(Button, { variant: "outlined", component: "span" }, "Choose CSV File")), selectedFileName && /* @__PURE__ */ React__default.createElement(Typography, { className: classes.fileName, variant: "body2" }, selectedFileName), /* @__PURE__ */ React__default.createElement(
115
115
  Button,
116
116
  {
117
117
  variant: "contained",
118
118
  color: "primary",
119
119
  onClick: handleUpload,
120
120
  disabled: !fileContent || uploading,
121
- startIcon: /* @__PURE__ */ React.createElement(CloudUploadIcon, null),
121
+ startIcon: /* @__PURE__ */ React__default.createElement(CloudUploadIcon, null),
122
122
  style: { marginLeft: 16 }
123
123
  },
124
124
  uploading ? "Processing..." : "Upload & Process"
125
- )), statusMessage && /* @__PURE__ */ React.createElement(
125
+ )), statusMessage && /* @__PURE__ */ React__default.createElement(
126
126
  Box,
127
127
  {
128
128
  className: `${classes.statusMessage} ${statusMessage.isError ? classes.error : classes.success}`
129
129
  },
130
- /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, statusMessage.text)
131
- ))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Developer Mappings" }, loading ? /* @__PURE__ */ React.createElement(Typography, null, "Loading mappings...") : mappings.length === 0 ? /* @__PURE__ */ React.createElement(Box, { className: classes.emptyState }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, "No developer mappings yet. Upload a CSV file or sync via GitHub above to get started.")) : /* @__PURE__ */ React.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Masked Name"), /* @__PURE__ */ React.createElement(TableCell, null, "Real Name"), /* @__PURE__ */ React.createElement(TableCell, null, "Created"), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React.createElement(TableBody, null, mappings.map((m) => /* @__PURE__ */ React.createElement(TableRow, { key: m.masked_name }, /* @__PURE__ */ React.createElement(TableCell, { className: classes.maskedCell }, m.masked_name), /* @__PURE__ */ React.createElement(TableCell, null, m.real_name), /* @__PURE__ */ React.createElement(TableCell, null, new Date(m.created_at).toLocaleDateString()), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React.createElement(
130
+ /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2" }, statusMessage.text)
131
+ ))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Developer Mappings" }, loading ? /* @__PURE__ */ React__default.createElement(Typography, null, "Loading mappings...") : mappings.length === 0 ? /* @__PURE__ */ React__default.createElement(Box, { className: classes.emptyState }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body1" }, "No developer mappings yet. Upload a CSV file or sync via GitHub above to get started.")) : /* @__PURE__ */ React__default.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React__default.createElement(Table, { size: "small" }, /* @__PURE__ */ React__default.createElement(TableHead, null, /* @__PURE__ */ React__default.createElement(TableRow, null, /* @__PURE__ */ React__default.createElement(TableCell, null, "Masked Name"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Real Name"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Created"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React__default.createElement(TableBody, null, mappings.map((m) => /* @__PURE__ */ React__default.createElement(TableRow, { key: m.masked_name }, /* @__PURE__ */ React__default.createElement(TableCell, { className: classes.maskedCell }, m.masked_name), /* @__PURE__ */ React__default.createElement(TableCell, null, m.real_name), /* @__PURE__ */ React__default.createElement(TableCell, null, new Date(m.created_at).toLocaleDateString()), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(
132
132
  IconButton,
133
133
  {
134
134
  size: "small",
135
135
  onClick: () => handleDelete(m.masked_name),
136
136
  "aria-label": "Delete mapping"
137
137
  },
138
- /* @__PURE__ */ React.createElement(DeleteIcon, { fontSize: "small" })
138
+ /* @__PURE__ */ React__default.createElement(DeleteIcon, { fontSize: "small" })
139
139
  ))))))))));
140
140
  };
141
141
 
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsContent.esm.js","sources":["../../src/components/SettingsContent.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n Typography,\n Button,\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n IconButton,\n Grid,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport { InfoCard } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { DeveloperMapping } from '../types';\nimport { GithubSyncSection } from './GithubSyncSection';\n\nconst useStyles = makeStyles(theme => ({\n fileInput: {\n display: 'none',\n },\n fileName: {\n marginLeft: theme.spacing(2),\n color: theme.palette.text.secondary,\n },\n maskedCell: {\n fontFamily: 'monospace',\n fontSize: '0.9em',\n },\n statusMessage: {\n marginTop: theme.spacing(2),\n padding: theme.spacing(1.5),\n borderRadius: theme.shape.borderRadius,\n },\n success: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n error: {\n backgroundColor: '#ffebee',\n color: '#c62828',\n },\n emptyState: {\n textAlign: 'center',\n padding: theme.spacing(4),\n color: theme.palette.text.secondary,\n },\n}));\n\ninterface SettingsContentProps {\n api: DevxpApi;\n}\n\nexport const SettingsContent = ({ api }: SettingsContentProps) => {\n const classes = useStyles();\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [mappings, setMappings] = useState<DeveloperMapping[]>([]);\n const [loading, setLoading] = useState(true);\n const [selectedFileName, setSelectedFileName] = useState('');\n const [fileContent, setFileContent] = useState('');\n const [uploading, setUploading] = useState(false);\n const [statusMessage, setStatusMessage] = useState<{\n text: string;\n isError: boolean;\n } | null>(null);\n\n const loadMappings = useCallback(async () => {\n try {\n setLoading(true);\n const result = await api.getMappings();\n setMappings(result.mappings);\n } catch {\n // Failed to load mappings\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n useEffect(() => {\n loadMappings();\n }, [loadMappings]);\n\n const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n if (!file) return;\n\n setSelectedFileName(file.name);\n setStatusMessage(null);\n\n const reader = new FileReader();\n reader.onload = e => {\n setFileContent(e.target?.result as string);\n };\n reader.readAsText(file);\n };\n\n const handleUpload = async () => {\n if (!fileContent) return;\n\n setUploading(true);\n setStatusMessage(null);\n try {\n const result = await api.uploadCsv(fileContent);\n if (result.error) {\n setStatusMessage({ text: result.error, isError: true });\n } else {\n setStatusMessage({ text: result.message, isError: false });\n setSelectedFileName('');\n setFileContent('');\n if (fileInputRef.current) fileInputRef.current.value = '';\n await loadMappings();\n }\n } catch (e: any) {\n setStatusMessage({\n text: e.message || 'Upload failed',\n isError: true,\n });\n } finally {\n setUploading(false);\n }\n };\n\n const handleDelete = async (maskedName: string) => {\n try {\n await api.deleteMapping(maskedName);\n await loadMappings();\n } catch {\n // Delete failed\n }\n };\n\n return (\n <Grid container spacing={3}>\n {/* GitHub Auto-Sync Section */}\n <Grid item xs={12}>\n <GithubSyncSection api={api} onSyncComplete={loadMappings} />\n </Grid>\n\n {/* CSV Upload Section */}\n <Grid item xs={12}>\n <InfoCard title=\"Upload Developer Names (CSV)\">\n <Typography variant=\"body2\" color=\"textSecondary\" paragraph>\n Upload a CSV file with developer names (one name per line). The\n system will compute SHA-256 hashes using the configured salt and\n store the masked-to-real name mappings. Duplicate names will be\n updated.\n </Typography>\n <Box display=\"flex\" alignItems=\"center\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".csv,.txt\"\n className={classes.fileInput}\n onChange={handleFileSelect}\n id=\"devxp-csv-upload\"\n />\n <label htmlFor=\"devxp-csv-upload\">\n <Button variant=\"outlined\" component=\"span\">\n Choose CSV File\n </Button>\n </label>\n {selectedFileName && (\n <Typography className={classes.fileName} variant=\"body2\">\n {selectedFileName}\n </Typography>\n )}\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleUpload}\n disabled={!fileContent || uploading}\n startIcon={<CloudUploadIcon />}\n style={{ marginLeft: 16 }}\n >\n {uploading ? 'Processing...' : 'Upload & Process'}\n </Button>\n </Box>\n {statusMessage && (\n <Box\n className={`${classes.statusMessage} ${statusMessage.isError ? classes.error : classes.success}`}\n >\n <Typography variant=\"body2\">{statusMessage.text}</Typography>\n </Box>\n )}\n </InfoCard>\n </Grid>\n\n {/* Mappings Table */}\n <Grid item xs={12}>\n <InfoCard title=\"Developer Mappings\">\n {loading ? (\n <Typography>Loading mappings...</Typography>\n ) : mappings.length === 0 ? (\n <Box className={classes.emptyState}>\n <Typography variant=\"body1\">\n No developer mappings yet. Upload a CSV file or sync via GitHub\n above to get started.\n </Typography>\n </Box>\n ) : (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Masked Name</TableCell>\n <TableCell>Real Name</TableCell>\n <TableCell>Created</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {mappings.map(m => (\n <TableRow key={m.masked_name}>\n <TableCell className={classes.maskedCell}>\n {m.masked_name}\n </TableCell>\n <TableCell>{m.real_name}</TableCell>\n <TableCell>\n {new Date(m.created_at).toLocaleDateString()}\n </TableCell>\n <TableCell align=\"right\">\n <IconButton\n size=\"small\"\n onClick={() => handleDelete(m.masked_name)}\n aria-label=\"Delete mapping\"\n >\n <DeleteIcon fontSize=\"small\" />\n </IconButton>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n )}\n </InfoCard>\n </Grid>\n </Grid>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SettingsContent.esm.js","sources":["../../src/components/SettingsContent.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback, useRef } from 'react';\nimport {\n Typography,\n Button,\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n IconButton,\n Grid,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport { InfoCard } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { DeveloperMapping } from '../types';\nimport { GithubSyncSection } from './GithubSyncSection';\n\nconst useStyles = makeStyles(theme => ({\n fileInput: {\n display: 'none',\n },\n fileName: {\n marginLeft: theme.spacing(2),\n color: theme.palette.text.secondary,\n },\n maskedCell: {\n fontFamily: 'monospace',\n fontSize: '0.9em',\n },\n statusMessage: {\n marginTop: theme.spacing(2),\n padding: theme.spacing(1.5),\n borderRadius: theme.shape.borderRadius,\n },\n success: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n error: {\n backgroundColor: '#ffebee',\n color: '#c62828',\n },\n emptyState: {\n textAlign: 'center',\n padding: theme.spacing(4),\n color: theme.palette.text.secondary,\n },\n}));\n\ninterface SettingsContentProps {\n api: DevxpApi;\n}\n\nexport const SettingsContent = ({ api }: SettingsContentProps) => {\n const classes = useStyles();\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [mappings, setMappings] = useState<DeveloperMapping[]>([]);\n const [loading, setLoading] = useState(true);\n const [selectedFileName, setSelectedFileName] = useState('');\n const [fileContent, setFileContent] = useState('');\n const [uploading, setUploading] = useState(false);\n const [statusMessage, setStatusMessage] = useState<{\n text: string;\n isError: boolean;\n } | null>(null);\n\n const loadMappings = useCallback(async () => {\n try {\n setLoading(true);\n const result = await api.getMappings();\n setMappings(result.mappings);\n } catch {\n // Failed to load mappings\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n useEffect(() => {\n loadMappings();\n }, [loadMappings]);\n\n const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n if (!file) return;\n\n setSelectedFileName(file.name);\n setStatusMessage(null);\n\n const reader = new FileReader();\n reader.onload = e => {\n setFileContent(e.target?.result as string);\n };\n reader.readAsText(file);\n };\n\n const handleUpload = async () => {\n if (!fileContent) return;\n\n setUploading(true);\n setStatusMessage(null);\n try {\n const result = await api.uploadCsv(fileContent);\n if (result.error) {\n setStatusMessage({ text: result.error, isError: true });\n } else {\n setStatusMessage({ text: result.message, isError: false });\n setSelectedFileName('');\n setFileContent('');\n if (fileInputRef.current) fileInputRef.current.value = '';\n await loadMappings();\n }\n } catch (e: any) {\n setStatusMessage({\n text: e.message || 'Upload failed',\n isError: true,\n });\n } finally {\n setUploading(false);\n }\n };\n\n const handleDelete = async (maskedName: string) => {\n try {\n await api.deleteMapping(maskedName);\n await loadMappings();\n } catch {\n // Delete failed\n }\n };\n\n return (\n <Grid container spacing={3}>\n {/* GitHub Auto-Sync Section */}\n <Grid item xs={12}>\n <GithubSyncSection api={api} onSyncComplete={loadMappings} />\n </Grid>\n\n {/* CSV Upload Section */}\n <Grid item xs={12}>\n <InfoCard title=\"Upload Developer Names (CSV)\">\n <Typography variant=\"body2\" color=\"textSecondary\" paragraph>\n Upload a CSV file with developer names (one name per line). The\n system will compute SHA-256 hashes using the configured salt and\n store the masked-to-real name mappings. Duplicate names will be\n updated.\n </Typography>\n <Box display=\"flex\" alignItems=\"center\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".csv,.txt\"\n className={classes.fileInput}\n onChange={handleFileSelect}\n id=\"devxp-csv-upload\"\n />\n <label htmlFor=\"devxp-csv-upload\">\n <Button variant=\"outlined\" component=\"span\">\n Choose CSV File\n </Button>\n </label>\n {selectedFileName && (\n <Typography className={classes.fileName} variant=\"body2\">\n {selectedFileName}\n </Typography>\n )}\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleUpload}\n disabled={!fileContent || uploading}\n startIcon={<CloudUploadIcon />}\n style={{ marginLeft: 16 }}\n >\n {uploading ? 'Processing...' : 'Upload & Process'}\n </Button>\n </Box>\n {statusMessage && (\n <Box\n className={`${classes.statusMessage} ${statusMessage.isError ? classes.error : classes.success}`}\n >\n <Typography variant=\"body2\">{statusMessage.text}</Typography>\n </Box>\n )}\n </InfoCard>\n </Grid>\n\n {/* Mappings Table */}\n <Grid item xs={12}>\n <InfoCard title=\"Developer Mappings\">\n {loading ? (\n <Typography>Loading mappings...</Typography>\n ) : mappings.length === 0 ? (\n <Box className={classes.emptyState}>\n <Typography variant=\"body1\">\n No developer mappings yet. Upload a CSV file or sync via GitHub\n above to get started.\n </Typography>\n </Box>\n ) : (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Masked Name</TableCell>\n <TableCell>Real Name</TableCell>\n <TableCell>Created</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {mappings.map(m => (\n <TableRow key={m.masked_name}>\n <TableCell className={classes.maskedCell}>\n {m.masked_name}\n </TableCell>\n <TableCell>{m.real_name}</TableCell>\n <TableCell>\n {new Date(m.created_at).toLocaleDateString()}\n </TableCell>\n <TableCell align=\"right\">\n <IconButton\n size=\"small\"\n onClick={() => handleDelete(m.masked_name)}\n aria-label=\"Delete mapping\"\n >\n <DeleteIcon fontSize=\"small\" />\n </IconButton>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n )}\n </InfoCard>\n </Grid>\n </Grid>\n );\n};\n"],"names":["React"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import * as react from 'react';
2
1
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
2
+ import * as React from 'react';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
 
5
5
  declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{}, {}, {
@@ -14,15 +14,15 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
14
14
  title?: string | undefined;
15
15
  path?: string | undefined;
16
16
  };
17
- output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
17
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<React.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
18
18
  optional: true;
19
- }> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {
19
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {
20
20
  optional: true;
21
21
  }> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
22
22
  optional: true;
23
23
  }>;
24
24
  inputs: {
25
- pages: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
25
+ pages: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<React.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
26
26
  optional: true;
27
27
  }> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.title", {
28
28
  optional: true;
@@ -38,7 +38,7 @@ declare const _default: _backstage_frontend_plugin_api.OverridableFrontendPlugin
38
38
  path: string;
39
39
  title?: string;
40
40
  icon?: _backstage_frontend_plugin_api.IconElement;
41
- loader?: () => Promise<react.JSX.Element>;
41
+ loader?: () => Promise<React.JSX.Element>;
42
42
  routeRef?: _backstage_frontend_plugin_api.RouteRef;
43
43
  noHeader?: boolean;
44
44
  };
@@ -1,10 +1,11 @@
1
+ import * as React from 'react';
1
2
  import { PageBlueprint, createFrontendPlugin } from '@backstage/frontend-plugin-api';
2
3
 
3
4
  const devxpPage = PageBlueprint.make({
4
5
  params: {
5
6
  path: "/devxp",
6
7
  title: "Dev Intelligence",
7
- loader: () => import('./components/DevxpPage.esm.js').then((m) => /* @__PURE__ */ React.createElement(m.DevxpPage, null))
8
+ loader: () => import('./components/DevxpPage.esm.js').then((m) => React.createElement(m.DevxpPage, null))
8
9
  }
9
10
  });
10
11
  var plugin = createFrontendPlugin({
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import { createFrontendPlugin, PageBlueprint } from '@backstage/frontend-plugin-api';\n\nconst devxpPage = PageBlueprint.make({\n params: {\n path: '/devxp',\n title: 'Dev Intelligence',\n loader: () =>\n import('./components/DevxpPage').then(m => <m.DevxpPage />),\n },\n});\n\nexport default createFrontendPlugin({\n pluginId: 'devxp',\n extensions: [devxpPage],\n});\n"],"names":[],"mappings":";;AAEA,MAAM,SAAA,GAAY,cAAc,IAAA,CAAK;AAAA,EACnC,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,QAAA;AAAA,IACN,KAAA,EAAO,kBAAA;AAAA,IACP,MAAA,EAAQ,MACN,OAAO,+BAAwB,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAAK,KAAA,CAAA,aAAA,CAAC,CAAA,CAAE,SAAA,EAAF,IAAY,CAAE;AAAA;AAEhE,CAAC,CAAA;AAED,aAAe,oBAAA,CAAqB;AAAA,EAClC,QAAA,EAAU,OAAA;AAAA,EACV,UAAA,EAAY,CAAC,SAAS;AACxB,CAAC,CAAA;;;;"}
1
+ {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createFrontendPlugin, PageBlueprint } from '@backstage/frontend-plugin-api';\n\nconst devxpPage = PageBlueprint.make({\n params: {\n path: '/devxp',\n title: 'Dev Intelligence',\n loader: () =>\n import('./components/DevxpPage').then(m => React.createElement(m.DevxpPage, null)),\n },\n});\n\nexport default createFrontendPlugin({\n pluginId: 'devxp',\n extensions: [devxpPage],\n});\n"],"names":[],"mappings":";;;AAGA,MAAM,SAAA,GAAY,cAAc,IAAA,CAAK;AAAA,EACnC,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,QAAA;AAAA,IACN,KAAA,EAAO,kBAAA;AAAA,IACP,MAAA,EAAQ,MACN,OAAO,+BAAwB,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,KAAK,KAAA,CAAM,aAAA,CAAc,CAAA,CAAE,SAAA,EAAW,IAAI,CAAC;AAAA;AAEvF,CAAC,CAAA;AAED,aAAe,oBAAA,CAAqB;AAAA,EAClC,QAAA,EAAU,OAAA;AAAA,EACV,UAAA,EAAY,CAAC,SAAS;AACxB,CAAC,CAAA;;;;"}
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@karimov-labs/backstage-plugin-devxp",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Backstage frontend plugin for developer intelligence — masked identity management, CSV upload, and unmask tooling.",
5
- "main": "src/index.ts",
6
- "types": "src/index.ts",
5
+ "main": "dist/index.esm.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
7
8
  "license": "Apache-2.0",
8
9
  "author": "karimov-labs",
9
10
  "homepage": "https://devxp.net",
@@ -19,10 +20,7 @@
19
20
  "pluginId": "devxp"
20
21
  },
21
22
  "publishConfig": {
22
- "access": "public",
23
- "main": "dist/index.cjs.js",
24
- "module": "dist/index.esm.js",
25
- "types": "dist/index.d.ts"
23
+ "access": "public"
26
24
  },
27
25
  "scripts": {
28
26
  "start": "backstage-cli package start",
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export { default } from './plugin';
2
- export { DevxpPage } from './components/DevxpPage';