@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 +1 -1
- package/dist/components/DashboardContent.esm.js +15 -15
- package/dist/components/DashboardContent.esm.js.map +1 -1
- package/dist/components/DevxpPage.esm.js +3 -3
- package/dist/components/DevxpPage.esm.js.map +1 -1
- package/dist/components/GithubSyncSection.esm.js +14 -14
- package/dist/components/GithubSyncSection.esm.js.map +1 -1
- package/dist/components/SettingsContent.esm.js +8 -8
- package/dist/components/SettingsContent.esm.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/plugin.esm.js +2 -1
- package/dist/plugin.esm.js.map +1 -1
- package/package.json +5 -7
- package/src/index.ts +0 -2
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.
|
|
35
|
+
| `@karimov-labs/backstage-plugin-devxp-backend` | `^1.1.0` |
|
|
36
36
|
| React | `^18` |
|
|
37
37
|
|
|
38
38
|
---
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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__ */
|
|
97
|
+
return /* @__PURE__ */ React__default.createElement(Typography, null, "Loading configuration...");
|
|
98
98
|
}
|
|
99
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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
|
|
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
|
|
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----- ... -----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----- ... -----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,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
121
|
+
startIcon: /* @__PURE__ */ React__default.createElement(CloudUploadIcon, null),
|
|
122
122
|
style: { marginLeft: 16 }
|
|
123
123
|
},
|
|
124
124
|
uploading ? "Processing..." : "Upload & Process"
|
|
125
|
-
)), statusMessage && /* @__PURE__ */
|
|
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__ */
|
|
131
|
-
))), /* @__PURE__ */
|
|
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__ */
|
|
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<
|
|
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<
|
|
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<
|
|
41
|
+
loader?: () => Promise<React.JSX.Element>;
|
|
42
42
|
routeRef?: _backstage_frontend_plugin_api.RouteRef;
|
|
43
43
|
noHeader?: boolean;
|
|
44
44
|
};
|
package/dist/plugin.esm.js
CHANGED
|
@@ -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) =>
|
|
8
|
+
loader: () => import('./components/DevxpPage.esm.js').then((m) => React.createElement(m.DevxpPage, null))
|
|
8
9
|
}
|
|
9
10
|
});
|
|
10
11
|
var plugin = createFrontendPlugin({
|
package/dist/plugin.esm.js.map
CHANGED
|
@@ -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 =>
|
|
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.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Backstage frontend plugin for developer intelligence — masked identity management, CSV upload, and unmask tooling.",
|
|
5
|
-
"main": "
|
|
6
|
-
"
|
|
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