@burdenoff/microfe-adaptercloud 2026.706.1 → 2026.706.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adaptercloud/AdapterCloudRoot.js +52 -44
- package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
- package/dist/adaptercloud/AdapterCloudRoutes.js +22 -14
- package/dist/adaptercloud/AdapterCloudRoutes.js.map +1 -1
- package/dist/adaptercloud/components/ConfirmDialog.js +27 -0
- package/dist/adaptercloud/components/ConfirmDialog.js.map +1 -0
- package/dist/adaptercloud/components/DataState.js +1 -1
- package/dist/adaptercloud/components/DataState.js.map +1 -1
- package/dist/adaptercloud/components/viz/BarChart.js +32 -0
- package/dist/adaptercloud/components/viz/BarChart.js.map +1 -0
- package/dist/adaptercloud/components/viz/Sparkline.js +50 -0
- package/dist/adaptercloud/components/viz/Sparkline.js.map +1 -0
- package/dist/adaptercloud/components/viz/StackedBar.js +1 -0
- package/dist/adaptercloud/components/viz/TopologyGraph.js +96 -0
- package/dist/adaptercloud/components/viz/TopologyGraph.js.map +1 -0
- package/dist/adaptercloud/components/viz/index.js +4 -0
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js +71 -34
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
- package/dist/adaptercloud/pages/OverviewPage.js +159 -156
- package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js +179 -113
- package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdapterFormPage.js +263 -179
- package/dist/adaptercloud/pages/adapters/AdapterFormPage.js.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +2 -0
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +141 -124
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionFormPage.js +2 -0
- package/dist/adaptercloud/pages/connections/ConnectionFormPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +158 -93
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
- package/dist/adaptercloud/pages/costs/CostsPage.js +183 -109
- package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
- package/dist/adaptercloud/pages/drift/DriftListPage.js +172 -71
- package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js +179 -96
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +109 -91
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/policies/PolicyFormPage.js +211 -184
- package/dist/adaptercloud/pages/policies/PolicyFormPage.js.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +183 -127
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js +121 -84
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +236 -153
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
- package/dist/adaptercloud/pages/topology/TopologyPage.js +397 -0
- package/dist/adaptercloud/pages/topology/TopologyPage.js.map +1 -0
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js +192 -131
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../src/adaptercloud/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * Overview / Dashboard Page\n * @module adaptercloud/pages\n *\n * The AdapterCloud landing page: a single-pane view of the estate — fabric\n * counts (adapters, connections, resources), governance posture (open\n * violations, unremediated drift), operations (recent sync runs) and FinOps\n * (workspace cost summary by service). All data is live from the deployed\n * `wspace-adaptercloud-svc` subgraph.\n */\n\nimport { useMemo, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Boxes,\n Cable,\n Server,\n ShieldAlert,\n GitCompareArrows,\n RefreshCw,\n DollarSign,\n ArrowRight,\n Activity,\n AlertCircle,\n Plug,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { SeverityBadge } from '../components/SeverityBadge';\nimport { DataState } from '../components/DataState';\nimport { useAdapterCloud } from '../context/AdapterCloudContext';\nimport {\n useAdapters,\n useConnections,\n useResources,\n useViolations,\n useDriftRecords,\n useSyncRuns,\n useCostSummary,\n} from '../hooks/useAdapterCloudApi';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { formatMoney, severityRank, syncKindLabels } from '../utils/domain';\nimport type { AdaSeverity } from '../types';\n\nexport function OverviewPage() {\n const { navigateTo, currentUser } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n // Estate count tiles read item counts from the page length, so the page must\n // be large enough to be meaningful (limit:1 would cap every count at 1).\n const adapters = useAdapters({ pagination: { limit: 100 } });\n const connections = useConnections({ pagination: { limit: 100 } });\n const resources = useResources({ pagination: { limit: 100 } });\n const openViolations = useViolations({ status: 'OPEN', pagination: { limit: 50 } });\n const drift = useDriftRecords({ status: 'DETECTED' });\n const syncRuns = useSyncRuns({ pagination: { limit: 5 } });\n const costSummary = useCostSummary();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n // Connection health breakdown\n const connectionHealth = useMemo(() => {\n const items = connections.data?.items ?? [];\n const counts = { CONNECTED: 0, DEGRADED: 0, ERROR: 0, PENDING: 0, DISABLED: 0 };\n for (const c of items) counts[c.status] += 1;\n return counts;\n }, [connections.data]);\n\n const topViolations = useMemo(() => {\n return [...(openViolations.data?.items ?? [])]\n .sort((a, b) => severityRank[b.severity] - severityRank[a.severity])\n .slice(0, 5);\n }, [openViolations.data]);\n\n const recentRuns = syncRuns.data?.items ?? [];\n\n const topCostLines = useMemo(() => {\n return [...(costSummary.data?.lines ?? [])].sort((a, b) => b.total - a.total).slice(0, 6);\n }, [costSummary.data]);\n\n const isLoading =\n adapters.isLoading ||\n connections.isLoading ||\n resources.isLoading ||\n openViolations.isLoading ||\n drift.isLoading ||\n costSummary.isLoading;\n\n const isError =\n adapters.isError ||\n connections.isError ||\n resources.isError ||\n openViolations.isError ||\n drift.isError ||\n costSummary.isError;\n\n const refetchAll = useCallback(() => {\n void adapters.refetch();\n void connections.refetch();\n void resources.refetch();\n void openViolations.refetch();\n void drift.refetch();\n void syncRuns.refetch();\n void costSummary.refetch();\n }, [adapters, connections, resources, openViolations, drift, syncRuns, costSummary]);\n\n const stats = [\n {\n icon: <Boxes className=\"h-5 w-5\" />,\n value: adapters.data?.items.length\n ? String(adapters.data.items.length)\n : adapters.data\n ? '0'\n : '—',\n label: t('adaptercloud.overview.adapters', 'Adapters'),\n onClick: () => navigateTo('/adapters'),\n },\n {\n icon: <Cable className=\"h-5 w-5\" />,\n value: connections.data ? String(connections.data.items.length) : '—',\n label: t('adaptercloud.overview.connections', 'Connections'),\n onClick: () => navigateTo('/connections'),\n },\n {\n icon: <Server className=\"h-5 w-5\" />,\n value: resources.data ? String(resources.data.items.length) : '—',\n label: t('adaptercloud.overview.resources', 'Resources'),\n onClick: () => navigateTo('/resources'),\n },\n {\n icon: <ShieldAlert className=\"h-5 w-5\" />,\n value: openViolations.data ? String(openViolations.data.items.length) : '—',\n label: t('adaptercloud.overview.openViolations', 'Open Violations'),\n onClick: () => navigateTo('/violations'),\n },\n ];\n\n return (\n <PageLayout\n title={`${t('adaptercloud.overview.hello', 'Welcome back')}, ${firstName}`}\n description={t(\n 'adaptercloud.overview.subtitle',\n 'Your infrastructure continuum at a glance — fabric, governance, operations and cost.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={refetchAll}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n {t('adaptercloud.common.refresh', 'Refresh')}\n </button>\n }\n >\n <PagePurpose className=\"mb-6\">\n {t(\n 'adaptercloud.overview.purpose',\n 'AdapterCloud connects your fragmented infrastructure — multi-cloud, on-prem, edge and SaaS — into one governable fabric. This overview is your control plane: see what you run, how connected it is, what is out of policy, and what it costs.'\n )}\n </PagePurpose>\n\n <DataState\n isLoading={isLoading}\n isError={isError}\n error={adapters.error}\n onRetry={refetchAll}\n >\n {/* Top metrics — the one emphasis zone */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.estate', 'Estate Summary')}\n </h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {stats.map((stat) => (\n <button key={stat.label} type=\"button\" onClick={stat.onClick} className=\"text-left\">\n <MetricsCard icon={stat.icon} value={stat.value} label={stat.label} />\n </button>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Connection health + Cost summary */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Connection health */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.connectionHealth', 'Connection Health')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard className=\"p-5\">\n {connections.data && connections.data.items.length > 0 ? (\n <ul className=\"space-y-3\">\n {(\n [\n ['CONNECTED', connectionHealth.CONNECTED],\n ['DEGRADED', connectionHealth.DEGRADED],\n ['ERROR', connectionHealth.ERROR],\n ['PENDING', connectionHealth.PENDING],\n ['DISABLED', connectionHealth.DISABLED],\n ] as const\n ).map(([status, count]) => (\n <li key={status} className=\"flex items-center justify-between\">\n <StatusBadge status={status} />\n <span className=\"text-sm font-semibold tabular-nums text-text-primary\">\n {count}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"flex flex-col items-center gap-3 py-6 text-center\">\n <Plug className=\"h-8 w-8 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noConnections',\n 'No connections yet. Connect a target to start discovering resources.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90\"\n >\n {t('adaptercloud.overview.addConnection', 'Add connection')}\n </button>\n </div>\n )}\n </GlassCard>\n </section>\n\n {/* Cost summary */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.costByService', 'Cost by Service')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/costs')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.overview.finops', 'FinOps')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard className=\"p-5\">\n <div className=\"mb-4 flex items-baseline gap-2\">\n <DollarSign className=\"h-5 w-5 text-text-secondary\" />\n <span className=\"text-2xl font-bold tabular-nums text-text-primary\">\n {costSummary.data\n ? formatMoney(costSummary.data.total, costSummary.data.currency)\n : '—'}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.totalSpend', 'total spend')}\n </span>\n </div>\n {topCostLines.length > 0 ? (\n <ul className=\"space-y-2\">\n {topCostLines.map((line) => (\n <li key={line.service} className=\"flex items-center justify-between text-sm\">\n <span className=\"truncate text-text-primary\">{line.service}</span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatMoney(line.total, costSummary.data?.currency ?? 'USD')}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"py-4 text-center text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noCost',\n 'No cost records ingested yet. Run a cost-ingest sync to populate FinOps.'\n )}\n </p>\n )}\n </GlassCard>\n </section>\n </div>\n\n {/* Governance + Operations */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Top open violations */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.priorityViolations', 'Priority Violations')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.overview.queue', 'Queue')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {topViolations.length > 0 ? (\n topViolations.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <AlertCircle className=\"mt-0.5 h-4 w-4 shrink-0 text-status-error-text\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{v.message}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {v.policy?.name ?? t('adaptercloud.overview.policy', 'Policy')} ·{' '}\n {formatRelativeTime(v.detectedAt)}\n </p>\n </div>\n <SeverityBadge severity={v.severity as AdaSeverity} />\n </button>\n ))\n ) : (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <ShieldAlert className=\"h-8 w-8 text-status-success-text\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noViolations',\n 'No open violations. Estate is clean.'\n )}\n </p>\n </div>\n )}\n </div>\n {drift.data && drift.data.length > 0 && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/drift')}\n className=\"mt-3 flex w-full items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-4 py-3 text-left text-sm transition-colors hover:bg-accent/50\"\n >\n <GitCompareArrows className=\"h-4 w-4 text-status-warning-text\" />\n <span className=\"text-text-primary\">\n {drift.data.length}{' '}\n {t('adaptercloud.overview.driftDetected', 'drift records detected')}\n </span>\n <ArrowRight className=\"ml-auto h-3.5 w-3.5 text-text-secondary\" />\n </button>\n )}\n </section>\n\n {/* Recent sync runs */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.recentRuns', 'Recent Sync Runs')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/sync-runs')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {recentRuns.length > 0 ? (\n recentRuns.map((run) => (\n <button\n key={run.id}\n type=\"button\"\n onClick={() => navigateTo('/sync-runs')}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken\">\n <Activity className=\"h-4 w-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {syncKindLabels[run.kind]}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {run.discoveredCount} {t('adaptercloud.overview.discovered', 'discovered')}{' '}\n · {formatRelativeTime(run.createdAt)}\n </p>\n </div>\n <StatusBadge status={run.status} />\n </button>\n ))\n ) : (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <RefreshCw className=\"h-8 w-8 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.noRuns', 'No sync runs yet.')}\n </p>\n </div>\n )}\n </div>\n </section>\n </div>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA8CA,SAAgB,IAAe;CAC7B,IAAM,EAAE,eAAY,mBAAgB,GAAiB,EAC/C,EAAE,GAAG,MAAc,GAAS,EAC5B,IAAI,GACP,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAU,CACZ,EAIK,IAAW,EAAY,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EACtD,IAAc,EAAe,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EAC5D,IAAY,EAAa,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,CAAC,EACxD,IAAiB,EAAc;EAAE,QAAQ;EAAQ,YAAY,EAAE,OAAO,IAAI;EAAE,CAAC,EAC7E,IAAQ,EAAgB,EAAE,QAAQ,YAAY,CAAC,EAC/C,IAAW,EAAY,EAAE,YAAY,EAAE,OAAO,GAAG,EAAE,CAAC,EACpD,IAAc,GAAgB,EAE9B,IAAY,GAAa,aAAa,GAAa,QAAQ,SAG3D,IAAmB,QAAc;EACrC,IAAM,IAAQ,EAAY,MAAM,SAAS,EAAE,EACrC,IAAS;GAAE,WAAW;GAAG,UAAU;GAAG,OAAO;GAAG,SAAS;GAAG,UAAU;GAAG;AAC/E,OAAK,IAAM,KAAK,EAAO,GAAO,EAAE,WAAW;AAC3C,SAAO;IACN,CAAC,EAAY,KAAK,CAAC,EAEhB,IAAgB,QACb,CAAC,GAAI,EAAe,MAAM,SAAS,EAAE,CAAE,CAC3C,MAAM,GAAG,MAAM,EAAa,EAAE,YAAY,EAAa,EAAE,UAAU,CACnE,MAAM,GAAG,EAAE,EACb,CAAC,EAAe,KAAK,CAAC,EAEnB,IAAa,EAAS,MAAM,SAAS,EAAE,EAEvC,IAAe,QACZ,CAAC,GAAI,EAAY,MAAM,SAAS,EAAE,CAAE,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG,EAAE,EACxF,CAAC,EAAY,KAAK,CAAC,EAEhB,IACJ,EAAS,aACT,EAAY,aACZ,EAAU,aACV,EAAe,aACf,EAAM,aACN,EAAY,WAER,IACJ,EAAS,WACT,EAAY,WACZ,EAAU,WACV,EAAe,WACf,EAAM,WACN,EAAY,SAER,IAAa,QAAkB;AAO9B,EANA,EAAS,SAAS,EAClB,EAAY,SAAS,EACrB,EAAU,SAAS,EACnB,EAAe,SAAS,EACxB,EAAM,SAAS,EACf,EAAS,SAAS,EAClB,EAAY,SAAS;IACzB;EAAC;EAAU;EAAa;EAAW;EAAgB;EAAO;EAAU;EAAY,CAAC,EAE9E,KAAQ;EACZ;GACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;GACnC,OAAO,EAAS,MAAM,MAAM,SACxB,OAAO,EAAS,KAAK,MAAM,OAAO,GAClC,EAAS,OACP,MACA;GACN,OAAO,EAAE,kCAAkC,WAAW;GACtD,eAAe,EAAW,YAAY;GACvC;EACD;GACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;GACnC,OAAO,EAAY,OAAO,OAAO,EAAY,KAAK,MAAM,OAAO,GAAG;GAClE,OAAO,EAAE,qCAAqC,cAAc;GAC5D,eAAe,EAAW,eAAe;GAC1C;EACD;GACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;GACpC,OAAO,EAAU,OAAO,OAAO,EAAU,KAAK,MAAM,OAAO,GAAG;GAC9D,OAAO,EAAE,mCAAmC,YAAY;GACxD,eAAe,EAAW,aAAa;GACxC;EACD;GACE,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;GACzC,OAAO,EAAe,OAAO,OAAO,EAAe,KAAK,MAAM,OAAO,GAAG;GACxE,OAAO,EAAE,wCAAwC,kBAAkB;GACnE,eAAe,EAAW,cAAc;GACzC;EACF;AAED,QACE,kBAAC,GAAD;EACE,OAAO,GAAG,EAAE,+BAA+B,eAAe,CAAC,IAAI;EAC/D,aAAa,EACX,kCACA,uFACD;EACD,SACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EAAE,+BAA+B,UAAU,CACrC;;YAdb,CAiBE,kBAAC,GAAD;GAAa,WAAU;aACpB,EACC,iCACA,iPACD;GACW,CAAA,EAEd,kBAAC,GAAD;GACa;GACF;GACT,OAAO,EAAS;GAChB,SAAS;aAJX;IAOE,kBAAC,GAAD;KAAe,WAAU;eAAzB,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAE,gCAAgC,iBAAiB;MACjD,CAAA,EACL,kBAAC,OAAD;MAAK,WAAU;gBACZ,GAAM,KAAK,MACV,kBAAC,UAAD;OAAyB,MAAK;OAAS,SAAS,EAAK;OAAS,WAAU;iBACtE,kBAAC,GAAD;QAAa,MAAM,EAAK;QAAM,OAAO,EAAK;QAAO,OAAO,EAAK;QAAS,CAAA;OAC/D,EAFI,EAAK,MAET,CACT;MACE,CAAA,CACQ;;IAGhB,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAE,0CAA0C,oBAAoB;OAC9D,CAAA,EACL,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,eAAe;OACzC,WAAU;iBAHZ;QAKG,EAAE,+BAA+B,WAAW;QAAE;QAC/C,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;QAC/B;SACL;SACN,kBAAC,GAAD;MAAW,WAAU;gBAClB,EAAY,QAAQ,EAAY,KAAK,MAAM,SAAS,IACnD,kBAAC,MAAD;OAAI,WAAU;iBAEV;QACE,CAAC,aAAa,EAAiB,UAAU;QACzC,CAAC,YAAY,EAAiB,SAAS;QACvC,CAAC,SAAS,EAAiB,MAAM;QACjC,CAAC,WAAW,EAAiB,QAAQ;QACrC,CAAC,YAAY,EAAiB,SAAS;QACxC,CACD,KAAK,CAAC,GAAQ,OACd,kBAAC,MAAD;QAAiB,WAAU;kBAA3B,CACE,kBAAC,GAAD,EAAqB,WAAU,CAAA,EAC/B,kBAAC,QAAD;SAAM,WAAU;mBACb;SACI,CAAA,CACJ;UALI,EAKJ,CACL;OACC,CAAA,GAEL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAM,WAAU,+BAAgC,CAAA;QAChD,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,uCACA,uEACD;SACC,CAAA;QACJ,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAW,mBAAmB;SAC7C,WAAU;mBAET,EAAE,uCAAuC,iBAAiB;SACpD,CAAA;QACL;;MAEE,CAAA,CACJ,EAAA,CAAA,EAGV,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAE,uCAAuC,kBAAkB;OACzD,CAAA,EACL,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,SAAS;OACnC,WAAU;iBAHZ;QAKG,EAAE,gCAAgC,SAAS;QAAC;QAAC,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;QAC7E;SACL;SACN,kBAAC,GAAD;MAAW,WAAU;gBAArB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAY,WAAU,+BAAgC,CAAA;QACtD,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAY,OACT,EAAY,EAAY,KAAK,OAAO,EAAY,KAAK,SAAS,GAC9D;SACC,CAAA;QACP,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAE,oCAAoC,cAAc;SAChD,CAAA;QACH;UACL,EAAa,SAAS,IACrB,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAa,KAAK,MACjB,kBAAC,MAAD;QAAuB,WAAU;kBAAjC,CACE,kBAAC,QAAD;SAAM,WAAU;mBAA8B,EAAK;SAAe,CAAA,EAClE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAY,EAAK,OAAO,EAAY,MAAM,YAAY,MAAM;SACxD,CAAA,CACJ;UALI,EAAK,QAKT,CACL;OACC,CAAA,GAEL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,gCACA,2EACD;OACC,CAAA,CAEI;QACJ,EAAA,CAAA,CACN;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,WAAD,EAAA,UAAA;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAE,4CAA4C,sBAAsB;QAClE,CAAA,EACL,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAW,cAAc;QACxC,WAAU;kBAHZ;SAKG,EAAE,+BAA+B,QAAQ;SAAC;SAAC,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;SAC3E;UACL;;MACN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAc,SAAS,IACtB,EAAc,KAAK,MACjB,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAW,cAAc;QACxC,WAAU;kBAJZ;SAME,kBAAC,GAAD,EAAa,WAAU,kDAAmD,CAAA;SAC1E,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAkD,EAAE;WAAY,CAAA,EAC7E,kBAAC,KAAD;WAAG,WAAU;qBAAb;YACG,EAAE,QAAQ,QAAQ,EAAE,gCAAgC,SAAS;YAAC;YAAG;YACjE,EAAmB,EAAE,WAAW;YAC/B;aACA;;SACN,kBAAC,GAAD,EAAe,UAAU,EAAE,UAA2B,CAAA;SAC/C;UAdF,EAAE,GAcA,CACT,GAEF,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,oCAAqC,CAAA,EAC5D,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,sCACA,uCACD;SACC,CAAA,CACA;;OAEJ,CAAA;MACL,EAAM,QAAQ,EAAM,KAAK,SAAS,KACjC,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,SAAS;OACnC,WAAU;iBAHZ;QAKE,kBAAC,GAAD,EAAkB,WAAU,oCAAqC,CAAA;QACjE,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACG,EAAM,KAAK;UAAQ;UACnB,EAAE,uCAAuC,yBAAyB;UAC9D;;QACP,kBAAC,GAAD,EAAY,WAAU,2CAA4C,CAAA;QAC3D;;MAEH,EAAA,CAAA,EAGV,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAE,oCAAoC,mBAAmB;OACvD,CAAA,EACL,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,aAAa;OACvC,WAAU;iBAHZ;QAKG,EAAE,+BAA+B,WAAW;QAAE;QAC/C,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;QAC/B;SACL;SACN,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAW,SAAS,IACnB,EAAW,KAAK,MACd,kBAAC,UAAD;OAEE,MAAK;OACL,eAAe,EAAW,aAAa;OACvC,WAAU;iBAJZ;QAME,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAU,WAAU,+BAAgC,CAAA;SAChD,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBACV,GAAe,EAAI;UAClB,CAAA,EACJ,kBAAC,KAAD;UAAG,WAAU;oBAAb;WACG,EAAI;WAAgB;WAAE,EAAE,oCAAoC,aAAa;WAAE;WAAI;WAC7E,EAAmB,EAAI,UAAU;WAClC;YACA;;QACN,kBAAC,GAAD,EAAa,QAAQ,EAAI,QAAU,CAAA;QAC5B;SAlBF,EAAI,GAkBF,CACT,GAEF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,+BAAgC,CAAA,EACrD,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAE,gCAAgC,oBAAoB;QACrD,CAAA,CACA;;MAEJ,CAAA,CACE,EAAA,CAAA,CACN;;IACI;KACD"}
|
|
1
|
+
{"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../src/adaptercloud/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * Overview / Dashboard Page\n * @module adaptercloud/pages\n *\n * The AdapterCloud landing page: a single-pane view of the estate — fabric\n * counts (adapters, connections, resources), governance posture (open\n * violations, unremediated drift), operations (recent sync runs) and FinOps\n * (workspace cost summary by service). All data is live from the deployed\n * `wspace-adaptercloud-svc` subgraph.\n */\n\nimport { useMemo, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Boxes,\n Cable,\n Server,\n ShieldAlert,\n GitCompareArrows,\n RefreshCw,\n DollarSign,\n ArrowRight,\n Activity,\n AlertCircle,\n Clock,\n Plug,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { SeverityBadge } from '../components/SeverityBadge';\nimport { DataState } from '../components/DataState';\nimport { StackedBar, BarChart, Sparkline } from '../components/viz';\nimport { useAdapterCloud } from '../context/AdapterCloudContext';\nimport {\n useAdapters,\n useConnections,\n useResources,\n useViolations,\n useDriftRecords,\n useSyncRuns,\n useCostSummary,\n useCostRecords,\n} from '../hooks/useAdapterCloudApi';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { formatMoney, severityRank, syncKindLabels } from '../utils/domain';\nimport type { AdaPageInfo, AdaSeverity } from '../types';\n\n/**\n * Estate count for a tile: prefer the estate-wide `pageInfo.totalCount`, fall\n * back to the loaded page length when the field is absent (null/undefined), and\n * render an em dash before the first page has loaded.\n */\nfunction countLabel(page: { items: unknown[]; pageInfo: AdaPageInfo } | undefined): string {\n if (!page) return '—';\n return String(page.pageInfo.totalCount ?? page.items.length);\n}\n\n/**\n * Bucket cost records into an ascending monthly series (sum of `amount` per\n * calendar month keyed by `periodStart`). Feeds the cost-trend sparkline.\n */\nfunction monthlyCostSeries(records: { periodStart: string; amount: number }[]): number[] {\n const buckets = new Map<string, number>();\n for (const r of records) {\n const d = new Date(r.periodStart);\n if (Number.isNaN(d.getTime())) continue;\n const key = `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}`;\n buckets.set(key, (buckets.get(key) ?? 0) + r.amount);\n }\n return [...buckets.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([, total]) => total);\n}\n\n/**\n * Human-readable run duration (\"3m 12s\", \"1h 05m\") from a run's start/finish\n * timestamps. Returns null when either bound is missing or invalid (e.g. a\n * QUEUED/RUNNING run that has not finished).\n */\nfunction formatRunDuration(startedAt: string | null, finishedAt: string | null): string | null {\n if (!startedAt || !finishedAt) return null;\n const start = new Date(startedAt).getTime();\n const end = new Date(finishedAt).getTime();\n if (Number.isNaN(start) || Number.isNaN(end) || end < start) return null;\n const totalSeconds = Math.round((end - start) / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n if (hours > 0) return `${hours}h ${String(minutes).padStart(2, '0')}m`;\n if (minutes > 0) return `${minutes}m ${String(seconds).padStart(2, '0')}s`;\n return `${seconds}s`;\n}\n\nexport function OverviewPage() {\n const { navigateTo, currentUser } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n // Estate count tiles prefer the estate-wide `pageInfo.totalCount` (svc-added)\n // and fall back to the loaded page length when it is absent, so the request\n // limit still bounds the fallback (limit:1 would cap the fallback at 1).\n const adapters = useAdapters({ pagination: { limit: 100 } });\n const connections = useConnections({ pagination: { limit: 100 } });\n const resources = useResources({ pagination: { limit: 100 } });\n const openViolations = useViolations({ status: 'OPEN', pagination: { limit: 50 } });\n const drift = useDriftRecords({ status: 'DETECTED' });\n const syncRuns = useSyncRuns({ pagination: { limit: 5 } });\n const costSummary = useCostSummary();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n // Connection health breakdown\n const connectionHealth = useMemo(() => {\n const items = connections.data?.items ?? [];\n const counts = { CONNECTED: 0, DEGRADED: 0, ERROR: 0, PENDING: 0, DISABLED: 0 };\n for (const c of items) counts[c.status] += 1;\n return counts;\n }, [connections.data]);\n\n const topViolations = useMemo(() => {\n return [...(openViolations.data?.items ?? [])]\n .sort((a, b) => severityRank[b.severity] - severityRank[a.severity])\n .slice(0, 5);\n }, [openViolations.data]);\n\n const recentRuns = syncRuns.data?.items ?? [];\n\n const topCostLines = useMemo(() => {\n return [...(costSummary.data?.lines ?? [])].sort((a, b) => b.total - a.total).slice(0, 6);\n }, [costSummary.data]);\n\n const isLoading =\n adapters.isLoading ||\n connections.isLoading ||\n resources.isLoading ||\n openViolations.isLoading ||\n drift.isLoading ||\n costSummary.isLoading;\n\n const isError =\n adapters.isError ||\n connections.isError ||\n resources.isError ||\n openViolations.isError ||\n drift.isError ||\n costSummary.isError;\n\n const refetchAll = useCallback(() => {\n void adapters.refetch();\n void connections.refetch();\n void resources.refetch();\n void openViolations.refetch();\n void drift.refetch();\n void syncRuns.refetch();\n void costSummary.refetch();\n }, [adapters, connections, resources, openViolations, drift, syncRuns, costSummary]);\n\n const stats = [\n {\n icon: <Boxes className=\"h-5 w-5\" />,\n value: countLabel(adapters.data),\n label: t('adaptercloud.overview.adapters', 'Adapters'),\n onClick: () => navigateTo('/adapters'),\n },\n {\n icon: <Cable className=\"h-5 w-5\" />,\n value: countLabel(connections.data),\n label: t('adaptercloud.overview.connections', 'Connections'),\n onClick: () => navigateTo('/connections'),\n },\n {\n icon: <Server className=\"h-5 w-5\" />,\n value: countLabel(resources.data),\n label: t('adaptercloud.overview.resources', 'Resources'),\n onClick: () => navigateTo('/resources'),\n },\n {\n icon: <ShieldAlert className=\"h-5 w-5\" />,\n value: countLabel(openViolations.data),\n label: t('adaptercloud.overview.openViolations', 'Open Violations'),\n onClick: () => navigateTo('/violations'),\n },\n ];\n\n return (\n <PageLayout\n title={`${t('adaptercloud.overview.hello', 'Welcome back')}, ${firstName}`}\n description={t(\n 'adaptercloud.overview.subtitle',\n 'Your infrastructure continuum at a glance — fabric, governance, operations and cost.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={refetchAll}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n {t('adaptercloud.common.refresh', 'Refresh')}\n </button>\n }\n >\n <PagePurpose className=\"mb-6\">\n {t(\n 'adaptercloud.overview.purpose',\n 'AdapterCloud connects your fragmented infrastructure — multi-cloud, on-prem, edge and SaaS — into one governable fabric. This overview is your control plane: see what you run, how connected it is, what is out of policy, and what it costs.'\n )}\n </PagePurpose>\n\n <DataState\n isLoading={isLoading}\n isError={isError}\n error={adapters.error}\n onRetry={refetchAll}\n >\n {/* Top metrics — the one emphasis zone */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.estate', 'Estate Summary')}\n </h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {stats.map((stat) => (\n <button key={stat.label} type=\"button\" onClick={stat.onClick} className=\"text-left\">\n <MetricsCard icon={stat.icon} value={stat.value} label={stat.label} />\n </button>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Connection health + Cost summary */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Connection health */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.connectionHealth', 'Connection Health')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections')}\n className=\"flex items-center gap-1 text-sm font-medium text-accent-blue hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard className=\"p-5\">\n {connections.data && connections.data.items.length > 0 ? (\n <ul className=\"space-y-3\">\n {(\n [\n ['CONNECTED', connectionHealth.CONNECTED],\n ['DEGRADED', connectionHealth.DEGRADED],\n ['ERROR', connectionHealth.ERROR],\n ['PENDING', connectionHealth.PENDING],\n ['DISABLED', connectionHealth.DISABLED],\n ] as const\n ).map(([status, count]) => (\n <li key={status} className=\"flex items-center justify-between\">\n <StatusBadge status={status} />\n <span className=\"text-sm font-semibold tabular-nums text-text-primary\">\n {count}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div className=\"flex flex-col items-center gap-3 py-6 text-center\">\n <Plug className=\"h-8 w-8 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noConnections',\n 'No connections yet. Connect a target to start discovering resources.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90\"\n >\n {t('adaptercloud.overview.addConnection', 'Add connection')}\n </button>\n </div>\n )}\n </GlassCard>\n </section>\n\n {/* Cost summary */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.costByService', 'Cost by Service')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/costs')}\n className=\"flex items-center gap-1 text-sm font-medium text-accent-blue hover:underline\"\n >\n {t('adaptercloud.overview.finops', 'FinOps')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <GlassCard className=\"p-5\">\n <div className=\"mb-4 flex items-baseline gap-2\">\n <DollarSign className=\"h-5 w-5 text-text-secondary\" />\n <span className=\"text-2xl font-bold tabular-nums text-text-primary\">\n {costSummary.data\n ? formatMoney(costSummary.data.total, costSummary.data.currency)\n : '—'}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.totalSpend', 'total spend')}\n </span>\n </div>\n {topCostLines.length > 0 ? (\n <ul className=\"space-y-2\">\n {topCostLines.map((line) => (\n <li key={line.service} className=\"flex items-center justify-between text-sm\">\n <span className=\"truncate text-text-primary\">{line.service}</span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatMoney(line.total, costSummary.data?.currency ?? 'USD')}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"py-4 text-center text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noCost',\n 'No cost records ingested yet. Run a cost-ingest sync to populate FinOps.'\n )}\n </p>\n )}\n </GlassCard>\n </section>\n </div>\n\n {/* Governance + Operations */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Top open violations */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.priorityViolations', 'Priority Violations')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"flex items-center gap-1 text-sm font-medium text-accent-blue hover:underline\"\n >\n {t('adaptercloud.overview.queue', 'Queue')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {topViolations.length > 0 ? (\n topViolations.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <AlertCircle className=\"mt-0.5 h-4 w-4 shrink-0 text-status-error-text\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{v.message}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {v.policy?.name ?? t('adaptercloud.overview.policy', 'Policy')} ·{' '}\n {formatRelativeTime(v.detectedAt)}\n </p>\n </div>\n <SeverityBadge severity={v.severity as AdaSeverity} />\n </button>\n ))\n ) : (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <ShieldAlert className=\"h-8 w-8 text-status-success-text\" />\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'adaptercloud.overview.noViolations',\n 'No open violations. Estate is clean.'\n )}\n </p>\n </div>\n )}\n </div>\n {drift.data && drift.data.length > 0 && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/drift')}\n className=\"mt-3 flex w-full items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-4 py-3 text-left text-sm transition-colors hover:bg-accent/50\"\n >\n <GitCompareArrows className=\"h-4 w-4 text-status-warning-text\" />\n <span className=\"text-text-primary\">\n {drift.data.length}{' '}\n {t('adaptercloud.overview.driftDetected', 'drift records detected')}\n </span>\n <ArrowRight className=\"ml-auto h-3.5 w-3.5 text-text-secondary\" />\n </button>\n )}\n </section>\n\n {/* Recent sync runs */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('adaptercloud.overview.recentRuns', 'Recent Sync Runs')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/sync-runs')}\n className=\"flex items-center gap-1 text-sm font-medium text-accent-blue hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {recentRuns.length > 0 ? (\n recentRuns.map((run) => (\n <button\n key={run.id}\n type=\"button\"\n onClick={() => navigateTo('/sync-runs')}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken\">\n <Activity className=\"h-4 w-4 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {syncKindLabels[run.kind]}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {run.discoveredCount} {t('adaptercloud.overview.discovered', 'discovered')}{' '}\n · {formatRelativeTime(run.createdAt)}\n </p>\n </div>\n <StatusBadge status={run.status} />\n </button>\n ))\n ) : (\n <div className=\"flex flex-col items-center gap-2 px-4 py-8 text-center\">\n <RefreshCw className=\"h-8 w-8 text-text-secondary\" />\n <p className=\"text-sm text-text-secondary\">\n {t('adaptercloud.overview.noRuns', 'No sync runs yet.')}\n </p>\n </div>\n )}\n </div>\n </section>\n </div>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,130 +1,139 @@
|
|
|
1
1
|
import { useAdapterCloud as e } from "../../context/AdapterCloudContext.js";
|
|
2
2
|
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
-
import { adapterKindLabels as n
|
|
4
|
-
import { StatusBadge as
|
|
5
|
-
import { DataState as
|
|
6
|
-
import { useAdapter as
|
|
7
|
-
import { formatDate as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
3
|
+
import { adapterKindLabels as n } from "../../utils/domain.js";
|
|
4
|
+
import { StatusBadge as r } from "../../components/StatusBadge.js";
|
|
5
|
+
import { DataState as i } from "../../components/DataState.js";
|
|
6
|
+
import { useAdapter as a, useArchiveAdapter as o } from "../../hooks/useAdapterCloudApi.js";
|
|
7
|
+
import { formatDate as s } from "../../utils/formatters.js";
|
|
8
|
+
import { ConfirmDialog as c } from "../../components/ConfirmDialog.js";
|
|
9
|
+
import { useCallback as l, useState as u } from "react";
|
|
10
|
+
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
|
+
import { Archive as f, ArrowLeft as p, Cable as m, Check as h, Copy as g, Pencil as _ } from "lucide-react";
|
|
12
|
+
import { useParams as v } from "react-router-dom";
|
|
13
|
+
import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
14
|
+
import { GlassCard as S } from "@burdenoff/fe-libs/ui";
|
|
14
15
|
//#region src/adaptercloud/pages/adapters/AdapterDetailPage.tsx
|
|
15
|
-
function
|
|
16
|
-
let { id:
|
|
17
|
-
let n =
|
|
16
|
+
function C() {
|
|
17
|
+
let { id: C } = v(), { navigateTo: w } = e(), { t: T } = d(), E = l((e, t) => {
|
|
18
|
+
let n = T(e);
|
|
18
19
|
return n === e ? t : n;
|
|
19
|
-
}, [
|
|
20
|
-
|
|
20
|
+
}, [T]), D = a(C), O = o(), k = D.data, [A, j] = u(!1), [M, N] = u(!1), P = l(() => {
|
|
21
|
+
if (k?.configSchema == null) return;
|
|
22
|
+
let e = JSON.stringify(k.configSchema, null, 2);
|
|
23
|
+
navigator.clipboard?.writeText(e).then(() => {
|
|
24
|
+
N(!0), setTimeout(() => N(!1), 1500);
|
|
25
|
+
}).catch(() => {});
|
|
26
|
+
}, [k?.configSchema]), F = l(() => {
|
|
27
|
+
C && O.mutate(C, { onSuccess: () => {
|
|
28
|
+
j(!1), w("/adapters");
|
|
29
|
+
} });
|
|
21
30
|
}, [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
O,
|
|
32
|
+
C,
|
|
33
|
+
w
|
|
25
34
|
]);
|
|
26
|
-
return /* @__PURE__ */
|
|
27
|
-
title:
|
|
28
|
-
description:
|
|
29
|
-
actions: /* @__PURE__ */
|
|
35
|
+
return /* @__PURE__ */ x(t, {
|
|
36
|
+
title: k?.name ?? E("adaptercloud.adapters.detailTitle", "Adapter"),
|
|
37
|
+
description: k?.slug,
|
|
38
|
+
actions: /* @__PURE__ */ x("div", {
|
|
30
39
|
className: "flex items-center gap-2",
|
|
31
|
-
children: [/* @__PURE__ */
|
|
40
|
+
children: [/* @__PURE__ */ x("button", {
|
|
32
41
|
type: "button",
|
|
33
|
-
onClick: () =>
|
|
42
|
+
onClick: () => w("/adapters"),
|
|
34
43
|
className: "inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent",
|
|
35
|
-
children: [/* @__PURE__ */
|
|
36
|
-
}),
|
|
44
|
+
children: [/* @__PURE__ */ b(p, { className: "h-4 w-4" }), E("adaptercloud.common.back", "Back")]
|
|
45
|
+
}), k && /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ x("button", {
|
|
37
46
|
type: "button",
|
|
38
|
-
onClick: () =>
|
|
47
|
+
onClick: () => w(`/adapters/${k.id}/edit`),
|
|
39
48
|
className: "inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent",
|
|
40
|
-
children: [/* @__PURE__ */ _
|
|
41
|
-
}),
|
|
49
|
+
children: [/* @__PURE__ */ b(_, { className: "h-4 w-4" }), E("adaptercloud.common.edit", "Edit")]
|
|
50
|
+
}), k.status !== "ARCHIVED" && /* @__PURE__ */ x("button", {
|
|
42
51
|
type: "button",
|
|
43
|
-
onClick:
|
|
44
|
-
disabled:
|
|
52
|
+
onClick: () => j(!0),
|
|
53
|
+
disabled: O.isPending,
|
|
45
54
|
className: "inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50",
|
|
46
|
-
children: [/* @__PURE__ */
|
|
55
|
+
children: [/* @__PURE__ */ b(f, { className: "h-4 w-4" }), E("adaptercloud.common.archive", "Archive")]
|
|
47
56
|
})] })]
|
|
48
57
|
}),
|
|
49
|
-
children: /* @__PURE__ */
|
|
50
|
-
isLoading:
|
|
51
|
-
isError:
|
|
52
|
-
isEmpty: !
|
|
53
|
-
error:
|
|
54
|
-
onRetry: () => void
|
|
55
|
-
emptyTitle:
|
|
56
|
-
emptyDescription:
|
|
57
|
-
children:
|
|
58
|
+
children: [/* @__PURE__ */ b(i, {
|
|
59
|
+
isLoading: D.isLoading,
|
|
60
|
+
isError: D.isError,
|
|
61
|
+
isEmpty: !D.isLoading && !k,
|
|
62
|
+
error: D.error,
|
|
63
|
+
onRetry: () => void D.refetch(),
|
|
64
|
+
emptyTitle: E("adaptercloud.adapters.notFound", "Adapter not found"),
|
|
65
|
+
emptyDescription: E("adaptercloud.adapters.notFoundDescription", "This adapter does not exist or belongs to another workspace."),
|
|
66
|
+
children: k && /* @__PURE__ */ x("div", {
|
|
58
67
|
className: "space-y-6",
|
|
59
68
|
children: [
|
|
60
|
-
/* @__PURE__ */
|
|
69
|
+
/* @__PURE__ */ x(S, {
|
|
61
70
|
className: "p-6",
|
|
62
71
|
children: [
|
|
63
|
-
/* @__PURE__ */
|
|
72
|
+
/* @__PURE__ */ x("div", {
|
|
64
73
|
className: "mb-4 flex items-center gap-3",
|
|
65
74
|
children: [
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
/* @__PURE__ */
|
|
75
|
+
/* @__PURE__ */ b(r, { status: k.status }),
|
|
76
|
+
/* @__PURE__ */ b("span", {
|
|
68
77
|
className: "inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
|
|
69
|
-
children: n[
|
|
78
|
+
children: n[k.kind]
|
|
70
79
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
80
|
+
/* @__PURE__ */ x("span", {
|
|
72
81
|
className: "text-xs text-text-secondary",
|
|
73
|
-
children: ["v",
|
|
82
|
+
children: ["v", k.version]
|
|
74
83
|
})
|
|
75
84
|
]
|
|
76
85
|
}),
|
|
77
|
-
|
|
86
|
+
k.description && /* @__PURE__ */ b("p", {
|
|
78
87
|
className: "mb-4 text-sm text-text-secondary",
|
|
79
|
-
children:
|
|
88
|
+
children: k.description
|
|
80
89
|
}),
|
|
81
|
-
/* @__PURE__ */
|
|
90
|
+
/* @__PURE__ */ x("dl", {
|
|
82
91
|
className: "grid grid-cols-2 gap-4 text-sm sm:grid-cols-3",
|
|
83
92
|
children: [
|
|
84
|
-
/* @__PURE__ */
|
|
93
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
85
94
|
className: "text-text-secondary",
|
|
86
|
-
children:
|
|
87
|
-
}), /* @__PURE__ */
|
|
95
|
+
children: E("adaptercloud.adapters.vendor", "Vendor")
|
|
96
|
+
}), /* @__PURE__ */ b("dd", {
|
|
88
97
|
className: "mt-0.5 font-medium text-text-primary",
|
|
89
|
-
children:
|
|
98
|
+
children: k.vendor ?? "—"
|
|
90
99
|
})] }),
|
|
91
|
-
/* @__PURE__ */
|
|
100
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
92
101
|
className: "text-text-secondary",
|
|
93
|
-
children:
|
|
94
|
-
}), /* @__PURE__ */
|
|
102
|
+
children: E("adaptercloud.common.created", "Created")
|
|
103
|
+
}), /* @__PURE__ */ b("dd", {
|
|
95
104
|
className: "mt-0.5 font-medium text-text-primary",
|
|
96
|
-
children:
|
|
105
|
+
children: s(k.createdAt)
|
|
97
106
|
})] }),
|
|
98
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("dt", {
|
|
99
108
|
className: "text-text-secondary",
|
|
100
|
-
children:
|
|
101
|
-
}), /* @__PURE__ */
|
|
109
|
+
children: E("adaptercloud.common.updated", "Updated")
|
|
110
|
+
}), /* @__PURE__ */ b("dd", {
|
|
102
111
|
className: "mt-0.5 font-medium text-text-primary",
|
|
103
|
-
children:
|
|
112
|
+
children: s(k.updatedAt)
|
|
104
113
|
})] })
|
|
105
114
|
]
|
|
106
115
|
}),
|
|
107
|
-
|
|
116
|
+
k.capabilities.length > 0 && /* @__PURE__ */ x("div", {
|
|
108
117
|
className: "mt-5",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
118
|
+
children: [/* @__PURE__ */ b("h3", {
|
|
110
119
|
className: "mb-2 text-sm font-semibold text-text-primary",
|
|
111
|
-
children:
|
|
112
|
-
}), /* @__PURE__ */
|
|
120
|
+
children: E("adaptercloud.adapters.capabilities", "Capabilities")
|
|
121
|
+
}), /* @__PURE__ */ b("div", {
|
|
113
122
|
className: "flex flex-wrap gap-1.5",
|
|
114
|
-
children:
|
|
123
|
+
children: k.capabilities.map((e) => /* @__PURE__ */ b("span", {
|
|
115
124
|
className: "inline-flex items-center rounded-full bg-accent-blue-subtle px-2.5 py-0.5 text-xs text-accent-blue",
|
|
116
125
|
children: e
|
|
117
126
|
}, e))
|
|
118
127
|
})]
|
|
119
128
|
}),
|
|
120
|
-
|
|
129
|
+
k.tags.length > 0 && /* @__PURE__ */ x("div", {
|
|
121
130
|
className: "mt-4",
|
|
122
|
-
children: [/* @__PURE__ */
|
|
131
|
+
children: [/* @__PURE__ */ b("h3", {
|
|
123
132
|
className: "mb-2 text-sm font-semibold text-text-primary",
|
|
124
|
-
children:
|
|
125
|
-
}), /* @__PURE__ */
|
|
133
|
+
children: E("adaptercloud.common.tags", "Tags")
|
|
134
|
+
}), /* @__PURE__ */ b("div", {
|
|
126
135
|
className: "flex flex-wrap gap-1.5",
|
|
127
|
-
children:
|
|
136
|
+
children: k.tags.map((e) => /* @__PURE__ */ b("span", {
|
|
128
137
|
className: "inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary",
|
|
129
138
|
children: e
|
|
130
139
|
}, e))
|
|
@@ -132,56 +141,113 @@ function b() {
|
|
|
132
141
|
})
|
|
133
142
|
]
|
|
134
143
|
}),
|
|
135
|
-
|
|
144
|
+
k.configSchema != null && /* @__PURE__ */ x(S, {
|
|
136
145
|
className: "p-6",
|
|
137
|
-
children: [/* @__PURE__ */
|
|
138
|
-
className: "mb-3
|
|
139
|
-
children:
|
|
140
|
-
|
|
146
|
+
children: [/* @__PURE__ */ x("div", {
|
|
147
|
+
className: "mb-3 flex items-center justify-between gap-3",
|
|
148
|
+
children: [/* @__PURE__ */ b("h3", {
|
|
149
|
+
className: "text-sm font-semibold text-text-primary",
|
|
150
|
+
children: E("adaptercloud.adapters.configSchema", "Config Schema")
|
|
151
|
+
}), /* @__PURE__ */ b("button", {
|
|
152
|
+
type: "button",
|
|
153
|
+
onClick: P,
|
|
154
|
+
"aria-label": E("adaptercloud.common.copy", "Copy"),
|
|
155
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-subtle bg-bg-surface px-2.5 py-1 text-xs font-medium text-text-secondary transition-colors hover:bg-accent hover:text-text-primary",
|
|
156
|
+
children: M ? /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(h, { className: "h-3.5 w-3.5 text-status-success-text" }), E("adaptercloud.common.copied", "Copied")] }) : /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(g, { className: "h-3.5 w-3.5" }), E("adaptercloud.common.copy", "Copy")] })
|
|
157
|
+
})]
|
|
158
|
+
}), /* @__PURE__ */ b("pre", {
|
|
141
159
|
className: "overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary",
|
|
142
|
-
children: JSON.stringify(
|
|
160
|
+
children: JSON.stringify(k.configSchema, null, 2)
|
|
143
161
|
})]
|
|
144
162
|
}),
|
|
145
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ x("section", { children: [/* @__PURE__ */ b("h3", {
|
|
146
164
|
className: "mb-3 text-base font-semibold text-text-primary",
|
|
147
|
-
children:
|
|
148
|
-
}),
|
|
149
|
-
className: "
|
|
150
|
-
children:
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
className: "
|
|
163
|
-
children:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
165
|
+
children: E("adaptercloud.adapters.connections", "Connections")
|
|
166
|
+
}), k.connections && k.connections.length > 0 ? /* @__PURE__ */ b("div", {
|
|
167
|
+
className: "overflow-x-auto rounded-xl border border-border-subtle bg-bg-surface",
|
|
168
|
+
children: /* @__PURE__ */ x("table", {
|
|
169
|
+
className: "w-full min-w-[32rem] text-sm",
|
|
170
|
+
children: [/* @__PURE__ */ b("thead", { children: /* @__PURE__ */ x("tr", {
|
|
171
|
+
className: "border-b border-border-subtle text-left text-xs uppercase tracking-wide text-text-secondary",
|
|
172
|
+
children: [
|
|
173
|
+
/* @__PURE__ */ b("th", {
|
|
174
|
+
scope: "col",
|
|
175
|
+
className: "px-4 py-2.5 font-medium",
|
|
176
|
+
children: E("adaptercloud.adapters.connName", "Name")
|
|
177
|
+
}),
|
|
178
|
+
/* @__PURE__ */ b("th", {
|
|
179
|
+
scope: "col",
|
|
180
|
+
className: "px-4 py-2.5 font-medium",
|
|
181
|
+
children: E("adaptercloud.resources.region", "Region")
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ b("th", {
|
|
184
|
+
scope: "col",
|
|
185
|
+
className: "px-4 py-2.5 font-medium",
|
|
186
|
+
children: E("adaptercloud.adapters.status", "Status")
|
|
187
|
+
}),
|
|
188
|
+
/* @__PURE__ */ b("th", {
|
|
189
|
+
scope: "col",
|
|
190
|
+
className: "px-4 py-2.5 text-right font-medium",
|
|
191
|
+
children: E("adaptercloud.adapters.resources", "Resources")
|
|
192
|
+
})
|
|
193
|
+
]
|
|
194
|
+
}) }), /* @__PURE__ */ b("tbody", {
|
|
195
|
+
className: "divide-y divide-border",
|
|
196
|
+
children: k.connections.map((e) => /* @__PURE__ */ x("tr", {
|
|
197
|
+
role: "link",
|
|
198
|
+
tabIndex: 0,
|
|
199
|
+
onClick: () => w(`/connections/${e.id}`),
|
|
200
|
+
onKeyDown: (t) => {
|
|
201
|
+
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), w(`/connections/${e.id}`));
|
|
202
|
+
},
|
|
203
|
+
className: "cursor-pointer transition-colors hover:bg-accent/50 focus:bg-accent/50 focus:outline-none",
|
|
204
|
+
children: [
|
|
205
|
+
/* @__PURE__ */ b("td", {
|
|
206
|
+
className: "px-4 py-3",
|
|
207
|
+
children: /* @__PURE__ */ x("span", {
|
|
208
|
+
className: "flex items-center gap-2",
|
|
209
|
+
children: [/* @__PURE__ */ b(m, { className: "h-4 w-4 shrink-0 text-text-secondary" }), /* @__PURE__ */ b("span", {
|
|
210
|
+
className: "truncate font-medium text-text-primary",
|
|
211
|
+
children: e.name
|
|
212
|
+
})]
|
|
213
|
+
})
|
|
214
|
+
}),
|
|
215
|
+
/* @__PURE__ */ b("td", {
|
|
216
|
+
className: "px-4 py-3 text-text-secondary",
|
|
217
|
+
children: e.region ?? "—"
|
|
218
|
+
}),
|
|
219
|
+
/* @__PURE__ */ b("td", {
|
|
220
|
+
className: "px-4 py-3",
|
|
221
|
+
children: /* @__PURE__ */ b(r, { status: e.status })
|
|
222
|
+
}),
|
|
223
|
+
/* @__PURE__ */ b("td", {
|
|
224
|
+
className: "px-4 py-3 text-right tabular-nums text-text-secondary",
|
|
225
|
+
children: e.resourceCount
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
}, e.id))
|
|
229
|
+
})]
|
|
230
|
+
})
|
|
231
|
+
}) : /* @__PURE__ */ b("p", {
|
|
176
232
|
className: "rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary",
|
|
177
|
-
children:
|
|
233
|
+
children: E("adaptercloud.adapters.noConnections", "No connections use this adapter yet.")
|
|
178
234
|
})] })
|
|
179
235
|
]
|
|
180
236
|
})
|
|
181
|
-
})
|
|
237
|
+
}), /* @__PURE__ */ b(c, {
|
|
238
|
+
open: A,
|
|
239
|
+
onOpenChange: j,
|
|
240
|
+
title: E("adaptercloud.adapters.archiveTitle", "Archive this adapter?"),
|
|
241
|
+
description: E("adaptercloud.adapters.archivePrompt", "The adapter will be archived and hidden from the catalog. Existing connections keep working, but no new connections can select it."),
|
|
242
|
+
confirmLabel: E("adaptercloud.common.archive", "Archive"),
|
|
243
|
+
cancelLabel: E("adaptercloud.common.cancel", "Cancel"),
|
|
244
|
+
onConfirm: F,
|
|
245
|
+
isPending: O.isPending,
|
|
246
|
+
tone: "danger"
|
|
247
|
+
})]
|
|
182
248
|
});
|
|
183
249
|
}
|
|
184
250
|
//#endregion
|
|
185
|
-
export {
|
|
251
|
+
export { C as AdapterDetailPage };
|
|
186
252
|
|
|
187
253
|
//# sourceMappingURL=AdapterDetailPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdapterDetailPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/adapters/AdapterDetailPage.tsx"],"sourcesContent":["/**\n * Adapter Detail Page\n * @module adaptercloud/pages/adapters\n *\n * Full view of one adapter (`adaAdapter`) — metadata, capabilities, config\n * schema and its connections. Supports archiving via `adaArchiveAdapter`.\n */\n\nimport { useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Archive, Cable, Pencil } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useAdapter, useArchiveAdapter } from '../../hooks/useAdapterCloudApi';\nimport { adapterKindLabels, authMethodLabels } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\n\nexport function AdapterDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const query = useAdapter(id);\n const archive = useArchiveAdapter();\n const adapter = query.data;\n\n const handleArchive = useCallback(() => {\n if (!id) return;\n archive.mutate(id, {\n onSuccess: () => navigateTo('/adapters'),\n });\n }, [archive, id, navigateTo]);\n\n return (\n <PageLayout\n title={adapter?.name ?? t('adaptercloud.adapters.detailTitle', 'Adapter')}\n description={adapter?.slug}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/adapters')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.back', 'Back')}\n </button>\n {adapter && (\n <>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/adapters/${adapter.id}/edit`)}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <Pencil className=\"h-4 w-4\" />\n {t('adaptercloud.common.edit', 'Edit')}\n </button>\n {adapter.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n onClick={handleArchive}\n disabled={archive.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n <Archive className=\"h-4 w-4\" />\n {t('adaptercloud.common.archive', 'Archive')}\n </button>\n )}\n </>\n )}\n </div>\n }\n >\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={!query.isLoading && !adapter}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.adapters.notFound', 'Adapter not found')}\n emptyDescription={t(\n 'adaptercloud.adapters.notFoundDescription',\n 'This adapter does not exist or belongs to another workspace.'\n )}\n >\n {adapter && (\n <div className=\"space-y-6\">\n {/* Metadata */}\n <GlassCard className=\"p-6\">\n <div className=\"mb-4 flex items-center gap-3\">\n <StatusBadge status={adapter.status} />\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {adapterKindLabels[adapter.kind]}\n </span>\n <span className=\"text-xs text-text-secondary\">v{adapter.version}</span>\n </div>\n\n {adapter.description && (\n <p className=\"mb-4 text-sm text-text-secondary\">{adapter.description}</p>\n )}\n\n <dl className=\"grid grid-cols-2 gap-4 text-sm sm:grid-cols-3\">\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.adapters.vendor', 'Vendor')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">{adapter.vendor ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.created', 'Created')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(adapter.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.updated', 'Updated')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(adapter.updatedAt)}\n </dd>\n </div>\n </dl>\n\n {adapter.capabilities.length > 0 && (\n <div className=\"mt-5\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.adapters.capabilities', 'Capabilities')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {adapter.capabilities.map((cap) => (\n <span\n key={cap}\n className=\"inline-flex items-center rounded-full bg-accent-blue-subtle px-2.5 py-0.5 text-xs text-accent-blue\"\n >\n {cap}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {adapter.tags.length > 0 && (\n <div className=\"mt-4\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.common.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {adapter.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n </GlassCard>\n\n {/* Config schema */}\n {adapter.configSchema != null && (\n <GlassCard className=\"p-6\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.adapters.configSchema', 'Config Schema')}\n </h3>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary\">\n {JSON.stringify(adapter.configSchema, null, 2)}\n </pre>\n </GlassCard>\n )}\n\n {/* Connections */}\n <section>\n <h3 className=\"mb-3 text-base font-semibold text-text-primary\">\n {t('adaptercloud.adapters.connections', 'Connections')}\n </h3>\n {adapter.connections && adapter.connections.length > 0 ? (\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {adapter.connections.map((conn) => (\n <button\n key={conn.id}\n type=\"button\"\n onClick={() => navigateTo(`/connections/${conn.id}`)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <Cable className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {conn.name}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {authMethodLabels[conn.authMethod]} · {conn.resourceCount}{' '}\n {t('adaptercloud.adapters.resourcesLower', 'resources')}\n </p>\n </div>\n <StatusBadge status={conn.status} />\n </button>\n ))}\n </div>\n ) : (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary\">\n {t('adaptercloud.adapters.noConnections', 'No connections use this adapter yet.')}\n </p>\n )}\n </section>\n </div>\n )}\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,SAAgB,IAAoB;CAClC,IAAM,EAAE,UAAO,GAA2B,EACpC,EAAE,kBAAe,GAAiB,EAClC,EAAE,GAAG,MAAc,GAAS,EAC5B,IAAI,GACP,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAU,CACZ,EAEK,IAAQ,EAAW,EAAG,EACtB,IAAU,GAAmB,EAC7B,IAAU,EAAM,MAEhB,IAAgB,QAAkB;AACjC,OACL,EAAQ,OAAO,GAAI,EACjB,iBAAiB,EAAW,YAAY,EACzC,CAAC;IACD;EAAC;EAAS;EAAI;EAAW,CAAC;AAE7B,QACE,kBAAC,GAAD;EACE,OAAO,GAAS,QAAQ,EAAE,qCAAqC,UAAU;EACzE,aAAa,GAAS;EACtB,SACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,YAAY;IACtC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EAAE,4BAA4B,OAAO,CAC/B;OACR,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,aAAa,EAAQ,GAAG,OAAO;IACzD,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAC7B,EAAE,4BAA4B,OAAO,CAC/B;OACR,EAAQ,WAAW,cAClB,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,UAAU,EAAQ;IAClB,WAAU;cAJZ,CAME,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA,EAC9B,EAAE,+BAA+B,UAAU,CACrC;MAEV,EAAA,CAAA,CAED;;YAGR,kBAAC,GAAD;GACE,WAAW,EAAM;GACjB,SAAS,EAAM;GACf,SAAS,CAAC,EAAM,aAAa,CAAC;GAC9B,OAAO,EAAM;GACb,eAAe,KAAK,EAAM,SAAS;GACnC,YAAY,EAAE,kCAAkC,oBAAoB;GACpE,kBAAkB,EAChB,6CACA,+DACD;aAEA,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,GAAD;MAAW,WAAU;gBAArB;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD,EAAa,QAAQ,EAAQ,QAAU,CAAA;SACvC,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAkB,EAAQ;UACtB,CAAA;SACP,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CAA8C,KAAE,EAAQ,QAAe;;SACnE;;OAEL,EAAQ,eACP,kBAAC,KAAD;QAAG,WAAU;kBAAoC,EAAQ;QAAgB,CAAA;OAG3E,kBAAC,MAAD;QAAI,WAAU;kBAAd;SACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAE,gCAAgC,SAAS;UACzC,CAAA,EACL,kBAAC,MAAD;UAAI,WAAU;oBAAwC,EAAQ,UAAU;UAAS,CAAA,CAC7E,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAE,+BAA+B,UAAU;UACzC,CAAA,EACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAW,EAAQ,UAAU;UAC3B,CAAA,CACD,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAE,+BAA+B,UAAU;UACzC,CAAA,EACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAW,EAAQ,UAAU;UAC3B,CAAA,CACD,EAAA,CAAA;SACH;;OAEJ,EAAQ,aAAa,SAAS,KAC7B,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAE,sCAAsC,eAAe;SACrD,CAAA,EACL,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAQ,aAAa,KAAK,MACzB,kBAAC,QAAD;UAEE,WAAU;oBAET;UACI,EAJA,EAIA,CACP;SACE,CAAA,CACF;;OAGP,EAAQ,KAAK,SAAS,KACrB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAE,4BAA4B,OAAO;SACnC,CAAA,EACL,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAQ,KAAK,KAAK,MACjB,kBAAC,QAAD;UAEE,WAAU;oBAET;UACI,EAJA,EAIA,CACP;SACE,CAAA,CACF;;OAEE;;KAGX,EAAQ,gBAAgB,QACvB,kBAAC,GAAD;MAAW,WAAU;gBAArB,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAE,sCAAsC,gBAAgB;OACtD,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBACZ,KAAK,UAAU,EAAQ,cAAc,MAAM,EAAE;OAC1C,CAAA,CACI;;KAId,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAE,qCAAqC,cAAc;MACnD,CAAA,EACJ,EAAQ,eAAe,EAAQ,YAAY,SAAS,IACnD,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAQ,YAAY,KAAK,MACxB,kBAAC,UAAD;OAEE,MAAK;OACL,eAAe,EAAW,gBAAgB,EAAK,KAAK;OACpD,WAAU;iBAJZ;QAME,kBAAC,GAAD,EAAO,WAAU,wCAAyC,CAAA;QAC1D,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAK;UACJ,CAAA,EACJ,kBAAC,KAAD;UAAG,WAAU;oBAAb;WACG,EAAiB,EAAK;WAAY;WAAI,EAAK;WAAe;WAC1D,EAAE,wCAAwC,YAAY;WACrD;YACA;;QACN,kBAAC,GAAD,EAAa,QAAQ,EAAK,QAAU,CAAA;QAC7B;SAhBF,EAAK,GAgBH,CACT;MACE,CAAA,GAEN,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAE,uCAAuC,uCAAuC;MAC/E,CAAA,CAEE,EAAA,CAAA;KACN;;GAEE,CAAA;EACD,CAAA"}
|
|
1
|
+
{"version":3,"file":"AdapterDetailPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/adapters/AdapterDetailPage.tsx"],"sourcesContent":["/**\n * Adapter Detail Page\n * @module adaptercloud/pages/adapters\n *\n * Full view of one adapter (`adaAdapter`) — metadata, capabilities, config\n * schema and its connections. Supports archiving via `adaArchiveAdapter`.\n */\n\nimport { useCallback, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Archive, Cable, Check, Copy, Pencil } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataState } from '../../components/DataState';\nimport { ConfirmDialog } from '../../components/ConfirmDialog';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useAdapter, useArchiveAdapter } from '../../hooks/useAdapterCloudApi';\nimport { adapterKindLabels } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\n\nexport function AdapterDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const query = useAdapter(id);\n const archive = useArchiveAdapter();\n const adapter = query.data;\n\n const [confirmArchiveOpen, setConfirmArchiveOpen] = useState(false);\n const [copied, setCopied] = useState(false);\n\n const handleCopyConfig = useCallback(() => {\n if (adapter?.configSchema == null) return;\n const text = JSON.stringify(adapter.configSchema, null, 2);\n void navigator.clipboard\n ?.writeText(text)\n .then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n })\n .catch(() => {\n /* clipboard unavailable — no-op */\n });\n }, [adapter?.configSchema]);\n\n const handleArchive = useCallback(() => {\n if (!id) return;\n archive.mutate(id, {\n onSuccess: () => {\n setConfirmArchiveOpen(false);\n navigateTo('/adapters');\n },\n });\n }, [archive, id, navigateTo]);\n\n return (\n <PageLayout\n title={adapter?.name ?? t('adaptercloud.adapters.detailTitle', 'Adapter')}\n description={adapter?.slug}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/adapters')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.back', 'Back')}\n </button>\n {adapter && (\n <>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/adapters/${adapter.id}/edit`)}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <Pencil className=\"h-4 w-4\" />\n {t('adaptercloud.common.edit', 'Edit')}\n </button>\n {adapter.status !== 'ARCHIVED' && (\n <button\n type=\"button\"\n onClick={() => setConfirmArchiveOpen(true)}\n disabled={archive.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n <Archive className=\"h-4 w-4\" />\n {t('adaptercloud.common.archive', 'Archive')}\n </button>\n )}\n </>\n )}\n </div>\n }\n >\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={!query.isLoading && !adapter}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.adapters.notFound', 'Adapter not found')}\n emptyDescription={t(\n 'adaptercloud.adapters.notFoundDescription',\n 'This adapter does not exist or belongs to another workspace.'\n )}\n >\n {adapter && (\n <div className=\"space-y-6\">\n {/* Metadata */}\n <GlassCard className=\"p-6\">\n <div className=\"mb-4 flex items-center gap-3\">\n <StatusBadge status={adapter.status} />\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary\">\n {adapterKindLabels[adapter.kind]}\n </span>\n <span className=\"text-xs text-text-secondary\">v{adapter.version}</span>\n </div>\n\n {adapter.description && (\n <p className=\"mb-4 text-sm text-text-secondary\">{adapter.description}</p>\n )}\n\n <dl className=\"grid grid-cols-2 gap-4 text-sm sm:grid-cols-3\">\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.adapters.vendor', 'Vendor')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">{adapter.vendor ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.created', 'Created')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(adapter.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.updated', 'Updated')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(adapter.updatedAt)}\n </dd>\n </div>\n </dl>\n\n {adapter.capabilities.length > 0 && (\n <div className=\"mt-5\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.adapters.capabilities', 'Capabilities')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {adapter.capabilities.map((cap) => (\n <span\n key={cap}\n className=\"inline-flex items-center rounded-full bg-accent-blue-subtle px-2.5 py-0.5 text-xs text-accent-blue\"\n >\n {cap}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {adapter.tags.length > 0 && (\n <div className=\"mt-4\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.common.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {adapter.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n </GlassCard>\n\n {/* Config schema */}\n {adapter.configSchema != null && (\n <GlassCard className=\"p-6\">\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {t('adaptercloud.adapters.configSchema', 'Config Schema')}\n </h3>\n <button\n type=\"button\"\n onClick={handleCopyConfig}\n aria-label={t('adaptercloud.common.copy', 'Copy')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle bg-bg-surface px-2.5 py-1 text-xs font-medium text-text-secondary transition-colors hover:bg-accent hover:text-text-primary\"\n >\n {copied ? (\n <>\n <Check className=\"h-3.5 w-3.5 text-status-success-text\" />\n {t('adaptercloud.common.copied', 'Copied')}\n </>\n ) : (\n <>\n <Copy className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.common.copy', 'Copy')}\n </>\n )}\n </button>\n </div>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary\">\n {JSON.stringify(adapter.configSchema, null, 2)}\n </pre>\n </GlassCard>\n )}\n\n {/* Connections */}\n <section>\n <h3 className=\"mb-3 text-base font-semibold text-text-primary\">\n {t('adaptercloud.adapters.connections', 'Connections')}\n </h3>\n {adapter.connections && adapter.connections.length > 0 ? (\n <div className=\"overflow-x-auto rounded-xl border border-border-subtle bg-bg-surface\">\n <table className=\"w-full min-w-[32rem] text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle text-left text-xs uppercase tracking-wide text-text-secondary\">\n <th scope=\"col\" className=\"px-4 py-2.5 font-medium\">\n {t('adaptercloud.adapters.connName', 'Name')}\n </th>\n <th scope=\"col\" className=\"px-4 py-2.5 font-medium\">\n {t('adaptercloud.resources.region', 'Region')}\n </th>\n <th scope=\"col\" className=\"px-4 py-2.5 font-medium\">\n {t('adaptercloud.adapters.status', 'Status')}\n </th>\n <th scope=\"col\" className=\"px-4 py-2.5 text-right font-medium\">\n {t('adaptercloud.adapters.resources', 'Resources')}\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {adapter.connections.map((conn) => (\n <tr\n key={conn.id}\n role=\"link\"\n tabIndex={0}\n onClick={() => navigateTo(`/connections/${conn.id}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigateTo(`/connections/${conn.id}`);\n }\n }}\n className=\"cursor-pointer transition-colors hover:bg-accent/50 focus:bg-accent/50 focus:outline-none\"\n >\n <td className=\"px-4 py-3\">\n <span className=\"flex items-center gap-2\">\n <Cable className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n <span className=\"truncate font-medium text-text-primary\">\n {conn.name}\n </span>\n </span>\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">{conn.region ?? '—'}</td>\n <td className=\"px-4 py-3\">\n <StatusBadge status={conn.status} />\n </td>\n <td className=\"px-4 py-3 text-right tabular-nums text-text-secondary\">\n {conn.resourceCount}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary\">\n {t('adaptercloud.adapters.noConnections', 'No connections use this adapter yet.')}\n </p>\n )}\n </section>\n </div>\n )}\n </DataState>\n\n <ConfirmDialog\n open={confirmArchiveOpen}\n onOpenChange={setConfirmArchiveOpen}\n title={t('adaptercloud.adapters.archiveTitle', 'Archive this adapter?')}\n description={t(\n 'adaptercloud.adapters.archivePrompt',\n 'The adapter will be archived and hidden from the catalog. Existing connections keep working, but no new connections can select it.'\n )}\n confirmLabel={t('adaptercloud.common.archive', 'Archive')}\n cancelLabel={t('adaptercloud.common.cancel', 'Cancel')}\n onConfirm={handleArchive}\n isPending={archive.isPending}\n tone=\"danger\"\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|