@karimov-labs/backstage-plugin-devxp 1.1.1 → 1.2.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/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/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 +81 -3
- package/dist/components/DevxpPage.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/SpiderChart.esm.js +174 -0
- package/dist/components/SpiderChart.esm.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import React__default, { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import { Box, TextField, IconButton, CircularProgress, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Typography, LinearProgress, Button, Tooltip, Chip } from '@material-ui/core';
|
|
3
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
4
|
+
import SearchIcon from '@material-ui/icons/Search';
|
|
5
|
+
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
|
6
|
+
import StorageIcon from '@material-ui/icons/Storage';
|
|
7
|
+
import PersonIcon from '@material-ui/icons/Person';
|
|
8
|
+
import { InfoCard, EmptyState } from '@backstage/core-components';
|
|
9
|
+
|
|
10
|
+
const CATEGORY_COLORS = {
|
|
11
|
+
"Frontend Engineering": "#e91e63",
|
|
12
|
+
"Backend Systems": "#3f51b5",
|
|
13
|
+
"Database & Data": "#9c27b0",
|
|
14
|
+
"Infrastructure (DevOps)": "#ff9800",
|
|
15
|
+
"Mobile Development": "#4caf50",
|
|
16
|
+
"AI & Machine Learning": "#607d8b",
|
|
17
|
+
"Testing & QA": "#00bcd4",
|
|
18
|
+
"Security & Auth": "#f44336",
|
|
19
|
+
"Distributed Systems": "#795548",
|
|
20
|
+
"Documentation": "#9e9e9e"
|
|
21
|
+
};
|
|
22
|
+
const useStyles = makeStyles((theme) => ({
|
|
23
|
+
header: {
|
|
24
|
+
display: "flex",
|
|
25
|
+
alignItems: "center",
|
|
26
|
+
gap: theme.spacing(1),
|
|
27
|
+
marginBottom: theme.spacing(2)
|
|
28
|
+
},
|
|
29
|
+
searchRow: {
|
|
30
|
+
display: "flex",
|
|
31
|
+
gap: theme.spacing(2),
|
|
32
|
+
marginBottom: theme.spacing(2),
|
|
33
|
+
alignItems: "center"
|
|
34
|
+
},
|
|
35
|
+
searchField: {
|
|
36
|
+
flex: 1,
|
|
37
|
+
maxWidth: 400
|
|
38
|
+
},
|
|
39
|
+
clickableRow: {
|
|
40
|
+
cursor: "pointer",
|
|
41
|
+
"&:hover": {
|
|
42
|
+
backgroundColor: theme.palette.action.hover
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
repoName: {
|
|
46
|
+
fontFamily: "monospace",
|
|
47
|
+
fontWeight: 600
|
|
48
|
+
},
|
|
49
|
+
pagination: {
|
|
50
|
+
display: "flex",
|
|
51
|
+
justifyContent: "space-between",
|
|
52
|
+
alignItems: "center",
|
|
53
|
+
padding: theme.spacing(1, 0),
|
|
54
|
+
marginTop: theme.spacing(1)
|
|
55
|
+
},
|
|
56
|
+
categoryChip: {
|
|
57
|
+
margin: theme.spacing(0.25),
|
|
58
|
+
fontSize: "0.65rem",
|
|
59
|
+
height: 18
|
|
60
|
+
},
|
|
61
|
+
scoreBar: {
|
|
62
|
+
height: 6,
|
|
63
|
+
borderRadius: 3,
|
|
64
|
+
marginTop: 4
|
|
65
|
+
},
|
|
66
|
+
sectionTitle: {
|
|
67
|
+
fontWeight: 600,
|
|
68
|
+
marginBottom: theme.spacing(1),
|
|
69
|
+
marginTop: theme.spacing(2)
|
|
70
|
+
},
|
|
71
|
+
statValue: {
|
|
72
|
+
fontWeight: 700,
|
|
73
|
+
fontSize: "1.5rem"
|
|
74
|
+
},
|
|
75
|
+
statLabel: {
|
|
76
|
+
color: theme.palette.text.secondary,
|
|
77
|
+
fontSize: "0.75rem"
|
|
78
|
+
},
|
|
79
|
+
statBox: {
|
|
80
|
+
textAlign: "center",
|
|
81
|
+
padding: theme.spacing(1.5)
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
const RepositoriesContent = ({ api, onViewDeveloper }) => {
|
|
85
|
+
const [view, setView] = useState("list");
|
|
86
|
+
const [selectedRepo, setSelectedRepo] = useState("");
|
|
87
|
+
return /* @__PURE__ */ React__default.createElement(Box, null, view === "list" ? /* @__PURE__ */ React__default.createElement(
|
|
88
|
+
RepositoryList,
|
|
89
|
+
{
|
|
90
|
+
api,
|
|
91
|
+
onViewRepo: (repoName) => {
|
|
92
|
+
setSelectedRepo(repoName);
|
|
93
|
+
setView("details");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
) : /* @__PURE__ */ React__default.createElement(
|
|
97
|
+
RepositoryDetailsView,
|
|
98
|
+
{
|
|
99
|
+
api,
|
|
100
|
+
repoName: selectedRepo,
|
|
101
|
+
onBack: () => setView("list"),
|
|
102
|
+
onViewDeveloper
|
|
103
|
+
}
|
|
104
|
+
));
|
|
105
|
+
};
|
|
106
|
+
const RepositoryList = ({ api, onViewRepo }) => {
|
|
107
|
+
const classes = useStyles();
|
|
108
|
+
const [repositories, setRepositories] = useState([]);
|
|
109
|
+
const [totalCount, setTotalCount] = useState(0);
|
|
110
|
+
const [totalPages, setTotalPages] = useState(1);
|
|
111
|
+
const [page, setPage] = useState(1);
|
|
112
|
+
const pageSize = 25;
|
|
113
|
+
const [loading, setLoading] = useState(true);
|
|
114
|
+
const [error, setError] = useState(null);
|
|
115
|
+
const [searchInput, setSearchInput] = useState("");
|
|
116
|
+
const [appliedSearch, setAppliedSearch] = useState("");
|
|
117
|
+
const load = useCallback(
|
|
118
|
+
async (pg, search) => {
|
|
119
|
+
setLoading(true);
|
|
120
|
+
setError(null);
|
|
121
|
+
try {
|
|
122
|
+
const data = await api.getRepositories({ page: pg, pageSize, searchQuery: search });
|
|
123
|
+
setRepositories(data.repositories);
|
|
124
|
+
setTotalCount(data.totalCount);
|
|
125
|
+
setTotalPages(data.totalPages);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
setError(e.message ?? "Failed to load repositories");
|
|
128
|
+
} finally {
|
|
129
|
+
setLoading(false);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
[api]
|
|
133
|
+
);
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
load(page, appliedSearch);
|
|
136
|
+
}, [load, page, appliedSearch]);
|
|
137
|
+
const applySearch = () => {
|
|
138
|
+
setPage(1);
|
|
139
|
+
setAppliedSearch(searchInput);
|
|
140
|
+
};
|
|
141
|
+
const formatDate = (iso) => {
|
|
142
|
+
try {
|
|
143
|
+
return new Date(iso).toLocaleDateString(void 0, {
|
|
144
|
+
year: "numeric",
|
|
145
|
+
month: "short",
|
|
146
|
+
day: "numeric"
|
|
147
|
+
});
|
|
148
|
+
} catch {
|
|
149
|
+
return iso;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
return /* @__PURE__ */ React__default.createElement(InfoCard, { title: `Repositories (${totalCount})`, noPadding: false }, /* @__PURE__ */ React__default.createElement(Box, { className: classes.searchRow }, /* @__PURE__ */ React__default.createElement(
|
|
153
|
+
TextField,
|
|
154
|
+
{
|
|
155
|
+
className: classes.searchField,
|
|
156
|
+
size: "small",
|
|
157
|
+
variant: "outlined",
|
|
158
|
+
label: "Search repositories",
|
|
159
|
+
value: searchInput,
|
|
160
|
+
onChange: (e) => setSearchInput(e.target.value),
|
|
161
|
+
onKeyDown: (e) => e.key === "Enter" && applySearch(),
|
|
162
|
+
InputProps: {
|
|
163
|
+
endAdornment: /* @__PURE__ */ React__default.createElement(IconButton, { size: "small", onClick: applySearch }, /* @__PURE__ */ React__default.createElement(SearchIcon, { fontSize: "small" }))
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
)), loading ? /* @__PURE__ */ React__default.createElement(Box, { display: "flex", justifyContent: "center", py: 4 }, /* @__PURE__ */ React__default.createElement(CircularProgress, null)) : error ? /* @__PURE__ */ React__default.createElement(EmptyState, { missing: "data", title: "Repositories unavailable", description: error }) : repositories.length === 0 ? /* @__PURE__ */ React__default.createElement(EmptyState, { missing: "data", title: "No repositories found", description: "Try a different search." }) : /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__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, "Repository"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Developers"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Events"), /* @__PURE__ */ React__default.createElement(TableCell, { style: { minWidth: 140 } }, "Avg Proficiency"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Last Activity"))), /* @__PURE__ */ React__default.createElement(TableBody, null, repositories.map((repo) => /* @__PURE__ */ React__default.createElement(
|
|
167
|
+
TableRow,
|
|
168
|
+
{
|
|
169
|
+
key: repo.repoName,
|
|
170
|
+
className: classes.clickableRow,
|
|
171
|
+
onClick: () => onViewRepo(repo.repoName)
|
|
172
|
+
},
|
|
173
|
+
/* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "center", style: { gap: 8 } }, /* @__PURE__ */ React__default.createElement(StorageIcon, { fontSize: "small", color: "action" }), /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", className: classes.repoName }, repo.repoName))),
|
|
174
|
+
/* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2" }, repo.developerCount)),
|
|
175
|
+
/* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2" }, repo.eventCount.toLocaleString())),
|
|
176
|
+
/* @__PURE__ */ React__default.createElement(TableCell, { style: { minWidth: 140 } }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", style: { fontWeight: 600 } }, (repo.avgProficiency * 10).toFixed(1), "%"), /* @__PURE__ */ React__default.createElement(
|
|
177
|
+
LinearProgress,
|
|
178
|
+
{
|
|
179
|
+
variant: "determinate",
|
|
180
|
+
value: repo.avgProficiency * 10,
|
|
181
|
+
className: classes.scoreBar
|
|
182
|
+
}
|
|
183
|
+
)),
|
|
184
|
+
/* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption", color: "textSecondary" }, formatDate(repo.lastActivity)))
|
|
185
|
+
))))), /* @__PURE__ */ React__default.createElement(Box, { className: classes.pagination }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Showing ", (page - 1) * pageSize + 1, "\u2013", Math.min(page * pageSize, totalCount), " of ", totalCount), /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "center", style: { gap: 8 } }, /* @__PURE__ */ React__default.createElement(Button, { size: "small", disabled: page <= 1, onClick: () => setPage((p) => p - 1) }, "Prev"), /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption" }, page, " / ", totalPages), /* @__PURE__ */ React__default.createElement(Button, { size: "small", disabled: page >= totalPages, onClick: () => setPage((p) => p + 1) }, "Next")))));
|
|
186
|
+
};
|
|
187
|
+
const RepositoryDetailsView = ({ api, repoName, onBack, onViewDeveloper }) => {
|
|
188
|
+
const classes = useStyles();
|
|
189
|
+
const [details, setDetails] = useState(null);
|
|
190
|
+
const [loading, setLoading] = useState(true);
|
|
191
|
+
const [error, setError] = useState(null);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
setLoading(true);
|
|
194
|
+
setError(null);
|
|
195
|
+
api.getRepositoryDetails(repoName).then(setDetails).catch((e) => setError(e.message ?? "Failed to load repository details")).finally(() => setLoading(false));
|
|
196
|
+
}, [api, repoName]);
|
|
197
|
+
const formatDate = (iso) => {
|
|
198
|
+
try {
|
|
199
|
+
return new Date(iso).toLocaleDateString(void 0, {
|
|
200
|
+
year: "numeric",
|
|
201
|
+
month: "short",
|
|
202
|
+
day: "numeric"
|
|
203
|
+
});
|
|
204
|
+
} catch {
|
|
205
|
+
return iso;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
return /* @__PURE__ */ React__default.createElement(Box, null, /* @__PURE__ */ React__default.createElement(Box, { className: classes.header }, /* @__PURE__ */ React__default.createElement(Tooltip, { title: "Back to repositories" }, /* @__PURE__ */ React__default.createElement(IconButton, { size: "small", onClick: onBack }, /* @__PURE__ */ React__default.createElement(ArrowBackIcon, null))), /* @__PURE__ */ React__default.createElement(StorageIcon, { color: "action" }), /* @__PURE__ */ React__default.createElement(Typography, { variant: "h6", className: classes.repoName }, repoName)), loading ? /* @__PURE__ */ React__default.createElement(Box, { display: "flex", justifyContent: "center", py: 6 }, /* @__PURE__ */ React__default.createElement(CircularProgress, null)) : error ? /* @__PURE__ */ React__default.createElement(EmptyState, { missing: "data", title: "Details unavailable", description: error }) : details ? /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Overview" }, /* @__PURE__ */ React__default.createElement(Box, { display: "flex", justifyContent: "space-around", flexWrap: "wrap" }, /* @__PURE__ */ React__default.createElement(Box, { className: classes.statBox }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.statValue }, details.developerCount), /* @__PURE__ */ React__default.createElement(Typography, { className: classes.statLabel }, "Developers")), /* @__PURE__ */ React__default.createElement(Box, { className: classes.statBox }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.statValue }, details.eventCount.toLocaleString()), /* @__PURE__ */ React__default.createElement(Typography, { className: classes.statLabel }, "Skill Events")), /* @__PURE__ */ React__default.createElement(Box, { className: classes.statBox }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.statValue }, (details.avgProficiency * 10).toFixed(1), "%"), /* @__PURE__ */ React__default.createElement(Typography, { className: classes.statLabel }, "Avg Proficiency")), /* @__PURE__ */ React__default.createElement(Box, { className: classes.statBox }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.statValue, style: { fontSize: "1rem" } }, formatDate(details.lastActivity)), /* @__PURE__ */ React__default.createElement(Typography, { className: classes.statLabel }, "Last Activity")))), details.categoryBreakdown.length > 0 && /* @__PURE__ */ React__default.createElement(Box, { mt: 2 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Skill Categories" }, /* @__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, "Category"), /* @__PURE__ */ React__default.createElement(TableCell, { style: { minWidth: 160 } }, "Avg Proficiency"))), /* @__PURE__ */ React__default.createElement(TableBody, null, details.categoryBreakdown.map((cat) => /* @__PURE__ */ React__default.createElement(TableRow, { key: cat.category }, /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(
|
|
209
|
+
Chip,
|
|
210
|
+
{
|
|
211
|
+
label: cat.category,
|
|
212
|
+
size: "small",
|
|
213
|
+
className: classes.categoryChip,
|
|
214
|
+
style: {
|
|
215
|
+
backgroundColor: `${CATEGORY_COLORS[cat.category] ?? "#9e9e9e"}20`,
|
|
216
|
+
color: CATEGORY_COLORS[cat.category] ?? "#9e9e9e",
|
|
217
|
+
height: 20
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
)), /* @__PURE__ */ React__default.createElement(TableCell, { style: { minWidth: 160 } }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", style: { fontWeight: 600 } }, (cat.averageProficiency * 10).toFixed(1), "%"), /* @__PURE__ */ React__default.createElement(
|
|
221
|
+
LinearProgress,
|
|
222
|
+
{
|
|
223
|
+
variant: "determinate",
|
|
224
|
+
value: cat.averageProficiency * 10,
|
|
225
|
+
className: classes.scoreBar,
|
|
226
|
+
style: { backgroundColor: `${CATEGORY_COLORS[cat.category] ?? "#9e9e9e"}30` }
|
|
227
|
+
}
|
|
228
|
+
))))))))), details.topDevelopers.length > 0 && /* @__PURE__ */ React__default.createElement(Box, { mt: 2 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Top Developers" }, /* @__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, "#"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Developer"), /* @__PURE__ */ React__default.createElement(TableCell, { style: { minWidth: 140 } }, "Avg Proficiency"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Skills"), onViewDeveloper && /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }))), /* @__PURE__ */ React__default.createElement(TableBody, null, details.topDevelopers.map((dev, idx) => /* @__PURE__ */ React__default.createElement(
|
|
229
|
+
TableRow,
|
|
230
|
+
{
|
|
231
|
+
key: dev.userId,
|
|
232
|
+
className: onViewDeveloper ? classes.clickableRow : void 0,
|
|
233
|
+
onClick: () => onViewDeveloper?.(dev.userId)
|
|
234
|
+
},
|
|
235
|
+
/* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary" }, idx + 1)),
|
|
236
|
+
/* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", style: { fontFamily: "monospace", fontWeight: 600 } }, dev.userId)),
|
|
237
|
+
/* @__PURE__ */ React__default.createElement(TableCell, { style: { minWidth: 140 } }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", style: { fontWeight: 600 } }, (dev.averageProficiency * 10).toFixed(1), "%"), /* @__PURE__ */ React__default.createElement(
|
|
238
|
+
LinearProgress,
|
|
239
|
+
{
|
|
240
|
+
variant: "determinate",
|
|
241
|
+
value: dev.averageProficiency * 10,
|
|
242
|
+
className: classes.scoreBar
|
|
243
|
+
}
|
|
244
|
+
)),
|
|
245
|
+
/* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2" }, dev.skillCount)),
|
|
246
|
+
onViewDeveloper && /* @__PURE__ */ React__default.createElement(TableCell, { align: "right", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React__default.createElement(Tooltip, { title: "View developer details" }, /* @__PURE__ */ React__default.createElement(IconButton, { size: "small", onClick: () => onViewDeveloper(dev.userId) }, /* @__PURE__ */ React__default.createElement(PersonIcon, { fontSize: "small" }))))
|
|
247
|
+
)))))))) : null);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export { RepositoriesContent };
|
|
251
|
+
//# sourceMappingURL=RepositoriesContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RepositoriesContent.esm.js","sources":["../../src/components/RepositoriesContent.tsx"],"sourcesContent":["import React, { useState, useCallback, useEffect } from 'react';\nimport {\n Box,\n Typography,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n TextField,\n Chip,\n IconButton,\n Button,\n CircularProgress,\n Tooltip,\n LinearProgress,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport SearchIcon from '@material-ui/icons/Search';\nimport ArrowBackIcon from '@material-ui/icons/ArrowBack';\nimport StorageIcon from '@material-ui/icons/Storage';\nimport PersonIcon from '@material-ui/icons/Person';\nimport { InfoCard, EmptyState } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { RepositoryStats, RepositoryDetails } from '../types';\n\nconst CATEGORY_COLORS: Record<string, string> = {\n 'Frontend Engineering': '#e91e63',\n 'Backend Systems': '#3f51b5',\n 'Database & Data': '#9c27b0',\n 'Infrastructure (DevOps)': '#ff9800',\n 'Mobile Development': '#4caf50',\n 'AI & Machine Learning': '#607d8b',\n 'Testing & QA': '#00bcd4',\n 'Security & Auth': '#f44336',\n 'Distributed Systems': '#795548',\n 'Documentation': '#9e9e9e',\n};\n\nconst useStyles = makeStyles(theme => ({\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginBottom: theme.spacing(2),\n },\n searchRow: {\n display: 'flex',\n gap: theme.spacing(2),\n marginBottom: theme.spacing(2),\n alignItems: 'center',\n },\n searchField: {\n flex: 1,\n maxWidth: 400,\n },\n clickableRow: {\n cursor: 'pointer',\n '&:hover': {\n backgroundColor: theme.palette.action.hover,\n },\n },\n repoName: {\n fontFamily: 'monospace',\n fontWeight: 600,\n },\n pagination: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n padding: theme.spacing(1, 0),\n marginTop: theme.spacing(1),\n },\n categoryChip: {\n margin: theme.spacing(0.25),\n fontSize: '0.65rem',\n height: 18,\n },\n scoreBar: {\n height: 6,\n borderRadius: 3,\n marginTop: 4,\n },\n sectionTitle: {\n fontWeight: 600,\n marginBottom: theme.spacing(1),\n marginTop: theme.spacing(2),\n },\n statValue: {\n fontWeight: 700,\n fontSize: '1.5rem',\n },\n statLabel: {\n color: theme.palette.text.secondary,\n fontSize: '0.75rem',\n },\n statBox: {\n textAlign: 'center',\n padding: theme.spacing(1.5),\n },\n}));\n\ninterface RepositoriesContentProps {\n api: DevxpApi;\n onViewDeveloper?: (userId: string) => void;\n}\n\nexport const RepositoriesContent = ({ api, onViewDeveloper }: RepositoriesContentProps) => {\n const [view, setView] = useState<'list' | 'details'>('list');\n const [selectedRepo, setSelectedRepo] = useState('');\n\n return (\n <Box>\n {view === 'list' ? (\n <RepositoryList\n api={api}\n onViewRepo={repoName => {\n setSelectedRepo(repoName);\n setView('details');\n }}\n />\n ) : (\n <RepositoryDetailsView\n api={api}\n repoName={selectedRepo}\n onBack={() => setView('list')}\n onViewDeveloper={onViewDeveloper}\n />\n )}\n </Box>\n );\n};\n\n// ─── Repository List ──────────────────────────────────────────────────────────\n\ninterface RepositoryListProps {\n api: DevxpApi;\n onViewRepo: (repoName: string) => void;\n}\n\nconst RepositoryList = ({ api, onViewRepo }: RepositoryListProps) => {\n const classes = useStyles();\n\n const [repositories, setRepositories] = useState<RepositoryStats[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [totalPages, setTotalPages] = useState(1);\n const [page, setPage] = useState(1);\n const pageSize = 25;\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [searchInput, setSearchInput] = useState('');\n const [appliedSearch, setAppliedSearch] = useState('');\n\n const load = useCallback(\n async (pg: number, search: string) => {\n setLoading(true);\n setError(null);\n try {\n const data = await api.getRepositories({ page: pg, pageSize, searchQuery: search });\n setRepositories(data.repositories);\n setTotalCount(data.totalCount);\n setTotalPages(data.totalPages);\n } catch (e: any) {\n setError(e.message ?? 'Failed to load repositories');\n } finally {\n setLoading(false);\n }\n },\n [api],\n );\n\n useEffect(() => {\n load(page, appliedSearch);\n }, [load, page, appliedSearch]);\n\n const applySearch = () => {\n setPage(1);\n setAppliedSearch(searchInput);\n };\n\n const formatDate = (iso: string) => {\n try {\n return new Date(iso).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n } catch {\n return iso;\n }\n };\n\n return (\n <InfoCard title={`Repositories (${totalCount})`} noPadding={false}>\n <Box className={classes.searchRow}>\n <TextField\n className={classes.searchField}\n size=\"small\"\n variant=\"outlined\"\n label=\"Search repositories\"\n value={searchInput}\n onChange={e => setSearchInput(e.target.value)}\n onKeyDown={e => e.key === 'Enter' && applySearch()}\n InputProps={{\n endAdornment: (\n <IconButton size=\"small\" onClick={applySearch}>\n <SearchIcon fontSize=\"small\" />\n </IconButton>\n ),\n }}\n />\n </Box>\n\n {loading ? (\n <Box display=\"flex\" justifyContent=\"center\" py={4}>\n <CircularProgress />\n </Box>\n ) : error ? (\n <EmptyState missing=\"data\" title=\"Repositories unavailable\" description={error} />\n ) : repositories.length === 0 ? (\n <EmptyState missing=\"data\" title=\"No repositories found\" description=\"Try a different search.\" />\n ) : (\n <React.Fragment>\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Repository</TableCell>\n <TableCell align=\"right\">Developers</TableCell>\n <TableCell align=\"right\">Events</TableCell>\n <TableCell style={{ minWidth: 140 }}>Avg Proficiency</TableCell>\n <TableCell align=\"right\">Last Activity</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {repositories.map(repo => (\n <TableRow\n key={repo.repoName}\n className={classes.clickableRow}\n onClick={() => onViewRepo(repo.repoName)}\n >\n <TableCell>\n <Box display=\"flex\" alignItems=\"center\" style={{ gap: 8 }}>\n <StorageIcon fontSize=\"small\" color=\"action\" />\n <Typography variant=\"body2\" className={classes.repoName}>\n {repo.repoName}\n </Typography>\n </Box>\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"body2\">{repo.developerCount}</Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"body2\">{repo.eventCount.toLocaleString()}</Typography>\n </TableCell>\n <TableCell style={{ minWidth: 140 }}>\n <Typography variant=\"body2\" style={{ fontWeight: 600 }}>\n {(repo.avgProficiency * 10).toFixed(1)}%\n </Typography>\n <LinearProgress\n variant=\"determinate\"\n value={repo.avgProficiency * 10}\n className={classes.scoreBar}\n />\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"caption\" color=\"textSecondary\">\n {formatDate(repo.lastActivity)}\n </Typography>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n\n <Box className={classes.pagination}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Showing {(page - 1) * pageSize + 1}–{Math.min(page * pageSize, totalCount)} of {totalCount}\n </Typography>\n <Box display=\"flex\" alignItems=\"center\" style={{ gap: 8 }}>\n <Button size=\"small\" disabled={page <= 1} onClick={() => setPage(p => p - 1)}>\n Prev\n </Button>\n <Typography variant=\"caption\">\n {page} / {totalPages}\n </Typography>\n <Button size=\"small\" disabled={page >= totalPages} onClick={() => setPage(p => p + 1)}>\n Next\n </Button>\n </Box>\n </Box>\n </React.Fragment>\n )}\n </InfoCard>\n );\n};\n\n// ─── Repository Details ───────────────────────────────────────────────────────\n\ninterface RepositoryDetailsViewProps {\n api: DevxpApi;\n repoName: string;\n onBack: () => void;\n onViewDeveloper?: (userId: string) => void;\n}\n\nconst RepositoryDetailsView = ({ api, repoName, onBack, onViewDeveloper }: RepositoryDetailsViewProps) => {\n const classes = useStyles();\n\n const [details, setDetails] = useState<RepositoryDetails | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n setLoading(true);\n setError(null);\n api\n .getRepositoryDetails(repoName)\n .then(setDetails)\n .catch((e: any) => setError(e.message ?? 'Failed to load repository details'))\n .finally(() => setLoading(false));\n }, [api, repoName]);\n\n const formatDate = (iso: string) => {\n try {\n return new Date(iso).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n } catch {\n return iso;\n }\n };\n\n return (\n <Box>\n <Box className={classes.header}>\n <Tooltip title=\"Back to repositories\">\n <IconButton size=\"small\" onClick={onBack}>\n <ArrowBackIcon />\n </IconButton>\n </Tooltip>\n <StorageIcon color=\"action\" />\n <Typography variant=\"h6\" className={classes.repoName}>\n {repoName}\n </Typography>\n </Box>\n\n {loading ? (\n <Box display=\"flex\" justifyContent=\"center\" py={6}>\n <CircularProgress />\n </Box>\n ) : error ? (\n <EmptyState missing=\"data\" title=\"Details unavailable\" description={error} />\n ) : details ? (\n <React.Fragment>\n {/* Summary stats */}\n <InfoCard title=\"Overview\">\n <Box display=\"flex\" justifyContent=\"space-around\" flexWrap=\"wrap\">\n <Box className={classes.statBox}>\n <Typography className={classes.statValue}>{details.developerCount}</Typography>\n <Typography className={classes.statLabel}>Developers</Typography>\n </Box>\n <Box className={classes.statBox}>\n <Typography className={classes.statValue}>{details.eventCount.toLocaleString()}</Typography>\n <Typography className={classes.statLabel}>Skill Events</Typography>\n </Box>\n <Box className={classes.statBox}>\n <Typography className={classes.statValue}>\n {(details.avgProficiency * 10).toFixed(1)}%\n </Typography>\n <Typography className={classes.statLabel}>Avg Proficiency</Typography>\n </Box>\n <Box className={classes.statBox}>\n <Typography className={classes.statValue} style={{ fontSize: '1rem' }}>\n {formatDate(details.lastActivity)}\n </Typography>\n <Typography className={classes.statLabel}>Last Activity</Typography>\n </Box>\n </Box>\n </InfoCard>\n\n {/* Category breakdown */}\n {details.categoryBreakdown.length > 0 && (\n <Box mt={2}>\n <InfoCard title=\"Skill Categories\">\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Category</TableCell>\n <TableCell style={{ minWidth: 160 }}>Avg Proficiency</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {details.categoryBreakdown.map(cat => (\n <TableRow key={cat.category}>\n <TableCell>\n <Chip\n label={cat.category}\n size=\"small\"\n className={classes.categoryChip}\n style={{\n backgroundColor: `${CATEGORY_COLORS[cat.category] ?? '#9e9e9e'}20`,\n color: CATEGORY_COLORS[cat.category] ?? '#9e9e9e',\n height: 20,\n }}\n />\n </TableCell>\n <TableCell style={{ minWidth: 160 }}>\n <Typography variant=\"body2\" style={{ fontWeight: 600 }}>\n {(cat.averageProficiency * 10).toFixed(1)}%\n </Typography>\n <LinearProgress\n variant=\"determinate\"\n value={cat.averageProficiency * 10}\n className={classes.scoreBar}\n style={{ backgroundColor: `${CATEGORY_COLORS[cat.category] ?? '#9e9e9e'}30` }}\n />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </InfoCard>\n </Box>\n )}\n\n {/* Top developers */}\n {details.topDevelopers.length > 0 && (\n <Box mt={2}>\n <InfoCard title=\"Top Developers\">\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>#</TableCell>\n <TableCell>Developer</TableCell>\n <TableCell style={{ minWidth: 140 }}>Avg Proficiency</TableCell>\n <TableCell align=\"right\">Skills</TableCell>\n {onViewDeveloper && <TableCell align=\"right\" />}\n </TableRow>\n </TableHead>\n <TableBody>\n {details.topDevelopers.map((dev, idx) => (\n <TableRow\n key={dev.userId}\n className={onViewDeveloper ? classes.clickableRow : undefined}\n onClick={() => onViewDeveloper?.(dev.userId)}\n >\n <TableCell>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {idx + 1}\n </Typography>\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" style={{ fontFamily: 'monospace', fontWeight: 600 }}>\n {dev.userId}\n </Typography>\n </TableCell>\n <TableCell style={{ minWidth: 140 }}>\n <Typography variant=\"body2\" style={{ fontWeight: 600 }}>\n {(dev.averageProficiency * 10).toFixed(1)}%\n </Typography>\n <LinearProgress\n variant=\"determinate\"\n value={dev.averageProficiency * 10}\n className={classes.scoreBar}\n />\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"body2\">{dev.skillCount}</Typography>\n </TableCell>\n {onViewDeveloper && (\n <TableCell align=\"right\" onClick={e => e.stopPropagation()}>\n <Tooltip title=\"View developer details\">\n <IconButton size=\"small\" onClick={() => onViewDeveloper(dev.userId)}>\n <PersonIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </TableCell>\n )}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </InfoCard>\n </Box>\n )}\n </React.Fragment>\n ) : null}\n </Box>\n );\n};\n"],"names":["React"],"mappings":"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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
3
|
+
|
|
4
|
+
const CATEGORIES = [
|
|
5
|
+
"Frontend Engineering",
|
|
6
|
+
"Backend Systems",
|
|
7
|
+
"Database & Data",
|
|
8
|
+
"Infrastructure (DevOps)",
|
|
9
|
+
"Mobile Development",
|
|
10
|
+
"AI & Machine Learning",
|
|
11
|
+
"Testing & QA",
|
|
12
|
+
"Security & Auth",
|
|
13
|
+
"Distributed Systems",
|
|
14
|
+
"Documentation"
|
|
15
|
+
];
|
|
16
|
+
const SHORT_NAMES = {
|
|
17
|
+
"Frontend Engineering": "Frontend",
|
|
18
|
+
"Backend Systems": "Backend",
|
|
19
|
+
"Database & Data": "Database",
|
|
20
|
+
"Infrastructure (DevOps)": "DevOps",
|
|
21
|
+
"Mobile Development": "Mobile",
|
|
22
|
+
"AI & Machine Learning": "AI/ML",
|
|
23
|
+
"Testing & QA": "Testing",
|
|
24
|
+
"Security & Auth": "Security",
|
|
25
|
+
"Distributed Systems": "Distributed",
|
|
26
|
+
"Documentation": "Docs"
|
|
27
|
+
};
|
|
28
|
+
const CENTER_X = 180;
|
|
29
|
+
const CENTER_Y = 180;
|
|
30
|
+
const MAX_RADIUS = 130;
|
|
31
|
+
const LABEL_RADIUS = 155;
|
|
32
|
+
const NUM_LEVELS = 5;
|
|
33
|
+
const SVG_SIZE = 360;
|
|
34
|
+
function polarToCartesian(cx, cy, r, angleDeg) {
|
|
35
|
+
const rad = (angleDeg - 90) * Math.PI / 180;
|
|
36
|
+
return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
|
|
37
|
+
}
|
|
38
|
+
function buildPolygon(values, maxVal) {
|
|
39
|
+
const step = 360 / values.length;
|
|
40
|
+
const pts = values.map((v, i) => {
|
|
41
|
+
const r = Math.max(v, 0) / maxVal * MAX_RADIUS;
|
|
42
|
+
const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);
|
|
43
|
+
return `${p.x},${p.y}`;
|
|
44
|
+
});
|
|
45
|
+
return pts.join(" ");
|
|
46
|
+
}
|
|
47
|
+
function buildGridPolygon(level) {
|
|
48
|
+
const r = level / NUM_LEVELS * MAX_RADIUS;
|
|
49
|
+
const step = 360 / CATEGORIES.length;
|
|
50
|
+
return CATEGORIES.map((_, i) => {
|
|
51
|
+
const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);
|
|
52
|
+
return `${p.x},${p.y}`;
|
|
53
|
+
}).join(" ");
|
|
54
|
+
}
|
|
55
|
+
const useStyles = makeStyles((theme) => ({
|
|
56
|
+
svg: {
|
|
57
|
+
width: "100%",
|
|
58
|
+
maxWidth: SVG_SIZE,
|
|
59
|
+
display: "block",
|
|
60
|
+
margin: "0 auto"
|
|
61
|
+
},
|
|
62
|
+
gridPolygon: {
|
|
63
|
+
fill: "none",
|
|
64
|
+
stroke: theme.palette.divider,
|
|
65
|
+
strokeWidth: 1
|
|
66
|
+
},
|
|
67
|
+
radialLine: {
|
|
68
|
+
stroke: theme.palette.divider,
|
|
69
|
+
strokeWidth: 1
|
|
70
|
+
},
|
|
71
|
+
dataPolygon: {
|
|
72
|
+
fill: "rgba(63, 81, 181, 0.18)",
|
|
73
|
+
stroke: "#3f51b5",
|
|
74
|
+
strokeWidth: 2
|
|
75
|
+
},
|
|
76
|
+
comparePolygon: {
|
|
77
|
+
fill: "rgba(233, 30, 99, 0.12)",
|
|
78
|
+
stroke: "#e91e63",
|
|
79
|
+
strokeWidth: 2
|
|
80
|
+
},
|
|
81
|
+
dataPoint: {
|
|
82
|
+
fill: "#3f51b5"
|
|
83
|
+
},
|
|
84
|
+
comparePoint: {
|
|
85
|
+
fill: "#e91e63"
|
|
86
|
+
},
|
|
87
|
+
label: {
|
|
88
|
+
fontSize: 10,
|
|
89
|
+
fontWeight: 600,
|
|
90
|
+
fill: theme.palette.text.primary,
|
|
91
|
+
dominantBaseline: "middle"
|
|
92
|
+
}
|
|
93
|
+
}));
|
|
94
|
+
const SpiderChart = ({ primary, compare, maxValue = 10 }) => {
|
|
95
|
+
const classes = useStyles();
|
|
96
|
+
const primaryMap = new Map(primary.categoryAverages.map((c) => [c.category, c.averageProficiency]));
|
|
97
|
+
const compareMap = compare ? new Map(compare.categoryAverages.map((c) => [c.category, c.averageProficiency])) : null;
|
|
98
|
+
const primaryValues = CATEGORIES.map((cat) => Math.max(primaryMap.get(cat) ?? 0, 1));
|
|
99
|
+
const compareValues = compareMap ? CATEGORIES.map((cat) => Math.max(compareMap.get(cat) ?? 0, 1)) : null;
|
|
100
|
+
const step = 360 / CATEGORIES.length;
|
|
101
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
102
|
+
"svg",
|
|
103
|
+
{
|
|
104
|
+
viewBox: `0 0 ${SVG_SIZE} ${SVG_SIZE}`,
|
|
105
|
+
className: classes.svg,
|
|
106
|
+
"aria-label": "Skill radar chart"
|
|
107
|
+
},
|
|
108
|
+
Array.from({ length: NUM_LEVELS }, (_, i) => i + 1).map((level) => /* @__PURE__ */ React__default.createElement(
|
|
109
|
+
"polygon",
|
|
110
|
+
{
|
|
111
|
+
key: level,
|
|
112
|
+
points: buildGridPolygon(level),
|
|
113
|
+
className: classes.gridPolygon
|
|
114
|
+
}
|
|
115
|
+
)),
|
|
116
|
+
CATEGORIES.map((_, i) => {
|
|
117
|
+
const outer = polarToCartesian(CENTER_X, CENTER_Y, MAX_RADIUS, i * step);
|
|
118
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
119
|
+
"line",
|
|
120
|
+
{
|
|
121
|
+
key: i,
|
|
122
|
+
x1: CENTER_X,
|
|
123
|
+
y1: CENTER_Y,
|
|
124
|
+
x2: outer.x,
|
|
125
|
+
y2: outer.y,
|
|
126
|
+
className: classes.radialLine
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
}),
|
|
130
|
+
compareValues && /* @__PURE__ */ React__default.createElement(React__default.Fragment, null, /* @__PURE__ */ React__default.createElement(
|
|
131
|
+
"polygon",
|
|
132
|
+
{
|
|
133
|
+
points: buildPolygon(compareValues, maxValue),
|
|
134
|
+
className: classes.comparePolygon
|
|
135
|
+
}
|
|
136
|
+
), compareValues.map((v, i) => {
|
|
137
|
+
const r = Math.max(v, 0) / maxValue * MAX_RADIUS;
|
|
138
|
+
const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);
|
|
139
|
+
return /* @__PURE__ */ React__default.createElement("circle", { key: i, cx: p.x, cy: p.y, r: 3, className: classes.comparePoint });
|
|
140
|
+
})),
|
|
141
|
+
/* @__PURE__ */ React__default.createElement(
|
|
142
|
+
"polygon",
|
|
143
|
+
{
|
|
144
|
+
points: buildPolygon(primaryValues, maxValue),
|
|
145
|
+
className: classes.dataPolygon
|
|
146
|
+
}
|
|
147
|
+
),
|
|
148
|
+
primaryValues.map((v, i) => {
|
|
149
|
+
const r = Math.max(v, 0) / maxValue * MAX_RADIUS;
|
|
150
|
+
const p = polarToCartesian(CENTER_X, CENTER_Y, r, i * step);
|
|
151
|
+
return /* @__PURE__ */ React__default.createElement("circle", { key: i, cx: p.x, cy: p.y, r: 4, className: classes.dataPoint });
|
|
152
|
+
}),
|
|
153
|
+
CATEGORIES.map((cat, i) => {
|
|
154
|
+
const pos = polarToCartesian(CENTER_X, CENTER_Y, LABEL_RADIUS, i * step);
|
|
155
|
+
let anchor = "middle";
|
|
156
|
+
if (pos.x < CENTER_X - 10) anchor = "end";
|
|
157
|
+
else if (pos.x > CENTER_X + 10) anchor = "start";
|
|
158
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
159
|
+
"text",
|
|
160
|
+
{
|
|
161
|
+
key: cat,
|
|
162
|
+
x: pos.x,
|
|
163
|
+
y: pos.y,
|
|
164
|
+
textAnchor: anchor,
|
|
165
|
+
className: classes.label
|
|
166
|
+
},
|
|
167
|
+
SHORT_NAMES[cat] ?? cat
|
|
168
|
+
);
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export { SpiderChart };
|
|
174
|
+
//# sourceMappingURL=SpiderChart.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpiderChart.esm.js","sources":["../../src/components/SpiderChart.tsx"],"sourcesContent":["import React from 'react';\nimport { 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 <React.Fragment>\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 </React.Fragment>\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":["React"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karimov-labs/backstage-plugin-devxp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Backstage frontend plugin for developer intelligence — masked identity management, CSV upload, and unmask tooling.",
|
|
5
5
|
"main": "dist/index.esm.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|