@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 CHANGED
@@ -117,6 +117,75 @@ class DevxpClient {
117
117
  if (!response.ok) return { message: "Auto-sync trigger failed" };
118
118
  return response.json();
119
119
  }
120
+ async getDashboardStats() {
121
+ const url = `${await this.baseUrl()}/analytics/dashboard`;
122
+ const response = await this.fetchApi.fetch(url, { method: "POST" });
123
+ if (!response.ok) throw new Error(`Failed to fetch dashboard stats: ${response.statusText}`);
124
+ return response.json();
125
+ }
126
+ async getDeveloperLeaderboard(filters = {}) {
127
+ const url = `${await this.baseUrl()}/analytics/leaderboard`;
128
+ const body = {
129
+ page: filters.page ?? 1,
130
+ pageSize: filters.pageSize ?? 20,
131
+ categories: filters.categories ?? [],
132
+ repoNames: filters.repoNames ?? [],
133
+ sortBy: filters.sortBy ?? "avgProficiency",
134
+ sortOrder: filters.sortOrder ?? "desc",
135
+ searchQuery: filters.searchQuery ?? ""
136
+ };
137
+ const response = await this.fetchApi.fetch(url, {
138
+ method: "POST",
139
+ headers: { "Content-Type": "application/json" },
140
+ body: JSON.stringify(body)
141
+ });
142
+ if (!response.ok) throw new Error(`Failed to fetch leaderboard: ${response.statusText}`);
143
+ return response.json();
144
+ }
145
+ async getDeveloperPerformance(userId) {
146
+ const url = `${await this.baseUrl()}/analytics/developer/performance`;
147
+ const response = await this.fetchApi.fetch(url, {
148
+ method: "POST",
149
+ headers: { "Content-Type": "application/json" },
150
+ body: JSON.stringify({ userId })
151
+ });
152
+ if (!response.ok) throw new Error(`Failed to fetch developer performance: ${response.statusText}`);
153
+ return response.json();
154
+ }
155
+ async getDeveloperAveragePerformance(repoNames = []) {
156
+ const url = `${await this.baseUrl()}/analytics/developer/performance/average`;
157
+ const response = await this.fetchApi.fetch(url, {
158
+ method: "POST",
159
+ headers: { "Content-Type": "application/json" },
160
+ body: JSON.stringify({ repoNames })
161
+ });
162
+ if (!response.ok) throw new Error(`Failed to fetch average performance: ${response.statusText}`);
163
+ return response.json();
164
+ }
165
+ async getRepositories(filters = {}) {
166
+ const url = `${await this.baseUrl()}/analytics/repositories`;
167
+ const response = await this.fetchApi.fetch(url, {
168
+ method: "POST",
169
+ headers: { "Content-Type": "application/json" },
170
+ body: JSON.stringify({
171
+ page: filters.page ?? 1,
172
+ pageSize: filters.pageSize ?? 50,
173
+ searchQuery: filters.searchQuery ?? ""
174
+ })
175
+ });
176
+ if (!response.ok) throw new Error(`Failed to fetch repositories: ${response.statusText}`);
177
+ return response.json();
178
+ }
179
+ async getRepositoryDetails(repoName) {
180
+ const url = `${await this.baseUrl()}/analytics/repository/details`;
181
+ const response = await this.fetchApi.fetch(url, {
182
+ method: "POST",
183
+ headers: { "Content-Type": "application/json" },
184
+ body: JSON.stringify({ repoName })
185
+ });
186
+ if (!response.ok) throw new Error(`Failed to fetch repository details: ${response.statusText}`);
187
+ return response.json();
188
+ }
120
189
  }
121
190
 
122
191
  export { DevxpClient };
