@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
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Built for use with the [DevXP](https://devxp.net) developer analytics platform.
|
|
|
32
32
|
| Dependency | Version |
|
|
33
33
|
|---|---|
|
|
34
34
|
| Backstage | >= 1.30 |
|
|
35
|
-
| `@karimov-labs/backstage-plugin-devxp-backend` | `^1.
|
|
35
|
+
| `@karimov-labs/backstage-plugin-devxp-backend` | `^1.1.0` |
|
|
36
36
|
| React | `^18` |
|
|
37
37
|
|
|
38
38
|
---
|
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 };
|
package/dist/api.esm.js.map
CHANGED
|
@@ -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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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React__default, { useState, useCallback, useEffect } from 'react';
|
|
2
2
|
import { Typography, Grid, Box, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Chip, TextField, Button } from '@material-ui/core';
|
|
3
3
|
import { makeStyles } from '@material-ui/core/styles';
|
|
4
4
|
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
|
@@ -94,44 +94,44 @@ const DashboardContent = ({ api }) => {
|
|
|
94
94
|
if (e.key === "Enter") handleUnmask();
|
|
95
95
|
};
|
|
96
96
|
if (loading) {
|
|
97
|
-
return /* @__PURE__ */
|
|
97
|
+
return /* @__PURE__ */ React__default.createElement(Typography, null, "Loading configuration...");
|
|
98
98
|
}
|
|
99
|
-
return /* @__PURE__ */
|
|
99
|
+
return /* @__PURE__ */ React__default.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Configuration Status" }, config ? /* @__PURE__ */ React__default.createElement(Box, null, /* @__PURE__ */ React__default.createElement(
|
|
100
100
|
ConfigItem,
|
|
101
101
|
{
|
|
102
102
|
label: "Mode",
|
|
103
103
|
value: config.masked ? "Masked" : "Unmasked",
|
|
104
104
|
ok: true
|
|
105
105
|
}
|
|
106
|
-
), /* @__PURE__ */
|
|
106
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
107
107
|
ConfigItem,
|
|
108
108
|
{
|
|
109
109
|
label: "Salt",
|
|
110
110
|
value: config.saltConfigured ? "Configured" : "Not configured",
|
|
111
111
|
ok: config.saltConfigured
|
|
112
112
|
}
|
|
113
|
-
), /* @__PURE__ */
|
|
113
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
114
114
|
ConfigItem,
|
|
115
115
|
{
|
|
116
116
|
label: "API Endpoint",
|
|
117
117
|
value: config.apiEndpointConfigured ? "Configured" : "Not configured",
|
|
118
118
|
ok: config.apiEndpointConfigured
|
|
119
119
|
}
|
|
120
|
-
), /* @__PURE__ */
|
|
120
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
121
121
|
ConfigItem,
|
|
122
122
|
{
|
|
123
123
|
label: "API Token",
|
|
124
124
|
value: config.apiTokenConfigured ? "Configured" : "Not configured",
|
|
125
125
|
ok: config.apiTokenConfigured
|
|
126
126
|
}
|
|
127
|
-
), /* @__PURE__ */
|
|
127
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
128
128
|
ConfigItem,
|
|
129
129
|
{
|
|
130
130
|
label: "Project ID",
|
|
131
131
|
value: config.projectIdConfigured ? "Configured" : "Not configured",
|
|
132
132
|
ok: config.projectIdConfigured
|
|
133
133
|
}
|
|
134
|
-
)) : /* @__PURE__ */
|
|
134
|
+
)) : /* @__PURE__ */ React__default.createElement(Typography, { color: "error" }, "Unable to load configuration. Is the backend plugin installed?"))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Developer Mappings" }, /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "h3" }, config?.mappingCount ?? 0), /* @__PURE__ */ React__default.createElement(
|
|
135
135
|
Typography,
|
|
136
136
|
{
|
|
137
137
|
variant: "body1",
|
|
@@ -139,14 +139,14 @@ const DashboardContent = ({ api }) => {
|
|
|
139
139
|
color: "textSecondary"
|
|
140
140
|
},
|
|
141
141
|
"developer name mappings stored"
|
|
142
|
-
)), /* @__PURE__ */
|
|
142
|
+
)), /* @__PURE__ */ React__default.createElement(Box, { mt: 2 }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Upload developer names via CSV or configure GitHub auto-sync in the Settings tab to populate the mapping database.")))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "GitHub Auto-Sync Configurations" }, syncConfigsLoading ? /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Loading sync configurations...") : syncConfigs.length === 0 ? /* @__PURE__ */ React__default.createElement(Typography, { className: classes.emptySync, variant: "body2" }, "No GitHub sync configurations. Go to Settings to register a GitHub App for automatic member syncing.") : /* @__PURE__ */ React__default.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React__default.createElement(Table, { size: "small" }, /* @__PURE__ */ React__default.createElement(TableHead, null, /* @__PURE__ */ React__default.createElement(TableRow, null, /* @__PURE__ */ React__default.createElement(TableCell, null, "Organization"), /* @__PURE__ */ React__default.createElement(TableCell, null, "GitHub Host"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Client ID"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Status"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Last Synced"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Registered"))), /* @__PURE__ */ React__default.createElement(TableBody, null, syncConfigs.map((cfg) => /* @__PURE__ */ React__default.createElement(TableRow, { key: cfg.id }, /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement("strong", null, cfg.org_name)), /* @__PURE__ */ React__default.createElement(TableCell, { style: { fontSize: "0.85em" } }, cfg.github_hostname ?? "github.com"), /* @__PURE__ */ React__default.createElement(TableCell, { style: { fontFamily: "monospace", fontSize: "0.85em" } }, cfg.app_client_id), /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(
|
|
143
143
|
Chip,
|
|
144
144
|
{
|
|
145
145
|
label: cfg.active ? "Active" : "Inactive",
|
|
146
146
|
size: "small",
|
|
147
147
|
className: cfg.active ? classes.activeChip : classes.inactiveChip
|
|
148
148
|
}
|
|
149
|
-
)), /* @__PURE__ */
|
|
149
|
+
)), /* @__PURE__ */ React__default.createElement(TableCell, null, cfg.last_synced_at ? /* @__PURE__ */ React__default.createElement(Typography, { className: classes.lastSynced }, new Date(cfg.last_synced_at).toLocaleString()) : /* @__PURE__ */ React__default.createElement(Typography, { className: classes.lastSynced }, "Never")), /* @__PURE__ */ React__default.createElement(TableCell, null, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.lastSynced }, new Date(cfg.created_at).toLocaleDateString()))))))))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Unmask Developer Name" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, "Enter a masked developer username (16-character hex hash) to look up the original name from the mapping database."), /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "flex-start" }, /* @__PURE__ */ React__default.createElement(
|
|
150
150
|
TextField,
|
|
151
151
|
{
|
|
152
152
|
label: "Masked username",
|
|
@@ -159,7 +159,7 @@ const DashboardContent = ({ api }) => {
|
|
|
159
159
|
style: { minWidth: 300 },
|
|
160
160
|
inputProps: { maxLength: 16 }
|
|
161
161
|
}
|
|
162
|
-
), /* @__PURE__ */
|
|
162
|
+
), /* @__PURE__ */ React__default.createElement(
|
|
163
163
|
Button,
|
|
164
164
|
{
|
|
165
165
|
variant: "contained",
|
|
@@ -169,28 +169,28 @@ const DashboardContent = ({ api }) => {
|
|
|
169
169
|
style: { marginLeft: 12, height: 40 }
|
|
170
170
|
},
|
|
171
171
|
"Unmask"
|
|
172
|
-
)), unmaskResult && /* @__PURE__ */
|
|
172
|
+
)), unmaskResult && /* @__PURE__ */ React__default.createElement(Box, { className: classes.unmaskResult }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.resultLabel, variant: "body2" }, "Masked:"), /* @__PURE__ */ React__default.createElement(Typography, { className: classes.resultValue }, unmaskResult.maskedName), /* @__PURE__ */ React__default.createElement(Box, { mt: 1 }, /* @__PURE__ */ React__default.createElement(Typography, { className: classes.resultLabel, variant: "body2" }, "Real Name:"), /* @__PURE__ */ React__default.createElement(Typography, { className: classes.resultValue }, unmaskResult.realName ? /* @__PURE__ */ React__default.createElement(
|
|
173
173
|
Chip,
|
|
174
174
|
{
|
|
175
175
|
label: unmaskResult.realName,
|
|
176
176
|
color: "primary",
|
|
177
177
|
variant: "outlined"
|
|
178
178
|
}
|
|
179
|
-
) : /* @__PURE__ */
|
|
179
|
+
) : /* @__PURE__ */ React__default.createElement(
|
|
180
180
|
Chip,
|
|
181
181
|
{
|
|
182
182
|
label: "No mapping found",
|
|
183
183
|
color: "default",
|
|
184
184
|
variant: "outlined"
|
|
185
185
|
}
|
|
186
|
-
)))), unmaskError && /* @__PURE__ */
|
|
186
|
+
)))), unmaskError && /* @__PURE__ */ React__default.createElement(Box, { mt: 2 }, /* @__PURE__ */ React__default.createElement(Typography, { color: "error" }, unmaskError)))));
|
|
187
187
|
};
|
|
188
188
|
function ConfigItem({
|
|
189
189
|
label,
|
|
190
190
|
value,
|
|
191
191
|
ok
|
|
192
192
|
}) {
|
|
193
|
-
return /* @__PURE__ */
|
|
193
|
+
return /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "center", mb: 1 }, ok ? /* @__PURE__ */ React__default.createElement(CheckCircleIcon, { style: { color: "#4caf50", marginRight: 8 }, fontSize: "small" }) : /* @__PURE__ */ React__default.createElement(ErrorIcon, { style: { color: "#f44336", marginRight: 8 }, fontSize: "small" }), /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React__default.createElement("strong", null, label, ":"), " ", value));
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
export { DashboardContent };
|