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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"GithubSyncSection.esm.js","sources":["../../src/components/GithubSyncSection.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\nimport {\n Typography,\n Button,\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n IconButton,\n TextField,\n Chip,\n Tooltip,\n Collapse,\n Link,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport SyncIcon from '@material-ui/icons/Sync';\nimport AddIcon from '@material-ui/icons/Add';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport ExpandLessIcon from '@material-ui/icons/ExpandLess';\nimport { InfoCard } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { GithubSyncConfig } from '../types';\n\nconst useStyles = makeStyles(theme => ({\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(2),\n marginTop: theme.spacing(2),\n },\n formRow: {\n display: 'flex',\n gap: theme.spacing(2),\n alignItems: 'flex-start',\n flexWrap: 'wrap',\n },\n textField: {\n flex: 1,\n minWidth: 200,\n },\n privateKeyField: {\n width: '100%',\n },\n statusMessage: {\n marginTop: theme.spacing(2),\n padding: theme.spacing(1.5),\n borderRadius: theme.shape.borderRadius,\n },\n success: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n error: {\n backgroundColor: '#ffebee',\n color: '#c62828',\n },\n activeChip: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n inactiveChip: {\n backgroundColor: '#f5f5f5',\n color: '#757575',\n },\n guideBox: {\n backgroundColor: theme.palette.background.default,\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: theme.shape.borderRadius,\n padding: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n guideHeader: {\n display: 'flex',\n alignItems: 'center',\n cursor: 'pointer',\n userSelect: 'none',\n },\n syncingRow: {\n opacity: 0.6,\n },\n lastSynced: {\n fontSize: '0.75rem',\n color: theme.palette.text.secondary,\n },\n emptyState: {\n textAlign: 'center',\n padding: theme.spacing(3),\n color: theme.palette.text.secondary,\n },\n}));\n\ninterface GithubSyncSectionProps {\n api: DevxpApi;\n onSyncComplete?: () => void;\n}\n\nexport const GithubSyncSection = ({ api, onSyncComplete }: GithubSyncSectionProps) => {\n const classes = useStyles();\n const [configs, setConfigs] = useState<GithubSyncConfig[]>([]);\n const [loading, setLoading] = useState(true);\n const [showForm, setShowForm] = useState(false);\n const [showGuide, setShowGuide] = useState(false);\n const [syncingIds, setSyncingIds] = useState<Set<number>>(new Set());\n const [statusMessage, setStatusMessage] = useState<{ text: string; isError: boolean } | null>(null);\n\n // Form state\n const [orgName, setOrgName] = useState('');\n const [githubHostname, setGithubHostname] = useState('github.com');\n const [appClientId, setAppClientId] = useState('');\n const [appPrivateKey, setAppPrivateKey] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n const loadConfigs = useCallback(async () => {\n try {\n setLoading(true);\n const result = await api.getGithubSyncConfigs();\n setConfigs(result.configs);\n } catch {\n // failed silently\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n useEffect(() => {\n loadConfigs();\n }, [loadConfigs]);\n\n const handleCreate = async () => {\n if (!orgName.trim() || !appClientId.trim() || !appPrivateKey.trim()) return;\n setSubmitting(true);\n setStatusMessage(null);\n try {\n const hostname = githubHostname.trim() || 'github.com';\n await api.createGithubSyncConfig(orgName.trim(), hostname, appClientId.trim(), appPrivateKey.trim());\n setStatusMessage({ text: `GitHub sync registered for org \"${orgName}\" on \"${hostname}\"`, isError: false });\n setOrgName('');\n setGithubHostname('github.com');\n setAppClientId('');\n setAppPrivateKey('');\n setShowForm(false);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to register sync config', isError: true });\n } finally {\n setSubmitting(false);\n }\n };\n\n const handleToggle = async (config: GithubSyncConfig) => {\n try {\n await api.toggleGithubSyncConfig(config.id, !config.active);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to update config', isError: true });\n }\n };\n\n const handleDelete = async (id: number) => {\n try {\n await api.deleteGithubSyncConfig(id);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to delete config', isError: true });\n }\n };\n\n const handleSync = async (id: number) => {\n setSyncingIds(prev => new Set(prev).add(id));\n setStatusMessage(null);\n try {\n const result = await api.syncGithubConfig(id);\n if (result.error) {\n setStatusMessage({ text: result.error, isError: true });\n } else {\n setStatusMessage({\n text: `Synced ${result.count} members from \"${result.orgName}\"`,\n isError: false,\n });\n await loadConfigs();\n onSyncComplete?.();\n }\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Sync failed', isError: true });\n } finally {\n setSyncingIds(prev => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n };\n\n // Preconfigured GitHub App creation URL with required permissions\n const githubAppCreateUrl =\n 'https://github.com/settings/apps/new?' +\n 'name=devxp-member-sync&' +\n 'url=https%3A%2F%2Fdevxp.net&' +\n 'public=false&' +\n 'members=read';\n\n return (\n <InfoCard title=\"GitHub Organization Auto-Sync\">\n {/* Setup Guide */}\n <Box\n className={classes.guideBox}\n onClick={() => setShowGuide(v => !v)}\n >\n <Box className={classes.guideHeader}>\n {showGuide ? <ExpandLessIcon fontSize=\"small\" /> : <ExpandMoreIcon fontSize=\"small\" />}\n <Typography variant=\"body2\" style={{ marginLeft: 4, fontWeight: 600 }}>\n How to set up a GitHub App for member sync\n </Typography>\n </Box>\n <Collapse in={showGuide}>\n <Box mt={1}>\n <Typography variant=\"body2\" paragraph>\n DevXP syncs GitHub organization members automatically using a{' '}\n <Link href=\"https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps\" target=\"_blank\" rel=\"noopener noreferrer\">\n GitHub App\n </Link>\n . The app needs <strong>Read-only</strong> access to <strong>Organization members</strong>.\n </Typography>\n <Typography variant=\"body2\" component=\"div\">\n <strong>Steps:</strong>\n <ol>\n <li>\n <Link href={githubAppCreateUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n Click here to pre-configure and create your GitHub App\n </Link>\n {' '}— this link pre-fills the name and required permissions.\n </li>\n <li>\n On the creation page, scroll to <em>Organization permissions</em> and verify{' '}\n <strong>Members → Read-only</strong> is set.\n </li>\n <li>\n After creating the app, note down the <strong>Client ID</strong> from the app's{' '}\n <em>General</em> tab.\n </li>\n <li>\n Scroll to <em>Private keys</em> and click <strong>Generate a private key</strong>.\n Download the <code>.pem</code> file and paste its full contents below.\n </li>\n <li>\n <Link href=\"https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app\" target=\"_blank\" rel=\"noopener noreferrer\">\n Install the app\n </Link>\n {' '}in your GitHub organization (Settings → Developer settings → GitHub Apps → Install).\n </li>\n </ol>\n </Typography>\n <Typography variant=\"body2\">\n <Link href=\"https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app\" target=\"_blank\" rel=\"noopener noreferrer\">\n Full GitHub App documentation →\n </Link>\n </Typography>\n </Box>\n </Collapse>\n </Box>\n\n {/* Status message */}\n {statusMessage && (\n <Box\n className={`${classes.statusMessage} ${statusMessage.isError ? classes.error : classes.success}`}\n mb={2}\n >\n <Typography variant=\"body2\">{statusMessage.text}</Typography>\n </Box>\n )}\n\n {/* Add new config button / form */}\n {!showForm ? (\n <Button\n variant=\"outlined\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => { setShowForm(true); setStatusMessage(null); }}\n size=\"small\"\n >\n Register GitHub App\n </Button>\n ) : (\n <Box className={classes.form}>\n <Typography variant=\"subtitle2\">Register new GitHub App sync</Typography>\n <Box className={classes.formRow}>\n <TextField\n label=\"GitHub Org Name\"\n value={orgName}\n onChange={e => setOrgName(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"my-organization\"\n helperText=\"Your GitHub organization's login name\"\n />\n <TextField\n label=\"GitHub Hostname\"\n value={githubHostname}\n onChange={e => setGithubHostname(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"github.com\"\n helperText='Use \"github.com\" or your GitHub Enterprise Server hostname (e.g. github.acme.com)'\n />\n </Box>\n <Box className={classes.formRow}>\n <TextField\n label=\"App Client ID\"\n value={appClientId}\n onChange={e => setAppClientId(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"Iv1.a1b2c3d4e5f67890\"\n helperText=\"Found on the GitHub App's General tab\"\n />\n </Box>\n <TextField\n label=\"App Private Key (PEM)\"\n value={appPrivateKey}\n onChange={e => setAppPrivateKey(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n multiline\n rows={6}\n className={classes.privateKeyField}\n placeholder=\"-----BEGIN RSA PRIVATE KEY-----&#10;...&#10;-----END RSA PRIVATE KEY-----\"\n helperText=\"Paste the full contents of the .pem file downloaded from GitHub\"\n />\n <Box style={{ display: 'flex', gap: 8 }}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleCreate}\n disabled={submitting || !orgName.trim() || !appClientId.trim() || !appPrivateKey.trim()}\n >\n {submitting ? 'Registering...' : 'Register'}\n </Button>\n <Button\n variant=\"outlined\"\n onClick={() => { setShowForm(false); setOrgName(''); setGithubHostname('github.com'); setAppClientId(''); setAppPrivateKey(''); }}\n disabled={submitting}\n style={{ marginLeft: 8 }}\n >\n Cancel\n </Button>\n </Box>\n </Box>\n )}\n\n\n\n {/* Configs table */}\n <Box mt={3}>\n {loading ? (\n <Typography variant=\"body2\" color=\"textSecondary\">Loading configurations...</Typography>\n ) : configs.length === 0 ? (\n <Box className={classes.emptyState}>\n <Typography variant=\"body2\">\n No GitHub sync configurations yet. Register a GitHub App above to enable automatic member syncing.\n </Typography>\n </Box>\n ) : (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Organization</TableCell>\n <TableCell>GitHub Host</TableCell>\n <TableCell>Client ID</TableCell>\n <TableCell>Status</TableCell>\n <TableCell>Last Synced</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {configs.map(cfg => {\n const isSyncing = syncingIds.has(cfg.id);\n return (\n <TableRow key={cfg.id} className={isSyncing ? classes.syncingRow : undefined}>\n <TableCell>\n <strong>{cfg.org_name}</strong>\n </TableCell>\n <TableCell style={{ fontSize: '0.85em' }}>\n {cfg.github_hostname ?? 'github.com'}\n </TableCell>\n <TableCell style={{ fontFamily: 'monospace', fontSize: '0.85em' }}>\n {cfg.app_client_id}\n </TableCell>\n <TableCell>\n <Chip\n label={cfg.active ? 'Active' : 'Inactive'}\n size=\"small\"\n className={cfg.active ? classes.activeChip : classes.inactiveChip}\n onClick={() => handleToggle(cfg)}\n clickable\n />\n </TableCell>\n <TableCell>\n {cfg.last_synced_at ? (\n <Typography className={classes.lastSynced}>\n {new Date(cfg.last_synced_at).toLocaleString()}\n </Typography>\n ) : (\n <Typography className={classes.lastSynced}>Never</Typography>\n )}\n </TableCell>\n <TableCell align=\"right\">\n <Tooltip title=\"Sync now\">\n <span>\n <IconButton\n size=\"small\"\n onClick={() => handleSync(cfg.id)}\n disabled={isSyncing}\n aria-label=\"Sync now\"\n >\n <SyncIcon\n fontSize=\"small\"\n style={isSyncing ? { animation: 'spin 1s linear infinite' } : undefined}\n />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Delete configuration\">\n <IconButton\n size=\"small\"\n onClick={() => handleDelete(cfg.id)}\n disabled={isSyncing}\n aria-label=\"Delete config\"\n >\n <DeleteIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </TableContainer>\n )}\n </Box>\n\n <style>{`\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n `}</style>\n </InfoCard>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GithubSyncSection.esm.js","sources":["../../src/components/GithubSyncSection.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n Typography,\n Button,\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n IconButton,\n TextField,\n Chip,\n Tooltip,\n Collapse,\n Link,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport SyncIcon from '@material-ui/icons/Sync';\nimport AddIcon from '@material-ui/icons/Add';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport ExpandLessIcon from '@material-ui/icons/ExpandLess';\nimport { InfoCard } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { GithubSyncConfig } from '../types';\n\nconst useStyles = makeStyles(theme => ({\n form: {\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(2),\n marginTop: theme.spacing(2),\n },\n formRow: {\n display: 'flex',\n gap: theme.spacing(2),\n alignItems: 'flex-start',\n flexWrap: 'wrap',\n },\n textField: {\n flex: 1,\n minWidth: 200,\n },\n privateKeyField: {\n width: '100%',\n },\n statusMessage: {\n marginTop: theme.spacing(2),\n padding: theme.spacing(1.5),\n borderRadius: theme.shape.borderRadius,\n },\n success: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n error: {\n backgroundColor: '#ffebee',\n color: '#c62828',\n },\n activeChip: {\n backgroundColor: '#e8f5e9',\n color: '#2e7d32',\n },\n inactiveChip: {\n backgroundColor: '#f5f5f5',\n color: '#757575',\n },\n guideBox: {\n backgroundColor: theme.palette.background.default,\n border: `1px solid ${theme.palette.divider}`,\n borderRadius: theme.shape.borderRadius,\n padding: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n guideHeader: {\n display: 'flex',\n alignItems: 'center',\n cursor: 'pointer',\n userSelect: 'none',\n },\n syncingRow: {\n opacity: 0.6,\n },\n lastSynced: {\n fontSize: '0.75rem',\n color: theme.palette.text.secondary,\n },\n emptyState: {\n textAlign: 'center',\n padding: theme.spacing(3),\n color: theme.palette.text.secondary,\n },\n}));\n\ninterface GithubSyncSectionProps {\n api: DevxpApi;\n onSyncComplete?: () => void;\n}\n\nexport const GithubSyncSection = ({ api, onSyncComplete }: GithubSyncSectionProps) => {\n const classes = useStyles();\n const [configs, setConfigs] = React.useState<GithubSyncConfig[]>([]);\n const [loading, setLoading] = React.useState(true);\n const [showForm, setShowForm] = React.useState(false);\n const [showGuide, setShowGuide] = React.useState(false);\n const [syncingIds, setSyncingIds] = React.useState<Set<number>>(new Set());\n const [statusMessage, setStatusMessage] = React.useState<{ text: string; isError: boolean } | null>(null);\n\n // Form state\n const [orgName, setOrgName] = React.useState('');\n const [githubHostname, setGithubHostname] = React.useState('github.com');\n const [appClientId, setAppClientId] = React.useState('');\n const [appPrivateKey, setAppPrivateKey] = React.useState('');\n const [submitting, setSubmitting] = React.useState(false);\n\n const loadConfigs = React.useCallback(async () => {\n try {\n setLoading(true);\n const result = await api.getGithubSyncConfigs();\n setConfigs(result.configs);\n } catch {\n // failed silently\n } finally {\n setLoading(false);\n }\n }, [api]);\n\n React.useEffect(() => {\n loadConfigs();\n }, [loadConfigs]);\n\n const handleCreate = async () => {\n if (!orgName.trim() || !appClientId.trim() || !appPrivateKey.trim()) return;\n setSubmitting(true);\n setStatusMessage(null);\n try {\n const hostname = githubHostname.trim() || 'github.com';\n await api.createGithubSyncConfig(orgName.trim(), hostname, appClientId.trim(), appPrivateKey.trim());\n setStatusMessage({ text: `GitHub sync registered for org \"${orgName}\" on \"${hostname}\"`, isError: false });\n setOrgName('');\n setGithubHostname('github.com');\n setAppClientId('');\n setAppPrivateKey('');\n setShowForm(false);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to register sync config', isError: true });\n } finally {\n setSubmitting(false);\n }\n };\n\n const handleToggle = async (config: GithubSyncConfig) => {\n try {\n await api.toggleGithubSyncConfig(config.id, !config.active);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to update config', isError: true });\n }\n };\n\n const handleDelete = async (id: number) => {\n try {\n await api.deleteGithubSyncConfig(id);\n await loadConfigs();\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Failed to delete config', isError: true });\n }\n };\n\n const handleSync = async (id: number) => {\n setSyncingIds(prev => new Set(prev).add(id));\n setStatusMessage(null);\n try {\n const result = await api.syncGithubConfig(id);\n if (result.error) {\n setStatusMessage({ text: result.error, isError: true });\n } else {\n setStatusMessage({\n text: `Synced ${result.count} members from \"${result.orgName}\"`,\n isError: false,\n });\n await loadConfigs();\n onSyncComplete?.();\n }\n } catch (e: any) {\n setStatusMessage({ text: e.message || 'Sync failed', isError: true });\n } finally {\n setSyncingIds(prev => {\n const next = new Set(prev);\n next.delete(id);\n return next;\n });\n }\n };\n\n // Preconfigured GitHub App creation URL with required permissions\n const githubAppCreateUrl =\n 'https://github.com/settings/apps/new?' +\n 'name=devxp-member-sync&' +\n 'url=https%3A%2F%2Fdevxp.net&' +\n 'public=false&' +\n 'members=read';\n\n return (\n <InfoCard title=\"GitHub Organization Auto-Sync\">\n {/* Setup Guide */}\n <Box\n className={classes.guideBox}\n onClick={() => setShowGuide(v => !v)}\n >\n <Box className={classes.guideHeader}>\n {showGuide ? <ExpandLessIcon fontSize=\"small\" /> : <ExpandMoreIcon fontSize=\"small\" />}\n <Typography variant=\"body2\" style={{ marginLeft: 4, fontWeight: 600 }}>\n How to set up a GitHub App for member sync\n </Typography>\n </Box>\n <Collapse in={showGuide}>\n <Box mt={1}>\n <Typography variant=\"body2\" paragraph>\n DevXP syncs GitHub organization members automatically using a{' '}\n <Link href=\"https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps\" target=\"_blank\" rel=\"noopener noreferrer\">\n GitHub App\n </Link>\n . The app needs <strong>Read-only</strong> access to <strong>Organization members</strong>.\n </Typography>\n <Typography variant=\"body2\" component=\"div\">\n <strong>Steps:</strong>\n <ol>\n <li>\n <Link href={githubAppCreateUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n Click here to pre-configure and create your GitHub App\n </Link>\n {' '}— this link pre-fills the name and required permissions.\n </li>\n <li>\n On the creation page, scroll to <em>Organization permissions</em> and verify{' '}\n <strong>Members → Read-only</strong> is set.\n </li>\n <li>\n After creating the app, note down the <strong>Client ID</strong> from the app's{' '}\n <em>General</em> tab.\n </li>\n <li>\n Scroll to <em>Private keys</em> and click <strong>Generate a private key</strong>.\n Download the <code>.pem</code> file and paste its full contents below.\n </li>\n <li>\n <Link href=\"https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app\" target=\"_blank\" rel=\"noopener noreferrer\">\n Install the app\n </Link>\n {' '}in your GitHub organization (Settings → Developer settings → GitHub Apps → Install).\n </li>\n </ol>\n </Typography>\n <Typography variant=\"body2\">\n <Link href=\"https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app\" target=\"_blank\" rel=\"noopener noreferrer\">\n Full GitHub App documentation →\n </Link>\n </Typography>\n </Box>\n </Collapse>\n </Box>\n\n {/* Status message */}\n {statusMessage && (\n <Box\n className={`${classes.statusMessage} ${statusMessage.isError ? classes.error : classes.success}`}\n mb={2}\n >\n <Typography variant=\"body2\">{statusMessage.text}</Typography>\n </Box>\n )}\n\n {/* Add new config button / form */}\n {!showForm ? (\n <Button\n variant=\"outlined\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => { setShowForm(true); setStatusMessage(null); }}\n size=\"small\"\n >\n Register GitHub App\n </Button>\n ) : (\n <Box className={classes.form}>\n <Typography variant=\"subtitle2\">Register new GitHub App sync</Typography>\n <Box className={classes.formRow}>\n <TextField\n label=\"GitHub Org Name\"\n value={orgName}\n onChange={e => setOrgName(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"my-organization\"\n helperText=\"Your GitHub organization's login name\"\n />\n <TextField\n label=\"GitHub Hostname\"\n value={githubHostname}\n onChange={e => setGithubHostname(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"github.com\"\n helperText='Use \"github.com\" or your GitHub Enterprise Server hostname (e.g. github.acme.com)'\n />\n </Box>\n <Box className={classes.formRow}>\n <TextField\n label=\"App Client ID\"\n value={appClientId}\n onChange={e => setAppClientId(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n className={classes.textField}\n placeholder=\"Iv1.a1b2c3d4e5f67890\"\n helperText=\"Found on the GitHub App's General tab\"\n />\n </Box>\n <TextField\n label=\"App Private Key (PEM)\"\n value={appPrivateKey}\n onChange={e => setAppPrivateKey(e.target.value)}\n variant=\"outlined\"\n size=\"small\"\n multiline\n rows={6}\n className={classes.privateKeyField}\n placeholder=\"-----BEGIN RSA PRIVATE KEY-----&#10;...&#10;-----END RSA PRIVATE KEY-----\"\n helperText=\"Paste the full contents of the .pem file downloaded from GitHub\"\n />\n <Box style={{ display: 'flex', gap: 8 }}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleCreate}\n disabled={submitting || !orgName.trim() || !appClientId.trim() || !appPrivateKey.trim()}\n >\n {submitting ? 'Registering...' : 'Register'}\n </Button>\n <Button\n variant=\"outlined\"\n onClick={() => { setShowForm(false); setOrgName(''); setGithubHostname('github.com'); setAppClientId(''); setAppPrivateKey(''); }}\n disabled={submitting}\n style={{ marginLeft: 8 }}\n >\n Cancel\n </Button>\n </Box>\n </Box>\n )}\n\n\n\n {/* Configs table */}\n <Box mt={3}>\n {loading ? (\n <Typography variant=\"body2\" color=\"textSecondary\">Loading configurations...</Typography>\n ) : configs.length === 0 ? (\n <Box className={classes.emptyState}>\n <Typography variant=\"body2\">\n No GitHub sync configurations yet. Register a GitHub App above to enable automatic member syncing.\n </Typography>\n </Box>\n ) : (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Organization</TableCell>\n <TableCell>GitHub Host</TableCell>\n <TableCell>Client ID</TableCell>\n <TableCell>Status</TableCell>\n <TableCell>Last Synced</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {configs.map(cfg => {\n const isSyncing = syncingIds.has(cfg.id);\n return (\n <TableRow key={cfg.id} className={isSyncing ? classes.syncingRow : undefined}>\n <TableCell>\n <strong>{cfg.org_name}</strong>\n </TableCell>\n <TableCell style={{ fontSize: '0.85em' }}>\n {cfg.github_hostname ?? 'github.com'}\n </TableCell>\n <TableCell style={{ fontFamily: 'monospace', fontSize: '0.85em' }}>\n {cfg.app_client_id}\n </TableCell>\n <TableCell>\n <Chip\n label={cfg.active ? 'Active' : 'Inactive'}\n size=\"small\"\n className={cfg.active ? classes.activeChip : classes.inactiveChip}\n onClick={() => handleToggle(cfg)}\n clickable\n />\n </TableCell>\n <TableCell>\n {cfg.last_synced_at ? (\n <Typography className={classes.lastSynced}>\n {new Date(cfg.last_synced_at).toLocaleString()}\n </Typography>\n ) : (\n <Typography className={classes.lastSynced}>Never</Typography>\n )}\n </TableCell>\n <TableCell align=\"right\">\n <Tooltip title=\"Sync now\">\n <span>\n <IconButton\n size=\"small\"\n onClick={() => handleSync(cfg.id)}\n disabled={isSyncing}\n aria-label=\"Sync now\"\n >\n <SyncIcon\n fontSize=\"small\"\n style={isSyncing ? { animation: 'spin 1s linear infinite' } : undefined}\n />\n </IconButton>\n </span>\n </Tooltip>\n <Tooltip title=\"Delete configuration\">\n <IconButton\n size=\"small\"\n onClick={() => handleDelete(cfg.id)}\n disabled={isSyncing}\n aria-label=\"Delete config\"\n >\n <DeleteIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </TableContainer>\n )}\n </Box>\n\n <style>{`\n @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n `}</style>\n </InfoCard>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,251 @@
1
+ import { useState, useCallback, useEffect } from 'react';
2
+ import { Box, TextField, IconButton, CircularProgress, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Typography, LinearProgress, Button, Tooltip, Chip } from '@material-ui/core';
3
+ import { makeStyles } from '@material-ui/core/styles';
4
+ import SearchIcon from '@material-ui/icons/Search';
5
+ import ArrowBackIcon from '@material-ui/icons/ArrowBack';
6
+ import StorageIcon from '@material-ui/icons/Storage';
7
+ import PersonIcon from '@material-ui/icons/Person';
8
+ import { InfoCard, EmptyState } from '@backstage/core-components';
9
+
10
+ const CATEGORY_COLORS = {
11
+ "Frontend Engineering": "#e91e63",
12
+ "Backend Systems": "#3f51b5",
13
+ "Database & Data": "#9c27b0",
14
+ "Infrastructure (DevOps)": "#ff9800",
15
+ "Mobile Development": "#4caf50",
16
+ "AI & Machine Learning": "#607d8b",
17
+ "Testing & QA": "#00bcd4",
18
+ "Security & Auth": "#f44336",
19
+ "Distributed Systems": "#795548",
20
+ "Documentation": "#9e9e9e"
21
+ };
22
+ const useStyles = makeStyles((theme) => ({
23
+ header: {
24
+ display: "flex",
25
+ alignItems: "center",
26
+ gap: theme.spacing(1),
27
+ marginBottom: theme.spacing(2)
28
+ },
29
+ searchRow: {
30
+ display: "flex",
31
+ gap: theme.spacing(2),
32
+ marginBottom: theme.spacing(2),
33
+ alignItems: "center"
34
+ },
35
+ searchField: {
36
+ flex: 1,
37
+ maxWidth: 400
38
+ },
39
+ clickableRow: {
40
+ cursor: "pointer",
41
+ "&:hover": {
42
+ backgroundColor: theme.palette.action.hover
43
+ }
44
+ },
45
+ repoName: {
46
+ fontFamily: "monospace",
47
+ fontWeight: 600
48
+ },
49
+ pagination: {
50
+ display: "flex",
51
+ justifyContent: "space-between",
52
+ alignItems: "center",
53
+ padding: theme.spacing(1, 0),
54
+ marginTop: theme.spacing(1)
55
+ },
56
+ categoryChip: {
57
+ margin: theme.spacing(0.25),
58
+ fontSize: "0.65rem",
59
+ height: 18
60
+ },
61
+ scoreBar: {
62
+ height: 6,
63
+ borderRadius: 3,
64
+ marginTop: 4
65
+ },
66
+ sectionTitle: {
67
+ fontWeight: 600,
68
+ marginBottom: theme.spacing(1),
69
+ marginTop: theme.spacing(2)
70
+ },
71
+ statValue: {
72
+ fontWeight: 700,
73
+ fontSize: "1.5rem"
74
+ },
75
+ statLabel: {
76
+ color: theme.palette.text.secondary,
77
+ fontSize: "0.75rem"
78
+ },
79
+ statBox: {
80
+ textAlign: "center",
81
+ padding: theme.spacing(1.5)
82
+ }
83
+ }));
84
+ const RepositoriesContent = ({ api, onViewDeveloper }) => {
85
+ const [view, setView] = useState("list");
86
+ const [selectedRepo, setSelectedRepo] = useState("");
87
+ return /* @__PURE__ */ React.createElement(Box, null, view === "list" ? /* @__PURE__ */ React.createElement(
88
+ RepositoryList,
89
+ {
90
+ api,
91
+ onViewRepo: (repoName) => {
92
+ setSelectedRepo(repoName);
93
+ setView("details");
94
+ }
95
+ }
96
+ ) : /* @__PURE__ */ React.createElement(
97
+ RepositoryDetailsView,
98
+ {
99
+ api,
100
+ repoName: selectedRepo,
101
+ onBack: () => setView("list"),
102
+ onViewDeveloper
103
+ }
104
+ ));
105
+ };
106
+ const RepositoryList = ({ api, onViewRepo }) => {
107
+ const classes = useStyles();
108
+ const [repositories, setRepositories] = useState([]);
109
+ const [totalCount, setTotalCount] = useState(0);
110
+ const [totalPages, setTotalPages] = useState(1);
111
+ const [page, setPage] = useState(1);
112
+ const pageSize = 25;
113
+ const [loading, setLoading] = useState(true);
114
+ const [error, setError] = useState(null);
115
+ const [searchInput, setSearchInput] = useState("");
116
+ const [appliedSearch, setAppliedSearch] = useState("");
117
+ const load = useCallback(
118
+ async (pg, search) => {
119
+ setLoading(true);
120
+ setError(null);
121
+ try {
122
+ const data = await api.getRepositories({ page: pg, pageSize, searchQuery: search });
123
+ setRepositories(data.repositories);
124
+ setTotalCount(data.totalCount);
125
+ setTotalPages(data.totalPages);
126
+ } catch (e) {
127
+ setError(e.message ?? "Failed to load repositories");
128
+ } finally {
129
+ setLoading(false);
130
+ }
131
+ },
132
+ [api]
133
+ );
134
+ useEffect(() => {
135
+ load(page, appliedSearch);
136
+ }, [load, page, appliedSearch]);
137
+ const applySearch = () => {
138
+ setPage(1);
139
+ setAppliedSearch(searchInput);
140
+ };
141
+ const formatDate = (iso) => {
142
+ try {
143
+ return new Date(iso).toLocaleDateString(void 0, {
144
+ year: "numeric",
145
+ month: "short",
146
+ day: "numeric"
147
+ });
148
+ } catch {
149
+ return iso;
150
+ }
151
+ };
152
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: `Repositories (${totalCount})`, noPadding: false }, /* @__PURE__ */ React.createElement(Box, { className: classes.searchRow }, /* @__PURE__ */ React.createElement(
153
+ TextField,
154
+ {
155
+ className: classes.searchField,
156
+ size: "small",
157
+ variant: "outlined",
158
+ label: "Search repositories",
159
+ value: searchInput,
160
+ onChange: (e) => setSearchInput(e.target.value),
161
+ onKeyDown: (e) => e.key === "Enter" && applySearch(),
162
+ InputProps: {
163
+ endAdornment: /* @__PURE__ */ React.createElement(IconButton, { size: "small", onClick: applySearch }, /* @__PURE__ */ React.createElement(SearchIcon, { fontSize: "small" }))
164
+ }
165
+ }
166
+ )), loading ? /* @__PURE__ */ React.createElement(Box, { display: "flex", justifyContent: "center", py: 4 }, /* @__PURE__ */ React.createElement(CircularProgress, null)) : error ? /* @__PURE__ */ React.createElement(EmptyState, { missing: "data", title: "Repositories unavailable", description: error }) : repositories.length === 0 ? /* @__PURE__ */ React.createElement(EmptyState, { missing: "data", title: "No repositories found", description: "Try a different search." }) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Repository"), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, "Developers"), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, "Events"), /* @__PURE__ */ React.createElement(TableCell, { style: { minWidth: 140 } }, "Avg Proficiency"), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, "Last Activity"))), /* @__PURE__ */ React.createElement(TableBody, null, repositories.map((repo) => /* @__PURE__ */ React.createElement(
167
+ TableRow,
168
+ {
169
+ key: repo.repoName,
170
+ className: classes.clickableRow,
171
+ onClick: () => onViewRepo(repo.repoName)
172
+ },
173
+ /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(StorageIcon, { fontSize: "small", color: "action" }), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", className: classes.repoName }, repo.repoName))),
174
+ /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, repo.developerCount)),
175
+ /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, repo.eventCount.toLocaleString())),
176
+ /* @__PURE__ */ React.createElement(TableCell, { style: { minWidth: 140 } }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", style: { fontWeight: 600 } }, (repo.avgProficiency * 10).toFixed(1), "%"), /* @__PURE__ */ React.createElement(
177
+ LinearProgress,
178
+ {
179
+ variant: "determinate",
180
+ value: repo.avgProficiency * 10,
181
+ className: classes.scoreBar
182
+ }
183
+ )),
184
+ /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, formatDate(repo.lastActivity)))
185
+ ))))), /* @__PURE__ */ React.createElement(Box, { className: classes.pagination }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Showing ", (page - 1) * pageSize + 1, "\u2013", Math.min(page * pageSize, totalCount), " of ", totalCount), /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(Button, { size: "small", disabled: page <= 1, onClick: () => setPage((p) => p - 1) }, "Prev"), /* @__PURE__ */ React.createElement(Typography, { variant: "caption" }, page, " / ", totalPages), /* @__PURE__ */ React.createElement(Button, { size: "small", disabled: page >= totalPages, onClick: () => setPage((p) => p + 1) }, "Next")))));
186
+ };
187
+ const RepositoryDetailsView = ({ api, repoName, onBack, onViewDeveloper }) => {
188
+ const classes = useStyles();
189
+ const [details, setDetails] = useState(null);
190
+ const [loading, setLoading] = useState(true);
191
+ const [error, setError] = useState(null);
192
+ useEffect(() => {
193
+ setLoading(true);
194
+ setError(null);
195
+ api.getRepositoryDetails(repoName).then(setDetails).catch((e) => setError(e.message ?? "Failed to load repository details")).finally(() => setLoading(false));
196
+ }, [api, repoName]);
197
+ const formatDate = (iso) => {
198
+ try {
199
+ return new Date(iso).toLocaleDateString(void 0, {
200
+ year: "numeric",
201
+ month: "short",
202
+ day: "numeric"
203
+ });
204
+ } catch {
205
+ return iso;
206
+ }
207
+ };
208
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Box, { className: classes.header }, /* @__PURE__ */ React.createElement(Tooltip, { title: "Back to repositories" }, /* @__PURE__ */ React.createElement(IconButton, { size: "small", onClick: onBack }, /* @__PURE__ */ React.createElement(ArrowBackIcon, null))), /* @__PURE__ */ React.createElement(StorageIcon, { color: "action" }), /* @__PURE__ */ React.createElement(Typography, { variant: "h6", className: classes.repoName }, repoName)), loading ? /* @__PURE__ */ React.createElement(Box, { display: "flex", justifyContent: "center", py: 6 }, /* @__PURE__ */ React.createElement(CircularProgress, null)) : error ? /* @__PURE__ */ React.createElement(EmptyState, { missing: "data", title: "Details unavailable", description: error }) : details ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InfoCard, { title: "Overview" }, /* @__PURE__ */ React.createElement(Box, { display: "flex", justifyContent: "space-around", flexWrap: "wrap" }, /* @__PURE__ */ React.createElement(Box, { className: classes.statBox }, /* @__PURE__ */ React.createElement(Typography, { className: classes.statValue }, details.developerCount), /* @__PURE__ */ React.createElement(Typography, { className: classes.statLabel }, "Developers")), /* @__PURE__ */ React.createElement(Box, { className: classes.statBox }, /* @__PURE__ */ React.createElement(Typography, { className: classes.statValue }, details.eventCount.toLocaleString()), /* @__PURE__ */ React.createElement(Typography, { className: classes.statLabel }, "Skill Events")), /* @__PURE__ */ React.createElement(Box, { className: classes.statBox }, /* @__PURE__ */ React.createElement(Typography, { className: classes.statValue }, (details.avgProficiency * 10).toFixed(1), "%"), /* @__PURE__ */ React.createElement(Typography, { className: classes.statLabel }, "Avg Proficiency")), /* @__PURE__ */ React.createElement(Box, { className: classes.statBox }, /* @__PURE__ */ React.createElement(Typography, { className: classes.statValue, style: { fontSize: "1rem" } }, formatDate(details.lastActivity)), /* @__PURE__ */ React.createElement(Typography, { className: classes.statLabel }, "Last Activity")))), details.categoryBreakdown.length > 0 && /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Skill Categories" }, /* @__PURE__ */ React.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Category"), /* @__PURE__ */ React.createElement(TableCell, { style: { minWidth: 160 } }, "Avg Proficiency"))), /* @__PURE__ */ React.createElement(TableBody, null, details.categoryBreakdown.map((cat) => /* @__PURE__ */ React.createElement(TableRow, { key: cat.category }, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(
209
+ Chip,
210
+ {
211
+ label: cat.category,
212
+ size: "small",
213
+ className: classes.categoryChip,
214
+ style: {
215
+ backgroundColor: `${CATEGORY_COLORS[cat.category] ?? "#9e9e9e"}20`,
216
+ color: CATEGORY_COLORS[cat.category] ?? "#9e9e9e",
217
+ height: 20
218
+ }
219
+ }
220
+ )), /* @__PURE__ */ React.createElement(TableCell, { style: { minWidth: 160 } }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", style: { fontWeight: 600 } }, (cat.averageProficiency * 10).toFixed(1), "%"), /* @__PURE__ */ React.createElement(
221
+ LinearProgress,
222
+ {
223
+ variant: "determinate",
224
+ value: cat.averageProficiency * 10,
225
+ className: classes.scoreBar,
226
+ style: { backgroundColor: `${CATEGORY_COLORS[cat.category] ?? "#9e9e9e"}30` }
227
+ }
228
+ ))))))))), details.topDevelopers.length > 0 && /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Top Developers" }, /* @__PURE__ */ React.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "#"), /* @__PURE__ */ React.createElement(TableCell, null, "Developer"), /* @__PURE__ */ React.createElement(TableCell, { style: { minWidth: 140 } }, "Avg Proficiency"), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, "Skills"), onViewDeveloper && /* @__PURE__ */ React.createElement(TableCell, { align: "right" }))), /* @__PURE__ */ React.createElement(TableBody, null, details.topDevelopers.map((dev, idx) => /* @__PURE__ */ React.createElement(
229
+ TableRow,
230
+ {
231
+ key: dev.userId,
232
+ className: onViewDeveloper ? classes.clickableRow : void 0,
233
+ onClick: () => onViewDeveloper?.(dev.userId)
234
+ },
235
+ /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, idx + 1)),
236
+ /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", style: { fontFamily: "monospace", fontWeight: 600 } }, dev.userId)),
237
+ /* @__PURE__ */ React.createElement(TableCell, { style: { minWidth: 140 } }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", style: { fontWeight: 600 } }, (dev.averageProficiency * 10).toFixed(1), "%"), /* @__PURE__ */ React.createElement(
238
+ LinearProgress,
239
+ {
240
+ variant: "determinate",
241
+ value: dev.averageProficiency * 10,
242
+ className: classes.scoreBar
243
+ }
244
+ )),
245
+ /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, dev.skillCount)),
246
+ onViewDeveloper && /* @__PURE__ */ React.createElement(TableCell, { align: "right", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(Tooltip, { title: "View developer details" }, /* @__PURE__ */ React.createElement(IconButton, { size: "small", onClick: () => onViewDeveloper(dev.userId) }, /* @__PURE__ */ React.createElement(PersonIcon, { fontSize: "small" }))))
247
+ )))))))) : null);
248
+ };
249
+
250
+ export { RepositoriesContent };
251
+ //# sourceMappingURL=RepositoriesContent.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RepositoriesContent.esm.js","sources":["../../src/components/RepositoriesContent.tsx"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport {\n Box,\n Typography,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Paper,\n TextField,\n Chip,\n IconButton,\n Button,\n CircularProgress,\n Tooltip,\n LinearProgress,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport SearchIcon from '@material-ui/icons/Search';\nimport ArrowBackIcon from '@material-ui/icons/ArrowBack';\nimport StorageIcon from '@material-ui/icons/Storage';\nimport PersonIcon from '@material-ui/icons/Person';\nimport { InfoCard, EmptyState } from '@backstage/core-components';\nimport type { DevxpApi } from '../api';\nimport type { RepositoryStats, RepositoryDetails } from '../types';\n\nconst CATEGORY_COLORS: Record<string, string> = {\n 'Frontend Engineering': '#e91e63',\n 'Backend Systems': '#3f51b5',\n 'Database & Data': '#9c27b0',\n 'Infrastructure (DevOps)': '#ff9800',\n 'Mobile Development': '#4caf50',\n 'AI & Machine Learning': '#607d8b',\n 'Testing & QA': '#00bcd4',\n 'Security & Auth': '#f44336',\n 'Distributed Systems': '#795548',\n 'Documentation': '#9e9e9e',\n};\n\nconst useStyles = makeStyles(theme => ({\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginBottom: theme.spacing(2),\n },\n searchRow: {\n display: 'flex',\n gap: theme.spacing(2),\n marginBottom: theme.spacing(2),\n alignItems: 'center',\n },\n searchField: {\n flex: 1,\n maxWidth: 400,\n },\n clickableRow: {\n cursor: 'pointer',\n '&:hover': {\n backgroundColor: theme.palette.action.hover,\n },\n },\n repoName: {\n fontFamily: 'monospace',\n fontWeight: 600,\n },\n pagination: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n padding: theme.spacing(1, 0),\n marginTop: theme.spacing(1),\n },\n categoryChip: {\n margin: theme.spacing(0.25),\n fontSize: '0.65rem',\n height: 18,\n },\n scoreBar: {\n height: 6,\n borderRadius: 3,\n marginTop: 4,\n },\n sectionTitle: {\n fontWeight: 600,\n marginBottom: theme.spacing(1),\n marginTop: theme.spacing(2),\n },\n statValue: {\n fontWeight: 700,\n fontSize: '1.5rem',\n },\n statLabel: {\n color: theme.palette.text.secondary,\n fontSize: '0.75rem',\n },\n statBox: {\n textAlign: 'center',\n padding: theme.spacing(1.5),\n },\n}));\n\ninterface RepositoriesContentProps {\n api: DevxpApi;\n onViewDeveloper?: (userId: string) => void;\n}\n\nexport const RepositoriesContent = ({ api, onViewDeveloper }: RepositoriesContentProps) => {\n const [view, setView] = useState<'list' | 'details'>('list');\n const [selectedRepo, setSelectedRepo] = useState('');\n\n return (\n <Box>\n {view === 'list' ? (\n <RepositoryList\n api={api}\n onViewRepo={repoName => {\n setSelectedRepo(repoName);\n setView('details');\n }}\n />\n ) : (\n <RepositoryDetailsView\n api={api}\n repoName={selectedRepo}\n onBack={() => setView('list')}\n onViewDeveloper={onViewDeveloper}\n />\n )}\n </Box>\n );\n};\n\n// ─── Repository List ──────────────────────────────────────────────────────────\n\ninterface RepositoryListProps {\n api: DevxpApi;\n onViewRepo: (repoName: string) => void;\n}\n\nconst RepositoryList = ({ api, onViewRepo }: RepositoryListProps) => {\n const classes = useStyles();\n\n const [repositories, setRepositories] = useState<RepositoryStats[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [totalPages, setTotalPages] = useState(1);\n const [page, setPage] = useState(1);\n const pageSize = 25;\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [searchInput, setSearchInput] = useState('');\n const [appliedSearch, setAppliedSearch] = useState('');\n\n const load = useCallback(\n async (pg: number, search: string) => {\n setLoading(true);\n setError(null);\n try {\n const data = await api.getRepositories({ page: pg, pageSize, searchQuery: search });\n setRepositories(data.repositories);\n setTotalCount(data.totalCount);\n setTotalPages(data.totalPages);\n } catch (e: any) {\n setError(e.message ?? 'Failed to load repositories');\n } finally {\n setLoading(false);\n }\n },\n [api],\n );\n\n useEffect(() => {\n load(page, appliedSearch);\n }, [load, page, appliedSearch]);\n\n const applySearch = () => {\n setPage(1);\n setAppliedSearch(searchInput);\n };\n\n const formatDate = (iso: string) => {\n try {\n return new Date(iso).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n } catch {\n return iso;\n }\n };\n\n return (\n <InfoCard title={`Repositories (${totalCount})`} noPadding={false}>\n <Box className={classes.searchRow}>\n <TextField\n className={classes.searchField}\n size=\"small\"\n variant=\"outlined\"\n label=\"Search repositories\"\n value={searchInput}\n onChange={e => setSearchInput(e.target.value)}\n onKeyDown={e => e.key === 'Enter' && applySearch()}\n InputProps={{\n endAdornment: (\n <IconButton size=\"small\" onClick={applySearch}>\n <SearchIcon fontSize=\"small\" />\n </IconButton>\n ),\n }}\n />\n </Box>\n\n {loading ? (\n <Box display=\"flex\" justifyContent=\"center\" py={4}>\n <CircularProgress />\n </Box>\n ) : error ? (\n <EmptyState missing=\"data\" title=\"Repositories unavailable\" description={error} />\n ) : repositories.length === 0 ? (\n <EmptyState missing=\"data\" title=\"No repositories found\" description=\"Try a different search.\" />\n ) : (\n <>\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Repository</TableCell>\n <TableCell align=\"right\">Developers</TableCell>\n <TableCell align=\"right\">Events</TableCell>\n <TableCell style={{ minWidth: 140 }}>Avg Proficiency</TableCell>\n <TableCell align=\"right\">Last Activity</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {repositories.map(repo => (\n <TableRow\n key={repo.repoName}\n className={classes.clickableRow}\n onClick={() => onViewRepo(repo.repoName)}\n >\n <TableCell>\n <Box display=\"flex\" alignItems=\"center\" style={{ gap: 8 }}>\n <StorageIcon fontSize=\"small\" color=\"action\" />\n <Typography variant=\"body2\" className={classes.repoName}>\n {repo.repoName}\n </Typography>\n </Box>\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"body2\">{repo.developerCount}</Typography>\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"body2\">{repo.eventCount.toLocaleString()}</Typography>\n </TableCell>\n <TableCell style={{ minWidth: 140 }}>\n <Typography variant=\"body2\" style={{ fontWeight: 600 }}>\n {(repo.avgProficiency * 10).toFixed(1)}%\n </Typography>\n <LinearProgress\n variant=\"determinate\"\n value={repo.avgProficiency * 10}\n className={classes.scoreBar}\n />\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"caption\" color=\"textSecondary\">\n {formatDate(repo.lastActivity)}\n </Typography>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n\n <Box className={classes.pagination}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Showing {(page - 1) * pageSize + 1}–{Math.min(page * pageSize, totalCount)} of {totalCount}\n </Typography>\n <Box display=\"flex\" alignItems=\"center\" style={{ gap: 8 }}>\n <Button size=\"small\" disabled={page <= 1} onClick={() => setPage(p => p - 1)}>\n Prev\n </Button>\n <Typography variant=\"caption\">\n {page} / {totalPages}\n </Typography>\n <Button size=\"small\" disabled={page >= totalPages} onClick={() => setPage(p => p + 1)}>\n Next\n </Button>\n </Box>\n </Box>\n </>\n )}\n </InfoCard>\n );\n};\n\n// ─── Repository Details ───────────────────────────────────────────────────────\n\ninterface RepositoryDetailsViewProps {\n api: DevxpApi;\n repoName: string;\n onBack: () => void;\n onViewDeveloper?: (userId: string) => void;\n}\n\nconst RepositoryDetailsView = ({ api, repoName, onBack, onViewDeveloper }: RepositoryDetailsViewProps) => {\n const classes = useStyles();\n\n const [details, setDetails] = useState<RepositoryDetails | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n setLoading(true);\n setError(null);\n api\n .getRepositoryDetails(repoName)\n .then(setDetails)\n .catch((e: any) => setError(e.message ?? 'Failed to load repository details'))\n .finally(() => setLoading(false));\n }, [api, repoName]);\n\n const formatDate = (iso: string) => {\n try {\n return new Date(iso).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n } catch {\n return iso;\n }\n };\n\n return (\n <Box>\n <Box className={classes.header}>\n <Tooltip title=\"Back to repositories\">\n <IconButton size=\"small\" onClick={onBack}>\n <ArrowBackIcon />\n </IconButton>\n </Tooltip>\n <StorageIcon color=\"action\" />\n <Typography variant=\"h6\" className={classes.repoName}>\n {repoName}\n </Typography>\n </Box>\n\n {loading ? (\n <Box display=\"flex\" justifyContent=\"center\" py={6}>\n <CircularProgress />\n </Box>\n ) : error ? (\n <EmptyState missing=\"data\" title=\"Details unavailable\" description={error} />\n ) : details ? (\n <>\n {/* Summary stats */}\n <InfoCard title=\"Overview\">\n <Box display=\"flex\" justifyContent=\"space-around\" flexWrap=\"wrap\">\n <Box className={classes.statBox}>\n <Typography className={classes.statValue}>{details.developerCount}</Typography>\n <Typography className={classes.statLabel}>Developers</Typography>\n </Box>\n <Box className={classes.statBox}>\n <Typography className={classes.statValue}>{details.eventCount.toLocaleString()}</Typography>\n <Typography className={classes.statLabel}>Skill Events</Typography>\n </Box>\n <Box className={classes.statBox}>\n <Typography className={classes.statValue}>\n {(details.avgProficiency * 10).toFixed(1)}%\n </Typography>\n <Typography className={classes.statLabel}>Avg Proficiency</Typography>\n </Box>\n <Box className={classes.statBox}>\n <Typography className={classes.statValue} style={{ fontSize: '1rem' }}>\n {formatDate(details.lastActivity)}\n </Typography>\n <Typography className={classes.statLabel}>Last Activity</Typography>\n </Box>\n </Box>\n </InfoCard>\n\n {/* Category breakdown */}\n {details.categoryBreakdown.length > 0 && (\n <Box mt={2}>\n <InfoCard title=\"Skill Categories\">\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Category</TableCell>\n <TableCell style={{ minWidth: 160 }}>Avg Proficiency</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {details.categoryBreakdown.map(cat => (\n <TableRow key={cat.category}>\n <TableCell>\n <Chip\n label={cat.category}\n size=\"small\"\n className={classes.categoryChip}\n style={{\n backgroundColor: `${CATEGORY_COLORS[cat.category] ?? '#9e9e9e'}20`,\n color: CATEGORY_COLORS[cat.category] ?? '#9e9e9e',\n height: 20,\n }}\n />\n </TableCell>\n <TableCell style={{ minWidth: 160 }}>\n <Typography variant=\"body2\" style={{ fontWeight: 600 }}>\n {(cat.averageProficiency * 10).toFixed(1)}%\n </Typography>\n <LinearProgress\n variant=\"determinate\"\n value={cat.averageProficiency * 10}\n className={classes.scoreBar}\n style={{ backgroundColor: `${CATEGORY_COLORS[cat.category] ?? '#9e9e9e'}30` }}\n />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </InfoCard>\n </Box>\n )}\n\n {/* Top developers */}\n {details.topDevelopers.length > 0 && (\n <Box mt={2}>\n <InfoCard title=\"Top Developers\">\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>#</TableCell>\n <TableCell>Developer</TableCell>\n <TableCell style={{ minWidth: 140 }}>Avg Proficiency</TableCell>\n <TableCell align=\"right\">Skills</TableCell>\n {onViewDeveloper && <TableCell align=\"right\" />}\n </TableRow>\n </TableHead>\n <TableBody>\n {details.topDevelopers.map((dev, idx) => (\n <TableRow\n key={dev.userId}\n className={onViewDeveloper ? classes.clickableRow : undefined}\n onClick={() => onViewDeveloper?.(dev.userId)}\n >\n <TableCell>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {idx + 1}\n </Typography>\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" style={{ fontFamily: 'monospace', fontWeight: 600 }}>\n {dev.userId}\n </Typography>\n </TableCell>\n <TableCell style={{ minWidth: 140 }}>\n <Typography variant=\"body2\" style={{ fontWeight: 600 }}>\n {(dev.averageProficiency * 10).toFixed(1)}%\n </Typography>\n <LinearProgress\n variant=\"determinate\"\n value={dev.averageProficiency * 10}\n className={classes.scoreBar}\n />\n </TableCell>\n <TableCell align=\"right\">\n <Typography variant=\"body2\">{dev.skillCount}</Typography>\n </TableCell>\n {onViewDeveloper && (\n <TableCell align=\"right\" onClick={e => e.stopPropagation()}>\n <Tooltip title=\"View developer details\">\n <IconButton size=\"small\" onClick={() => onViewDeveloper(dev.userId)}>\n <PersonIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </TableCell>\n )}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </InfoCard>\n </Box>\n )}\n </>\n ) : null}\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState, useCallback, useEffect } from 'react';
1
+ import React__default, { useRef, useState, useCallback, useEffect } from 'react';
2
2
  import { Grid, Typography, Box, Button, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, IconButton } from '@material-ui/core';