@@ -1 +1 @@
1
- {"version":3,"file":"api.esm.js","sources":["../src/api.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport type {\n DevxpConfig,\n DeveloperMapping,\n UnmaskResult,\n HashResult,\n UploadResult,\n GithubSyncConfig,\n GithubSyncResult,\n} from './types';\n\nexport interface DevxpApi {\n getConfig(): Promise<DevxpConfig>;\n getMappings(): Promise<{ mappings: DeveloperMapping[] }>;\n uploadCsv(csvContent: string): Promise<UploadResult>;\n unmask(maskedName: string): Promise<UnmaskResult>;\n hash(realName: string): Promise<HashResult>;\n deleteMapping(maskedName: string): Promise<{ message: string }>;\n // GitHub sync\n getGithubSyncConfigs(): Promise<{ configs: GithubSyncConfig[] }>;\n createGithubSyncConfig(orgName: string, githubHostname: string, appClientId: string, appPrivateKey: string): Promise<{ id: number; message: string }>;\n toggleGithubSyncConfig(id: number, active: boolean): Promise<{ message: string }>;\n deleteGithubSyncConfig(id: number): Promise<{ message: string }>;\n syncGithubConfig(id: number): Promise<GithubSyncResult>;\n triggerAutoSync(): Promise<{ message: string }>;\n}\n\nexport const devxpApiRef = createApiRef<DevxpApi>({\n id: 'plugin.devxp.api',\n});\n\nexport class DevxpClient implements DevxpApi {\n private readonly fetchApi: { fetch: typeof fetch };\n private readonly discoveryApi: { getBaseUrl: (pluginId: string) => Promise<string> };\n\n constructor(options: {\n fetchApi: { fetch: typeof fetch };\n discoveryApi: { getBaseUrl: (pluginId: string) => Promise<string> };\n }) {\n this.fetchApi = options.fetchApi;\n this.discoveryApi = options.discoveryApi;\n }\n\n private async baseUrl(): Promise<string> {\n return this.discoveryApi.getBaseUrl('devxp');\n }\n\n async getConfig(): Promise<DevxpConfig> {\n const url = `${await this.baseUrl()}/config`;\n const response = await this.fetchApi.fetch(url);\n if (!response.ok) throw new Error(`Failed to fetch config: ${response.statusText}`);\n return response.json();\n }\n\n async getMappings(): Promise<{ mappings: DeveloperMapping[] }> {\n const url = `${await this.baseUrl()}/mappings`;\n const response = await this.fetchApi.fetch(url);\n if (!response.ok) throw new Error(`Failed to fetch mappings: ${response.statusText}`);\n return response.json();\n }\n\n async uploadCsv(csvContent: string): Promise<UploadResult> {\n const url = `${await this.baseUrl()}/mappings/upload`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ csvContent }),\n });\n const data = await response.json();\n if (!response.ok) return { message: data.error || 'Upload failed', count: 0, error: data.error };\n return data;\n }\n\n async unmask(maskedName: string): Promise<UnmaskResult> {\n const url = `${await this.baseUrl()}/unmask`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ maskedName }),\n });\n if (!response.ok) throw new Error(`Failed to unmask: ${response.statusText}`);\n return response.json();\n }\n\n async hash(realName: string): Promise<HashResult> {\n const url = `${await this.baseUrl()}/hash`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ realName }),\n });\n if (!response.ok) throw new Error(`Failed to hash: ${response.statusText}`);\n return response.json();\n }\n\n async deleteMapping(maskedName: string): Promise<{ message: string }> {\n const url = `${await this.baseUrl()}/mappings/delete`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ maskedName }),\n });\n if (!response.ok) throw new Error(`Failed to delete mapping: ${response.statusText}`);\n return response.json();\n }\n\n // ─── GitHub Sync ─────────────────────────────────────────────────────────────\n\n async getGithubSyncConfigs(): Promise<{ configs: GithubSyncConfig[] }> {\n const url = `${await this.baseUrl()}/github-sync`;\n const response = await this.fetchApi.fetch(url);\n if (!response.ok) throw new Error(`Failed to fetch GitHub sync configs: ${response.statusText}`);\n return response.json();\n }\n\n async createGithubSyncConfig(\n orgName: string,\n githubHostname: string,\n appClientId: string,\n appPrivateKey: string,\n ): Promise<{ id: number; message: string }> {\n const url = `${await this.baseUrl()}/github-sync`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgName, githubHostname, appClientId, appPrivateKey }),\n });\n const data = await response.json();\n if (!response.ok) throw new Error(data.error || 'Failed to create GitHub sync config');\n return data;\n }\n\n async toggleGithubSyncConfig(id: number, active: boolean): Promise<{ message: string }> {\n const url = `${await this.baseUrl()}/github-sync/${id}/toggle`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ active }),\n });\n const data = await response.json();\n if (!response.ok) throw new Error(data.error || 'Failed to toggle GitHub sync config');\n return data;\n }\n\n async deleteGithubSyncConfig(id: number): Promise<{ message: string }> {\n const url = `${await this.baseUrl()}/github-sync/${id}`;\n const response = await this.fetchApi.fetch(url, {\n method: 'DELETE',\n });\n const data = await response.json();\n if (!response.ok) throw new Error(data.error || 'Failed to delete GitHub sync config');\n return data;\n }\n\n async syncGithubConfig(id: number): Promise<GithubSyncResult> {\n const url = `${await this.baseUrl()}/github-sync/${id}/sync`;\n const response = await this.fetchApi.fetch(url, { method: 'POST' });\n const data = await response.json();\n if (!response.ok) return { message: data.error || 'Sync failed', count: 0, orgName: '', error: data.error };\n return data;\n }\n\n async triggerAutoSync(): Promise<{ message: string }> {\n const url = `${await this.baseUrl()}/github-sync/auto`;\n const response = await this.fetchApi.fetch(url, { method: 'POST' });\n if (!response.ok) return { message: 'Auto-sync trigger failed' };\n return response.json();\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.esm.js","sources":["../src/api.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport type {\n DevxpConfig,\n DeveloperMapping,\n UnmaskResult,\n HashResult,\n UploadResult,\n GithubSyncConfig,\n GithubSyncResult,\n DashboardStats,\n DeveloperLeaderboard,\n LeaderboardFilters,\n DeveloperPerformance,\n DeveloperAveragePerformance,\n RepositoryList,\n RepositoryDetails,\n} from './types';\n\nexport interface DevxpApi {\n getConfig(): Promise<DevxpConfig>;\n getMappings(): Promise<{ mappings: DeveloperMapping[] }>;\n uploadCsv(csvContent: string): Promise<UploadResult>;\n unmask(maskedName: string): Promise<UnmaskResult>;\n hash(realName: string): Promise<HashResult>;\n deleteMapping(maskedName: string): Promise<{ message: string }>;\n // GitHub sync\n getGithubSyncConfigs(): Promise<{ configs: GithubSyncConfig[] }>;\n createGithubSyncConfig(orgName: string, githubHostname: string, appClientId: string, appPrivateKey: string): Promise<{ id: number; message: string }>;\n toggleGithubSyncConfig(id: number, active: boolean): Promise<{ message: string }>;\n deleteGithubSyncConfig(id: number): Promise<{ message: string }>;\n syncGithubConfig(id: number): Promise<GithubSyncResult>;\n triggerAutoSync(): Promise<{ message: string }>;\n // Analytics dashboard\n getDashboardStats(): Promise<DashboardStats>;\n getDeveloperLeaderboard(filters: Partial<LeaderboardFilters>): Promise<DeveloperLeaderboard>;\n getDeveloperPerformance(userId: string): Promise<DeveloperPerformance>;\n getDeveloperAveragePerformance(repoNames?: string[]): Promise<DeveloperAveragePerformance>;\n getRepositories(filters: { page?: number; pageSize?: number; searchQuery?: string }): Promise<RepositoryList>;\n getRepositoryDetails(repoName: string): Promise<RepositoryDetails>;\n}\n\nexport const devxpApiRef = createApiRef<DevxpApi>({\n id: 'plugin.devxp.api',\n});\n\nexport class DevxpClient implements DevxpApi {\n private readonly fetchApi: { fetch: typeof fetch };\n private readonly discoveryApi: { getBaseUrl: (pluginId: string) => Promise<string> };\n\n constructor(options: {\n fetchApi: { fetch: typeof fetch };\n discoveryApi: { getBaseUrl: (pluginId: string) => Promise<string> };\n }) {\n this.fetchApi = options.fetchApi;\n this.discoveryApi = options.discoveryApi;\n }\n\n private async baseUrl(): Promise<string> {\n return this.discoveryApi.getBaseUrl('devxp');\n }\n\n async getConfig(): Promise<DevxpConfig> {\n const url = `${await this.baseUrl()}/config`;\n const response = await this.fetchApi.fetch(url);\n if (!response.ok) throw new Error(`Failed to fetch config: ${response.statusText}`);\n return response.json();\n }\n\n async getMappings(): Promise<{ mappings: DeveloperMapping[] }> {\n const url = `${await this.baseUrl()}/mappings`;\n const response = await this.fetchApi.fetch(url);\n if (!response.ok) throw new Error(`Failed to fetch mappings: ${response.statusText}`);\n return response.json();\n }\n\n async uploadCsv(csvContent: string): Promise<UploadResult> {\n const url = `${await this.baseUrl()}/mappings/upload`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ csvContent }),\n });\n const data = await response.json();\n if (!response.ok) return { message: data.error || 'Upload failed', count: 0, error: data.error };\n return data;\n }\n\n async unmask(maskedName: string): Promise<UnmaskResult> {\n const url = `${await this.baseUrl()}/unmask`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ maskedName }),\n });\n if (!response.ok) throw new Error(`Failed to unmask: ${response.statusText}`);\n return response.json();\n }\n\n async hash(realName: string): Promise<HashResult> {\n const url = `${await this.baseUrl()}/hash`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ realName }),\n });\n if (!response.ok) throw new Error(`Failed to hash: ${response.statusText}`);\n return response.json();\n }\n\n async deleteMapping(maskedName: string): Promise<{ message: string }> {\n const url = `${await this.baseUrl()}/mappings/delete`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ maskedName }),\n });\n if (!response.ok) throw new Error(`Failed to delete mapping: ${response.statusText}`);\n return response.json();\n }\n\n // ─── GitHub Sync ─────────────────────────────────────────────────────────────\n\n async getGithubSyncConfigs(): Promise<{ configs: GithubSyncConfig[] }> {\n const url = `${await this.baseUrl()}/github-sync`;\n const response = await this.fetchApi.fetch(url);\n if (!response.ok) throw new Error(`Failed to fetch GitHub sync configs: ${response.statusText}`);\n return response.json();\n }\n\n async createGithubSyncConfig(\n orgName: string,\n githubHostname: string,\n appClientId: string,\n appPrivateKey: string,\n ): Promise<{ id: number; message: string }> {\n const url = `${await this.baseUrl()}/github-sync`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgName, githubHostname, appClientId, appPrivateKey }),\n });\n const data = await response.json();\n if (!response.ok) throw new Error(data.error || 'Failed to create GitHub sync config');\n return data;\n }\n\n async toggleGithubSyncConfig(id: number, active: boolean): Promise<{ message: string }> {\n const url = `${await this.baseUrl()}/github-sync/${id}/toggle`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ active }),\n });\n const data = await response.json();\n if (!response.ok) throw new Error(data.error || 'Failed to toggle GitHub sync config');\n return data;\n }\n\n async deleteGithubSyncConfig(id: number): Promise<{ message: string }> {\n const url = `${await this.baseUrl()}/github-sync/${id}`;\n const response = await this.fetchApi.fetch(url, {\n method: 'DELETE',\n });\n const data = await response.json();\n if (!response.ok) throw new Error(data.error || 'Failed to delete GitHub sync config');\n return data;\n }\n\n async syncGithubConfig(id: number): Promise<GithubSyncResult> {\n const url = `${await this.baseUrl()}/github-sync/${id}/sync`;\n const response = await this.fetchApi.fetch(url, { method: 'POST' });\n const data = await response.json();\n if (!response.ok) return { message: data.error || 'Sync failed', count: 0, orgName: '', error: data.error };\n return data;\n }\n\n async triggerAutoSync(): Promise<{ message: string }> {\n const url = `${await this.baseUrl()}/github-sync/auto`;\n const response = await this.fetchApi.fetch(url, { method: 'POST' });\n if (!response.ok) return { message: 'Auto-sync trigger failed' };\n return response.json();\n }\n\n async getDashboardStats(): Promise<DashboardStats> {\n const url = `${await this.baseUrl()}/analytics/dashboard`;\n const response = await this.fetchApi.fetch(url, { method: 'POST' });\n if (!response.ok) throw new Error(`Failed to fetch dashboard stats: ${response.statusText}`);\n return response.json();\n }\n\n async getDeveloperLeaderboard(filters: Partial<LeaderboardFilters> = {}): Promise<DeveloperLeaderboard> {\n const url = `${await this.baseUrl()}/analytics/leaderboard`;\n const body: Record<string, any> = {\n page: filters.page ?? 1,\n pageSize: filters.pageSize ?? 20,\n categories: filters.categories ?? [],\n repoNames: filters.repoNames ?? [],\n sortBy: filters.sortBy ?? 'avgProficiency',\n sortOrder: filters.sortOrder ?? 'desc',\n searchQuery: filters.searchQuery ?? '',\n };\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!response.ok) throw new Error(`Failed to fetch leaderboard: ${response.statusText}`);\n return response.json();\n }\n\n async getDeveloperPerformance(userId: string): Promise<DeveloperPerformance> {\n const url = `${await this.baseUrl()}/analytics/developer/performance`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ userId }),\n });\n if (!response.ok) throw new Error(`Failed to fetch developer performance: ${response.statusText}`);\n return response.json();\n }\n\n async getDeveloperAveragePerformance(repoNames: string[] = []): Promise<DeveloperAveragePerformance> {\n const url = `${await this.baseUrl()}/analytics/developer/performance/average`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ repoNames }),\n });\n if (!response.ok) throw new Error(`Failed to fetch average performance: ${response.statusText}`);\n return response.json();\n }\n\n async getRepositories(filters: { page?: number; pageSize?: number; searchQuery?: string } = {}): Promise<RepositoryList> {\n const url = `${await this.baseUrl()}/analytics/repositories`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n page: filters.page ?? 1,\n pageSize: filters.pageSize ?? 50,\n searchQuery: filters.searchQuery ?? '',\n }),\n });\n if (!response.ok) throw new Error(`Failed to fetch repositories: ${response.statusText}`);\n return response.json();\n }\n\n async getRepositoryDetails(repoName: string): Promise<RepositoryDetails> {\n const url = `${await this.baseUrl()}/analytics/repository/details`;\n const response = await this.fetchApi.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ repoName }),\n });\n if (!response.ok) throw new Error(`Failed to fetch repository details: ${response.statusText}`);\n return response.json();\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,249 @@
1
+ import React__default, { useState, useCallback, useEffect } from 'react';
2
+ import { Box, CircularProgress, Grid, Typography, LinearProgress, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Chip } from '@material-ui/core';
3
+ import { makeStyles } from '@material-ui/core/styles';
4
+ import TrendingUpIcon from '@material-ui/icons/TrendingUp';
5
+ import PeopleIcon from '@material-ui/icons/People';
6
+ import StorageIcon from '@material-ui/icons/Storage';
7
+ import BuildIcon from '@material-ui/icons/Build';
8
+ import { EmptyState, InfoCard } from '@backstage/core-components';
9
+
10
+ const useStyles = makeStyles((theme) => ({
11
+ statCard: {
12
+ display: "flex",
13
+ alignItems: "center",
14
+ gap: theme.spacing(2),
15
+ padding: theme.spacing(2.5)
16
+ },
17
+ statIcon: {
18
+ width: 48,
19
+ height: 48,
20
+ borderRadius: "50%",
21
+ display: "flex",
22
+ alignItems: "center",
23
+ justifyContent: "center",
24
+ flexShrink: 0
25
+ },
26
+ statValue: {
27
+ fontWeight: 700,
28
+ lineHeight: 1.1
29
+ },
30
+ statLabel: {
31
+ color: theme.palette.text.secondary,
32
+ marginTop: 2
33
+ },
34
+ categoryBar: {
35
+ height: 6,
36
+ borderRadius: 3,
37
+ backgroundColor: theme.palette.action.hover
38
+ },
39
+ categoryBarFill: {
40
+ borderRadius: 3,
41
+ transition: "width 0.6s ease"
42
+ },
43
+ proficiencyBar: {
44
+ height: 8,
45
+ borderRadius: 4,
46
+ marginTop: 4
47
+ },
48
+ skillChip: {
49
+ margin: theme.spacing(0.25),
50
+ fontSize: "0.7rem",
51
+ height: 22
52
+ },
53
+ timestampCell: {
54
+ fontSize: "0.75rem",
55
+ color: theme.palette.text.secondary,
56
+ whiteSpace: "nowrap"
57
+ },
58
+ notConfigured: {
59
+ padding: theme.spacing(3),
60
+ textAlign: "center"
61
+ },
62
+ categoryName: {
63
+ textTransform: "capitalize",
64
+ fontWeight: 500
65
+ },
66
+ timelineBar: {
67
+ display: "flex",
68
+ alignItems: "flex-end",
69
+ height: 60,
70
+ gap: 2,
71
+ overflow: "hidden"
72
+ },
73
+ timelineBarItem: {
74
+ flex: 1,
75
+ borderRadius: "2px 2px 0 0",
76
+ minWidth: 2,
77
+ backgroundColor: theme.palette.primary.main,
78
+ opacity: 0.7,
79
+ transition: "height 0.4s ease"
80
+ }
81
+ }));
82
+ const CATEGORY_COLORS = {
83
+ backend: "#3f51b5",
84
+ frontend: "#e91e63",
85
+ devops: "#ff9800",
86
+ mobile: "#4caf50",
87
+ data: "#9c27b0",
88
+ security: "#f44336",
89
+ testing: "#00bcd4",
90
+ architecture: "#795548",
91
+ ai: "#607d8b",
92
+ infrastructure: "#ff5722"
93
+ };
94
+ function categoryColor(category) {
95
+ return CATEGORY_COLORS[category.toLowerCase()] ?? "#9e9e9e";
96
+ }
97
+ function StatCard({ icon, iconBg, value, label }) {
98
+ const classes = useStyles();
99
+ return /* @__PURE__ */ React__default.createElement(Box, { className: classes.statCard }, /* @__PURE__ */ React__default.createElement(Box, { className: classes.statIcon, style: { backgroundColor: iconBg } }, icon), /* @__PURE__ */ React__default.createElement(Box, null, /* @__PURE__ */ React__default.createElement(Typography, { variant: "h4", className: classes.statValue }, value.toLocaleString()), /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", className: classes.statLabel }, label)));
100
+ }
101
+ const AnalyticsDashboard = ({ api }) => {
102
+ const classes = useStyles();
103
+ const [stats, setStats] = useState(null);
104
+ const [loading, setLoading] = useState(true);
105
+ const [error, setError] = useState(null);
106
+ const loadStats = useCallback(async () => {
107
+ setLoading(true);
108
+ setError(null);
109
+ try {
110
+ const data = await api.getDashboardStats();
111
+ setStats(data);
112
+ } catch (e) {
113
+ setError(e.message ?? "Failed to load analytics");
114
+ } finally {
115
+ setLoading(false);
116
+ }
117
+ }, [api]);
118
+ useEffect(() => {
119
+ loadStats();
120
+ }, [loadStats]);
121
+ if (loading) {
122
+ return /* @__PURE__ */ React__default.createElement(Box, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 200 }, /* @__PURE__ */ React__default.createElement(CircularProgress, null));
123
+ }
124
+ if (error) {
125
+ return /* @__PURE__ */ React__default.createElement(
126
+ EmptyState,
127
+ {
128
+ missing: "data",
129
+ title: "Analytics unavailable",
130
+ description: error.includes("not configured") ? "Configure devxp.apiEndpoint, devxp.apiToken, and devxp.projectId in app-config.yaml to enable the analytics dashboard." : error
131
+ }
132
+ );
133
+ }
134
+ if (!stats) return null;
135
+ const maxCategoryEvents = Math.max(...(stats.categoryBreakdown ?? []).map((c) => c.eventCount), 1);
136
+ const maxSkillUsage = Math.max(...(stats.topSkills ?? []).map((s) => s.usageCount), 1);
137
+ const maxTimelineCount = Math.max(...(stats.activityTimeline ?? []).map((t) => t.eventCount), 1);
138
+ return /* @__PURE__ */ React__default.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, sm: 6, md: 3 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "", noPadding: true }, /* @__PURE__ */ React__default.createElement(
139
+ StatCard,
140
+ {
141
+ icon: /* @__PURE__ */ React__default.createElement(PeopleIcon, { style: { color: "#fff" } }),
142
+ iconBg: "#3f51b5",
143
+ value: stats.totalUsers,
144
+ label: "Developers"
145
+ }
146
+ ))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, sm: 6, md: 3 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "", noPadding: true }, /* @__PURE__ */ React__default.createElement(
147
+ StatCard,
148
+ {
149
+ icon: /* @__PURE__ */ React__default.createElement(StorageIcon, { style: { color: "#fff" } }),
150
+ iconBg: "#009688",
151
+ value: stats.totalRepos,
152
+ label: "Repositories"
153
+ }
154
+ ))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, sm: 6, md: 3 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "", noPadding: true }, /* @__PURE__ */ React__default.createElement(
155
+ StatCard,
156
+ {
157
+ icon: /* @__PURE__ */ React__default.createElement(TrendingUpIcon, { style: { color: "#fff" } }),
158
+ iconBg: "#ff9800",
159
+ value: stats.totalEvents,
160
+ label: "Total Events"
161
+ }
162
+ ))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, sm: 6, md: 3 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "", noPadding: true }, /* @__PURE__ */ React__default.createElement(
163
+ StatCard,
164
+ {
165
+ icon: /* @__PURE__ */ React__default.createElement(BuildIcon, { style: { color: "#fff" } }),
166
+ iconBg: "#9c27b0",
167
+ value: stats.totalSkills,
168
+ label: "Unique Skills"
169
+ }
170
+ ))), stats.activityTimeline.length > 0 && /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, md: 8 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Activity (last 30 days)" }, /* @__PURE__ */ React__default.createElement(Box, null, /* @__PURE__ */ React__default.createElement(Box, { className: classes.timelineBar }, stats.activityTimeline.map((d) => /* @__PURE__ */ React__default.createElement(
171
+ Box,
172
+ {
173
+ key: d.date,
174
+ className: classes.timelineBarItem,
175
+ title: `${d.date}: ${d.eventCount} events`,
176
+ style: { height: `${Math.round(d.eventCount / maxTimelineCount * 100)}%` }
177
+ }
178
+ ))), /* @__PURE__ */ React__default.createElement(Box, { display: "flex", justifyContent: "space-between", mt: 0.5 }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption", color: "textSecondary" }, stats.activityTimeline[0]?.date), /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption", color: "textSecondary" }, stats.activityTimeline[stats.activityTimeline.length - 1]?.date))))), stats.categoryBreakdown.length > 0 && /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, md: stats.activityTimeline.length > 0 ? 4 : 6 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Skill Categories" }, /* @__PURE__ */ React__default.createElement(Box, null, stats.categoryBreakdown.slice(0, 8).map((cat) => /* @__PURE__ */ React__default.createElement(Box, { key: cat.category, mb: 1.5 }, /* @__PURE__ */ React__default.createElement(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 0.5 }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", className: classes.categoryName }, cat.category), /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption", color: "textSecondary" }, cat.eventCount.toLocaleString(), " events")), /* @__PURE__ */ React__default.createElement(
179
+ LinearProgress,
180
+ {
181
+ variant: "determinate",
182
+ value: cat.eventCount / maxCategoryEvents * 100,
183
+ className: classes.proficiencyBar,
184
+ style: { color: categoryColor(cat.category) }
185
+ }
186
+ )))))), stats.topSkills.length > 0 && /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Top Skills" }, /* @__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, "Skill"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Category"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Usage"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Avg score"))), /* @__PURE__ */ React__default.createElement(TableBody, null, stats.topSkills.slice(0, 10).map((skill) => /* @__PURE__ */ React__default.createElement(TableRow, { key: `${skill.skillName}-${skill.category}` }, /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", style: { fontWeight: 500 } }, skill.skillName), /* @__PURE__ */ React__default.createElement(
187
+ LinearProgress,
188
+ {
189
+ variant: "determinate",
190
+ value: skill.usageCount / maxSkillUsage * 100,
191
+ className: classes.proficiencyBar,
192
+ style: { color: categoryColor(skill.category) }
193
+ }
194
+ )), /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(
195
+ Chip,
196
+ {
197
+ label: skill.category,
198
+ size: "small",
199
+ className: classes.skillChip,
200
+ style: {
201
+ backgroundColor: `${categoryColor(skill.category)}20`,
202
+ color: categoryColor(skill.category)
203
+ }
204
+ }
205
+ )), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption" }, skill.usageCount.toLocaleString())), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption" }, (skill.avgProficiency * 100).toFixed(0), "%"))))))))), stats.recentActivity.length > 0 && /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, md: stats.topSkills.length > 0 ? 6 : 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Recent Activity" }, /* @__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, "Developer"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Skill"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Repository"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Score"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Time"))), /* @__PURE__ */ React__default.createElement(TableBody, null, stats.recentActivity.map((activity, idx) => /* @__PURE__ */ React__default.createElement(TableRow, { key: idx }, /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(
206
+ Typography,
207
+ {
208
+ variant: "caption",
209
+ style: { fontFamily: "monospace" }
210
+ },
211
+ activity.userId.slice(0, 12),
212
+ "\u2026"
213
+ )), /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(Box, null, /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption", style: { fontWeight: 500 } }, activity.skillName), /* @__PURE__ */ React__default.createElement(
214
+ Chip,
215
+ {
216
+ label: activity.category,
217
+ size: "small",
218
+ className: classes.skillChip,
219
+ style: {
220
+ backgroundColor: `${categoryColor(activity.category)}20`,
221
+ color: categoryColor(activity.category),
222
+ marginLeft: 4
223
+ }
224
+ }
225
+ ))), /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(
226
+ Typography,
227
+ {
228
+ variant: "caption",
229
+ color: "textSecondary",
230
+ style: { fontFamily: "monospace" }
231
+ },
232
+ activity.repoName
233
+ )), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "caption" }, (activity.proficiencyScore * 100).toFixed(0), "%")), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.timestampCell }, new Date(activity.timestamp).toLocaleString(void 0, {
234
+ month: "short",
235
+ day: "numeric",
236
+ hour: "2-digit",
237
+ minute: "2-digit"
238
+ })))))))))), stats.totalEvents === 0 && /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(
239
+ EmptyState,
240
+ {
241
+ missing: "data",
242
+ title: "No analytics data yet",
243
+ description: "Analytics data will appear here once developers start committing code and dev-xp-analyzer processes their contributions."
244
+ }
245
+ )));
246
+ };
247
+
248
+ export { AnalyticsDashboard };
249
+ //# sourceMappingURL=AnalyticsDashboard.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyticsDashboard.esm.js","sources":["../../src/components/AnalyticsDashboard.tsx"],"sourcesContent":["import React, { useState, useEffect, useCallback } from 'react';\nimport {\n Typography,\n Grid,\n Box,\n Chip,\n LinearProgress,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n CircularProgress,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport TrendingUpIcon from '@material-ui/icons/TrendingUp';\nimport PeopleIcon from '@material-ui/icons/People';\nimport StorageIcon from '@material-ui/icons/Storage';\nimport BuildIcon from '@material-ui/icons/Build';\nimport { InfoCard, EmptyState } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { DashboardStats, CategoryData, TopSkillData, RecentActivityData } from '../types';\n\nconst useStyles = makeStyles(theme => ({\n statCard: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(2),\n padding: theme.spacing(2.5),\n },\n statIcon: {\n width: 48,\n height: 48,\n borderRadius: '50%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n },\n statValue: {\n fontWeight: 700,\n lineHeight: 1.1,\n },\n statLabel: {\n color: theme.palette.text.secondary,\n marginTop: 2,\n },\n categoryBar: {\n height: 6,\n borderRadius: 3,\n backgroundColor: theme.palette.action.hover,\n },\n categoryBarFill: {\n borderRadius: 3,\n transition: 'width 0.6s ease',\n },\n proficiencyBar: {\n height: 8,\n borderRadius: 4,\n marginTop: 4,\n },\n skillChip: {\n margin: theme.spacing(0.25),\n fontSize: '0.7rem',\n height: 22,\n },\n timestampCell: {\n fontSize: '0.75rem',\n color: theme.palette.text.secondary,\n whiteSpace: 'nowrap',\n },\n notConfigured: {\n padding: theme.spacing(3),\n textAlign: 'center',\n },\n categoryName: {\n textTransform: 'capitalize',\n fontWeight: 500,\n },\n timelineBar: {\n display: 'flex',\n alignItems: 'flex-end',\n height: 60,\n gap: 2,\n overflow: 'hidden',\n },\n timelineBarItem: {\n flex: 1,\n borderRadius: '2px 2px 0 0',\n minWidth: 2,\n backgroundColor: theme.palette.primary.main,\n opacity: 0.7,\n transition: 'height 0.4s ease',\n },\n}));\n\nconst CATEGORY_COLORS: Record<string, string> = {\n backend: '#3f51b5',\n frontend: '#e91e63',\n devops: '#ff9800',\n mobile: '#4caf50',\n data: '#9c27b0',\n security: '#f44336',\n testing: '#00bcd4',\n architecture: '#795548',\n ai: '#607d8b',\n infrastructure: '#ff5722',\n};\n\nfunction categoryColor(category: string): string {\n return CATEGORY_COLORS[category.toLowerCase()] ?? '#9e9e9e';\n}\n\ninterface StatCardProps {\n icon: React.ReactNode;\n iconBg: string;\n value: number;\n label: string;\n}\n\nfunction StatCard({ icon, iconBg, value, label }: StatCardProps) {\n const classes = useStyles();\n return (\n <Box className={classes.statCard}>\n <Box className={classes.statIcon} style={{ backgroundColor: iconBg }}>\n {icon}\n </Box>\n <Box>\n <Typography variant=\"h4\" className={classes.statValue}>\n {value.toLocaleString()}\n </Typography>\n <Typography variant=\"body2\" className={classes.statLabel}>\n {label}\n </Typography>\n </Box>\n </Box>\n );\n}\n\ninterface AnalyticsDashboardProps {\n api: DevxpApi;\n}\n\nexport const AnalyticsDashboard = ({ api }: AnalyticsDashboardProps) => {\n const classes = useStyles();\n const [stats, setStats] = useState<DashboardStats | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const loadStats = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const data = await api.getDashboardStats();\n setStats(data);\n } catch (e: any) {\n setError(e.message ?? 'Failed to load analytics');\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n useEffect(() => {\n loadStats();\n }, [loadStats]);\n\n if (loading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight={200}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error) {\n return (\n <EmptyState\n missing=\"data\"\n title=\"Analytics unavailable\"\n description={error.includes('not configured')\n ? 'Configure devxp.apiEndpoint, devxp.apiToken, and devxp.projectId in app-config.yaml to enable the analytics dashboard.'\n : error}\n />\n );\n }\n\n if (!stats) return null;\n\n const maxCategoryEvents = Math.max(...(stats.categoryBreakdown ?? []).map(c => c.eventCount), 1);\n const maxSkillUsage = Math.max(...(stats.topSkills ?? []).map(s => s.usageCount), 1);\n const maxTimelineCount = Math.max(...(stats.activityTimeline ?? []).map(t => t.eventCount), 1);\n\n return (\n <Grid container spacing={3}>\n\n {/* ── Summary stats ──────────────────────────────────────────────── */}\n <Grid item xs={12} sm={6} md={3}>\n <InfoCard title=\"\" noPadding>\n <StatCard\n icon={<PeopleIcon style={{ color: '#fff' }} />}\n iconBg=\"#3f51b5\"\n value={stats.totalUsers}\n label=\"Developers\"\n />\n </InfoCard>\n </Grid>\n <Grid item xs={12} sm={6} md={3}>\n <InfoCard title=\"\" noPadding>\n <StatCard\n icon={<StorageIcon style={{ color: '#fff' }} />}\n iconBg=\"#009688\"\n value={stats.totalRepos}\n label=\"Repositories\"\n />\n </InfoCard>\n </Grid>\n <Grid item xs={12} sm={6} md={3}>\n <InfoCard title=\"\" noPadding>\n <StatCard\n icon={<TrendingUpIcon style={{ color: '#fff' }} />}\n iconBg=\"#ff9800\"\n value={stats.totalEvents}\n label=\"Total Events\"\n />\n </InfoCard>\n </Grid>\n <Grid item xs={12} sm={6} md={3}>\n <InfoCard title=\"\" noPadding>\n <StatCard\n icon={<BuildIcon style={{ color: '#fff' }} />}\n iconBg=\"#9c27b0\"\n value={stats.totalSkills}\n label=\"Unique Skills\"\n />\n </InfoCard>\n </Grid>\n\n {/* ── Activity timeline ──────────────────────────────────────────── */}\n {stats.activityTimeline.length > 0 && (\n <Grid item xs={12} md={8}>\n <InfoCard title=\"Activity (last 30 days)\">\n <Box>\n <Box className={classes.timelineBar}>\n {stats.activityTimeline.map(d => (\n <Box\n key={d.date}\n className={classes.timelineBarItem}\n title={`${d.date}: ${d.eventCount} events`}\n style={{ height: `${Math.round((d.eventCount / maxTimelineCount) * 100)}%` }}\n />\n ))}\n </Box>\n <Box display=\"flex\" justifyContent=\"space-between\" mt={0.5}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {stats.activityTimeline[0]?.date}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {stats.activityTimeline[stats.activityTimeline.length - 1]?.date}\n </Typography>\n </Box>\n </Box>\n </InfoCard>\n </Grid>\n )}\n\n {/* ── Category breakdown ─────────────────────────────────────────── */}\n {stats.categoryBreakdown.length > 0 && (\n <Grid item xs={12} md={stats.activityTimeline.length > 0 ? 4 : 6}>\n <InfoCard title=\"Skill Categories\">\n <Box>\n {stats.categoryBreakdown.slice(0, 8).map((cat: CategoryData) => (\n <Box key={cat.category} mb={1.5}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={0.5}>\n <Typography variant=\"body2\" className={classes.categoryName}>\n {cat.category}\n </Typography>\n <Typography variant=\"caption\" color=\"textSecondary\">\n {cat.eventCount.toLocaleString()} events\n </Typography>\n </Box>\n <LinearProgress\n variant=\"determinate\"\n value={(cat.eventCount / maxCategoryEvents) * 100}\n className={classes.proficiencyBar}\n style={{ color: categoryColor(cat.category) }}\n />\n </Box>\n ))}\n </Box>\n </InfoCard>\n </Grid>\n )}\n\n {/* ── Top skills ─────────────────────────────────────────────────── */}\n {stats.topSkills.length > 0 && (\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Top Skills\">\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Skill</TableCell>\n <TableCell>Category</TableCell>\n <TableCell align=\"right\">Usage</TableCell>\n <TableCell align=\"right\">Avg score</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {stats.topSkills.slice(0, 10).map((skill: TopSkillData) => (\n <TableRow key={`${skill.skillName}-${skill.category}`}>\n <TableCell>\n <Typography variant=\"body2\" style={{ fontWeight: 500 }}>\n {skill.skillName}\n </Typography>\n <LinearProgress\n variant=\"determinate\"\n value={(skill.usageCount / maxSkillUsage) * 100}\n className={classes.proficiencyBar}\n style={{ color: categoryColor(skill.category) }}\n />\n </TableCell>\n <TableCell>\n <Chip\n label={skill.category}\n size=\"small\"\n className={classes.skillChip}\n style={{\n backgroundColor: `${categoryColor(skill.category)}20`,\n color: categoryColor(skill.category),\n }}\n />\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"caption\">{skill.usageCount.toLocaleString()}</Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"caption\">\n {(skill.avgProficiency * 100).toFixed(0)}%\n </Typography>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </InfoCard>\n </Grid>\n )}\n\n {/* ── Recent activity ────────────────────────────────────────────── */}\n {stats.recentActivity.length > 0 && (\n <Grid item xs={12} md={stats.topSkills.length > 0 ? 6 : 12}>\n <InfoCard title=\"Recent Activity\">\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Developer</TableCell>\n <TableCell>Skill</TableCell>\n <TableCell>Repository</TableCell>\n <TableCell align=\"right\">Score</TableCell>\n <TableCell align=\"right\">Time</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {stats.recentActivity.map((activity: RecentActivityData, idx: number) => (\n <TableRow key={idx}>\n <TableCell>\n <Typography\n variant=\"caption\"\n style={{ fontFamily: 'monospace' }}\n >\n {activity.userId.slice(0, 12)}…\n </Typography>\n </TableCell>\n <TableCell>\n <Box>\n <Typography variant=\"caption\" style={{ fontWeight: 500 }}>\n {activity.skillName}\n </Typography>\n <Chip\n label={activity.category}\n size=\"small\"\n className={classes.skillChip}\n style={{\n backgroundColor: `${categoryColor(activity.category)}20`,\n color: categoryColor(activity.category),\n marginLeft: 4,\n }}\n />\n </Box>\n </TableCell>\n <TableCell>\n <Typography\n variant=\"caption\"\n color=\"textSecondary\"\n style={{ fontFamily: 'monospace' }}\n >\n {activity.repoName}\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"caption\">\n {(activity.proficiencyScore * 100).toFixed(0)}%\n </Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Typography className={classes.timestampCell}>\n {new Date(activity.timestamp).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })}\n </Typography>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </InfoCard>\n </Grid>\n )}\n\n {/* Empty state when there's no data at all */}\n {stats.totalEvents === 0 && (\n <Grid item xs={12}>\n <EmptyState\n missing=\"data\"\n title=\"No analytics data yet\"\n description=\"Analytics data will appear here once developers start committing code and dev-xp-analyzer processes their contributions.\"\n />\n </Grid>\n )}\n </Grid>\n );\n};\n"],"names":["React"],"mappings":"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"}