@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.
- package/README.md +1 -1
- package/dist/api.esm.js +69 -0
- package/dist/api.esm.js.map +1 -1
- package/dist/components/AnalyticsDashboard.esm.js +249 -0
- package/dist/components/AnalyticsDashboard.esm.js.map +1 -0
- package/dist/components/DashboardContent.esm.js +15 -15
- package/dist/components/DashboardContent.esm.js.map +1 -1
- package/dist/components/DeveloperComparison.esm.js +258 -0
- package/dist/components/DeveloperComparison.esm.js.map +1 -0
- package/dist/components/DeveloperDetails.esm.js +129 -0
- package/dist/components/DeveloperDetails.esm.js.map +1 -0
- package/dist/components/DeveloperLeaderboard.esm.js +340 -0
- package/dist/components/DeveloperLeaderboard.esm.js.map +1 -0
- package/dist/components/DevxpPage.esm.js +84 -6
- 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/RepositoriesContent.esm.js +251 -0
- package/dist/components/RepositoriesContent.esm.js.map +1 -0
- package/dist/components/SettingsContent.esm.js +8 -8
- package/dist/components/SettingsContent.esm.js.map +1 -1
- package/dist/components/SpiderChart.esm.js +173 -0
- package/dist/components/SpiderChart.esm.js.map +1 -0
- 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
|
@@ -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<
|
|
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.
|
|
3
|
+
"version": "1.2.0",
|
|
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