3
3
  import { makeStyles } from '@material-ui/core/styles';
4
4
  import DeleteIcon from '@material-ui/icons/Delete';
@@ -101,7 +101,7 @@ const SettingsContent = ({ api }) => {
101
101
  } catch {
102
102
  }
103
103
  };
104
- return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(GithubSyncSection, { api, onSyncComplete: loadMappings })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Upload Developer Names (CSV)" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, "Upload a CSV file with developer names (one name per line). The system will compute SHA-256 hashes using the configured salt and store the masked-to-real name mappings. Duplicate names will be updated."), /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(
104
+ return /* @__PURE__ */ React__default.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(GithubSyncSection, { api, onSyncComplete: loadMappings })), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Upload Developer Names (CSV)" }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, "Upload a CSV file with developer names (one name per line). The system will compute SHA-256 hashes using the configured salt and store the masked-to-real name mappings. Duplicate names will be updated."), /* @__PURE__ */ React__default.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React__default.createElement(
105
105
  "input",
106
106
  {
107
107
  ref: fileInputRef,
@@ -111,31 +111,31 @@ const SettingsContent = ({ api }) => {
111
111
  onChange: handleFileSelect,
112
112
  id: "devxp-csv-upload"
113
113
  }
114
- ), /* @__PURE__ */ React.createElement("label", { htmlFor: "devxp-csv-upload" }, /* @__PURE__ */ React.createElement(Button, { variant: "outlined", component: "span" }, "Choose CSV File")), selectedFileName && /* @__PURE__ */ React.createElement(Typography, { className: classes.fileName, variant: "body2" }, selectedFileName), /* @__PURE__ */ React.createElement(
114
+ ), /* @__PURE__ */ React__default.createElement("label", { htmlFor: "devxp-csv-upload" }, /* @__PURE__ */ React__default.createElement(Button, { variant: "outlined", component: "span" }, "Choose CSV File")), selectedFileName && /* @__PURE__ */ React__default.createElement(Typography, { className: classes.fileName, variant: "body2" }, selectedFileName), /* @__PURE__ */ React__default.createElement(
115
115
  Button,
116
116
  {
117
117
  variant: "contained",
118
118
  color: "primary",
119
119
  onClick: handleUpload,
120
120
  disabled: !fileContent || uploading,
121
- startIcon: /* @__PURE__ */ React.createElement(CloudUploadIcon, null),
121
+ startIcon: /* @__PURE__ */ React__default.createElement(CloudUploadIcon, null),
122
122
  style: { marginLeft: 16 }
123
123
  },
124
124
  uploading ? "Processing..." : "Upload & Process"
125
- )), statusMessage && /* @__PURE__ */ React.createElement(
125
+ )), statusMessage && /* @__PURE__ */ React__default.createElement(
126
126
  Box,
127
127
  {
128
128
  className: `${classes.statusMessage} ${statusMessage.isError ? classes.error : classes.success}`
129
129
  },
130
- /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, statusMessage.text)
131
- ))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Developer Mappings" }, loading ? /* @__PURE__ */ React.createElement(Typography, null, "Loading mappings...") : mappings.length === 0 ? /* @__PURE__ */ React.createElement(Box, { className: classes.emptyState }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, "No developer mappings yet. Upload a CSV file or sync via GitHub above to get started.")) : /* @__PURE__ */ React.createElement(TableContainer, { component: Paper, variant: "outlined" }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Masked Name"), /* @__PURE__ */ React.createElement(TableCell, null, "Real Name"), /* @__PURE__ */ React.createElement(TableCell, null, "Created"), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React.createElement(TableBody, null, mappings.map((m) => /* @__PURE__ */ React.createElement(TableRow, { key: m.masked_name }, /* @__PURE__ */ React.createElement(TableCell, { className: classes.maskedCell }, m.masked_name), /* @__PURE__ */ React.createElement(TableCell, null, m.real_name), /* @__PURE__ */ React.createElement(TableCell, null, new Date(m.created_at).toLocaleDateString()), /* @__PURE__ */ React.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React.createElement(
130
+ /* @__PURE__ */ React__default.createElement(Typography, { variant: "body2" }, statusMessage.text)
131
+ ))), /* @__PURE__ */ React__default.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React__default.createElement(InfoCard, { title: "Developer Mappings" }, loading ? /* @__PURE__ */ React__default.createElement(Typography, null, "Loading mappings...") : mappings.length === 0 ? /* @__PURE__ */ React__default.createElement(Box, { className: classes.emptyState }, /* @__PURE__ */ React__default.createElement(Typography, { variant: "body1" }, "No developer mappings yet. Upload a CSV file or sync via GitHub above to get started.")) : /* @__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, "Masked Name"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Real Name"), /* @__PURE__ */ React__default.createElement(TableCell, null, "Created"), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React__default.createElement(TableBody, null, mappings.map((m) => /* @__PURE__ */ React__default.createElement(TableRow, { key: m.masked_name }, /* @__PURE__ */ React__default.createElement(TableCell, { className: classes.maskedCell }, m.masked_name), /* @__PURE__ */ React__default.createElement(TableCell, null, m.real_name), /* @__PURE__ */ React__default.createElement(TableCell, null, new Date(m.created_at).toLocaleDateString()), /* @__PURE__ */ React__default.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React__default.createElement(
132
132
  IconButton,
133
133
  {
134
134
  size: "small",
135
135
  onClick: () => handleDelete(m.masked_name),
136
136
  "aria-label": "Delete mapping"
137
137
  },
138
- /* @__PURE__ */ React.createElement(DeleteIcon, { fontSize: "small" })
138
+ /* @__PURE__ */ React__default.createElement(DeleteIcon, { fontSize: "small" })
139
139
  ))))))))));
140
140
  };
141
141