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

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.
@@ -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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"}
@@ -0,0 +1,173 @@
1
+ import { makeStyles } from '@material-ui/core/styles';
2
+
3
+ const CATEGORIES = [
4
+ "Frontend Engineering",
5
+ "Backend Systems",
6
+ "Database & Data",
7
+ "Infrastructure (DevOps)",
8
+ "Mobile Development",
9
+ "AI & Machine Learning",
10
+ "Testing & QA",
11
+ "Security & Auth",
12
+ "Distributed Systems",
13
+ "Documentation"
14
+ ];
15
+ const SHORT_NAMES = {
16
+ "Frontend Engineering": "Frontend",
17
+ "Backend Systems": "Backend",
18
+ "Database & Data": "Database",
19
+ "Infrastructure (DevOps)": "DevOps",
20
+ "Mobile Development": "Mobile",
21
+ "AI & Machine Learning": "AI/ML",
22
+ "Testing & QA": "Testing",
23
+ "Security & Auth": "Security",
24
+ "Distributed Systems": "Distributed",
25
+ "Documentation": "Docs"
26
+ };
27
+ const CENTER_X = 180;
28
+ const CENTER_Y = 180;
29
+ const MAX_RADIUS = 130;
30
+ const LABEL_RADIUS = 155;
31
+ const NUM_LEVELS = 5;
32
+ const SVG_SIZE = 360;
33
+ function polarToCartesian(cx, cy, r, angleDeg) {
34
+ const rad = (angleDeg - 90) * Math.PI / 180;
35
+ return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
36
+ }
37
+ function buildPolygon(values, maxVal) {
38
+ const step = 360 / values.length;
39
+ const pts = values.map((v, i) => {
40
+ const r = Math.max(v, 0) / maxVal * MAX_RADIUS;
41
+ const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);
42
+ return `${p.x},${p.y}`;
43
+ });
44
+ return pts.join(" ");
45
+ }
46
+ function buildGridPolygon(level) {
47
+ const r = level / NUM_LEVELS * MAX_RADIUS;
48
+ const step = 360 / CATEGORIES.length;
49
+ return CATEGORIES.map((_, i) => {
50
+ const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);
51
+ return `${p.x},${p.y}`;
52
+ }).join(" ");
53
+ }
54
+ const useStyles = makeStyles((theme) => ({
55
+ svg: {
56
+ width: "100%",
57
+ maxWidth: SVG_SIZE,
58
+ display: "block",
59
+ margin: "0 auto"
60
+ },
61
+ gridPolygon: {
62
+ fill: "none",
63
+ stroke: theme.palette.divider,
64
+ strokeWidth: 1
65
+ },
66
+ radialLine: {
67
+ stroke: theme.palette.divider,
68
+ strokeWidth: 1
69
+ },
70
+ dataPolygon: {
71
+ fill: "rgba(63, 81, 181, 0.18)",
72
+ stroke: "#3f51b5",
73
+ strokeWidth: 2
74
+ },
75
+ comparePolygon: {
76
+ fill: "rgba(233, 30, 99, 0.12)",
77
+ stroke: "#e91e63",
78
+ strokeWidth: 2
79
+ },
80
+ dataPoint: {
81
+ fill: "#3f51b5"
82
+ },
83
+ comparePoint: {
84
+ fill: "#e91e63"
85
+ },
86
+ label: {
87
+ fontSize: 10,
88
+ fontWeight: 600,
89
+ fill: theme.palette.text.primary,
90
+ dominantBaseline: "middle"
91
+ }
92
+ }));
93
+ const SpiderChart = ({ primary, compare, maxValue = 10 }) => {
94
+ const classes = useStyles();
95
+ const primaryMap = new Map(primary.categoryAverages.map((c) => [c.category, c.averageProficiency]));
96
+ const compareMap = compare ? new Map(compare.categoryAverages.map((c) => [c.category, c.averageProficiency])) : null;
97
+ const primaryValues = CATEGORIES.map((cat) => Math.max(primaryMap.get(cat) ?? 0, 1));
98
+ const compareValues = compareMap ? CATEGORIES.map((cat) => Math.max(compareMap.get(cat) ?? 0, 1)) : null;
99
+ const step = 360 / CATEGORIES.length;
100
+ return /* @__PURE__ */ React.createElement(
101
+ "svg",
102
+ {
103
+ viewBox: `0 0 ${SVG_SIZE} ${SVG_SIZE}`,
104
+ className: classes.svg,
105
+ "aria-label": "Skill radar chart"
106
+ },
107
+ Array.from({ length: NUM_LEVELS }, (_, i) => i + 1).map((level) => /* @__PURE__ */ React.createElement(
108
+ "polygon",
109
+ {
110
+ key: level,
111
+ points: buildGridPolygon(level),
112
+ className: classes.gridPolygon
113
+ }
114
+ )),
115
+ CATEGORIES.map((_, i) => {
116
+ const outer = polarToCartesian(CENTER_X, CENTER_Y, MAX_RADIUS, i * step);
117
+ return /* @__PURE__ */ React.createElement(
118
+ "line",
119
+ {
120
+ key: i,
121
+ x1: CENTER_X,
122
+ y1: CENTER_Y,
123
+ x2: outer.x,
124
+ y2: outer.y,
125
+ className: classes.radialLine
126
+ }
127
+ );
128
+ }),
129
+ compareValues && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
130
+ "polygon",
131
+ {
132
+ points: buildPolygon(compareValues, maxValue),
133
+ className: classes.comparePolygon
134
+ }
135
+ ), compareValues.map((v, i) => {
136
+ const r = Math.max(v, 0) / maxValue * MAX_RADIUS;
137
+ const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);
138
+ return /* @__PURE__ */ React.createElement("circle", { key: i, cx: p.x, cy: p.y, r: 3, className: classes.comparePoint });
139
+ })),
140
+ /* @__PURE__ */ React.createElement(
141
+ "polygon",
142
+ {
143
+ points: buildPolygon(primaryValues, maxValue),
144
+ className: classes.dataPolygon
145
+ }
146
+ ),
147
+ primaryValues.map((v, i) => {
148
+ const r = Math.max(v, 0) / maxValue * MAX_RADIUS;
149
+ const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);
150
+ return /* @__PURE__ */ React.createElement("circle", { key: i, cx: p.x, cy: p.y, r: 4, className: classes.dataPoint });
151
+ }),
152
+ CATEGORIES.map((cat, i) => {
153
+ const pos = polarToCartesian(CENTER_X, CENTER_Y, LABEL_RADIUS, i * step);
154
+ let anchor = "middle";
155
+ if (pos.x < CENTER_X - 10) anchor = "end";
156
+ else if (pos.x > CENTER_X + 10) anchor = "start";
157
+ return /* @__PURE__ */ React.createElement(
158
+ "text",
159
+ {
160
+ key: cat,
161
+ x: pos.x,
162
+ y: pos.y,
163
+ textAnchor: anchor,
164
+ className: classes.label
165
+ },
166
+ SHORT_NAMES[cat] ?? cat
167
+ );
168
+ })
169
+ );
170
+ };
171
+
172
+ export { SpiderChart };
173
+ //# sourceMappingURL=SpiderChart.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpiderChart.esm.js","sources":["../../src/components/SpiderChart.tsx"],"sourcesContent":["import { makeStyles } from '@material-ui/core/styles';\n\nconst CATEGORIES = [\n 'Frontend Engineering',\n 'Backend Systems',\n 'Database & Data',\n 'Infrastructure (DevOps)',\n 'Mobile Development',\n 'AI & Machine Learning',\n 'Testing & QA',\n 'Security & Auth',\n 'Distributed Systems',\n 'Documentation',\n];\n\nconst SHORT_NAMES: Record<string, string> = {\n 'Frontend Engineering': 'Frontend',\n 'Backend Systems': 'Backend',\n 'Database & Data': 'Database',\n 'Infrastructure (DevOps)': 'DevOps',\n 'Mobile Development': 'Mobile',\n 'AI & Machine Learning': 'AI/ML',\n 'Testing & QA': 'Testing',\n 'Security & Auth': 'Security',\n 'Distributed Systems': 'Distributed',\n 'Documentation': 'Docs',\n};\n\nconst CENTER_X = 180;\nconst CENTER_Y = 180;\nconst MAX_RADIUS = 130;\nconst LABEL_RADIUS = 155;\nconst NUM_LEVELS = 5;\nconst SVG_SIZE = 360;\n\nfunction polarToCartesian(cx: number, cy: number, r: number, angleDeg: number) {\n const rad = ((angleDeg - 90) * Math.PI) / 180;\n return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };\n}\n\nfunction buildPolygon(values: number[], maxVal: number): string {\n const step = 360 / values.length;\n const pts = values.map((v, i) => {\n const r = (Math.max(v, 0) / maxVal) * MAX_RADIUS;\n const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);\n return `${p.x},${p.y}`;\n });\n return pts.join(' ');\n}\n\nfunction buildGridPolygon(level: number): string {\n const r = (level / NUM_LEVELS) * MAX_RADIUS;\n const step = 360 / CATEGORIES.length;\n return CATEGORIES.map((_, i) => {\n const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);\n return `${p.x},${p.y}`;\n }).join(' ');\n}\n\nconst useStyles = makeStyles(theme => ({\n svg: {\n width: '100%',\n maxWidth: SVG_SIZE,\n display: 'block',\n margin: '0 auto',\n },\n gridPolygon: {\n fill: 'none',\n stroke: theme.palette.divider,\n strokeWidth: 1,\n },\n radialLine: {\n stroke: theme.palette.divider,\n strokeWidth: 1,\n },\n dataPolygon: {\n fill: 'rgba(63, 81, 181, 0.18)',\n stroke: '#3f51b5',\n strokeWidth: 2,\n },\n comparePolygon: {\n fill: 'rgba(233, 30, 99, 0.12)',\n stroke: '#e91e63',\n strokeWidth: 2,\n },\n dataPoint: {\n fill: '#3f51b5',\n },\n comparePoint: {\n fill: '#e91e63',\n },\n label: {\n fontSize: 10,\n fontWeight: 600,\n fill: theme.palette.text.primary,\n dominantBaseline: 'middle',\n },\n}));\n\nexport interface SpiderChartData {\n /** Map of category name → proficiency value (0-10 scale) */\n categoryAverages: Array<{ category: string; averageProficiency: number }>;\n}\n\ninterface SpiderChartProps {\n primary: SpiderChartData;\n /** Optional second dataset shown as overlay (e.g. platform average or second developer) */\n compare?: SpiderChartData;\n maxValue?: number;\n}\n\nexport const SpiderChart = ({ primary, compare, maxValue = 10 }: SpiderChartProps) => {\n const classes = useStyles();\n\n const primaryMap = new Map(primary.categoryAverages.map(c => [c.category, c.averageProficiency]));\n const compareMap = compare\n ? new Map(compare.categoryAverages.map(c => [c.category, c.averageProficiency]))\n : null;\n\n const primaryValues = CATEGORIES.map(cat => Math.max(primaryMap.get(cat) ?? 0, 1));\n const compareValues = compareMap ? CATEGORIES.map(cat => Math.max(compareMap.get(cat) ?? 0, 1)) : null;\n\n const step = 360 / CATEGORIES.length;\n\n return (\n <svg\n viewBox={`0 0 ${SVG_SIZE} ${SVG_SIZE}`}\n className={classes.svg}\n aria-label=\"Skill radar chart\"\n >\n {/* Grid */}\n {Array.from({ length: NUM_LEVELS }, (_, i) => i + 1).map(level => (\n <polygon\n key={level}\n points={buildGridPolygon(level)}\n className={classes.gridPolygon}\n />\n ))}\n\n {/* Radial lines */}\n {CATEGORIES.map((_, i) => {\n const outer = polarToCartesian(CENTER_X, CENTER_Y, MAX_RADIUS, i * step);\n return (\n <line\n key={i}\n x1={CENTER_X}\n y1={CENTER_Y}\n x2={outer.x}\n y2={outer.y}\n className={classes.radialLine}\n />\n );\n })}\n\n {/* Compare overlay */}\n {compareValues && (\n <>\n <polygon\n points={buildPolygon(compareValues, maxValue)}\n className={classes.comparePolygon}\n />\n {compareValues.map((v, i) => {\n const r = (Math.max(v, 0) / maxValue) * MAX_RADIUS;\n const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);\n return <circle key={i} cx={p.x} cy={p.y} r={3} className={classes.comparePoint} />;\n })}\n </>\n )}\n\n {/* Primary data */}\n <polygon\n points={buildPolygon(primaryValues, maxValue)}\n className={classes.dataPolygon}\n />\n {primaryValues.map((v, i) => {\n const r = (Math.max(v, 0) / maxValue) * MAX_RADIUS;\n const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);\n return <circle key={i} cx={p.x} cy={p.y} r={4} className={classes.dataPoint} />;\n })}\n\n {/* Category labels */}\n {CATEGORIES.map((cat, i) => {\n const pos = polarToCartesian(CENTER_X, CENTER_Y, LABEL_RADIUS, i * step);\n let anchor: 'inherit' | 'end' | 'start' | 'middle' = 'middle';\n if (pos.x < CENTER_X - 10) anchor = 'end';\n else if (pos.x > CENTER_X + 10) anchor = 'start';\n return (\n <text\n key={cat}\n x={pos.x}\n y={pos.y}\n textAnchor={anchor}\n className={classes.label}\n >\n {SHORT_NAMES[cat] ?? cat}\n </text>\n );\n })}\n </svg>\n );\n};\n"],"names":[],"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.2.0",
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';