@burdenoff/microfe-adaptercloud 2026.825.3 → 2026.827.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adaptercloud/components/ActiveSwitch.d.ts +17 -0
- package/dist/adaptercloud/components/ActiveSwitch.d.ts.map +1 -0
- package/dist/adaptercloud/components/ActiveSwitch.js +28 -0
- package/dist/adaptercloud/components/ActiveSwitch.js.map +1 -0
- package/dist/adaptercloud/components/index.d.ts +1 -0
- package/dist/adaptercloud/components/index.d.ts.map +1 -1
- package/dist/adaptercloud/hooks/useAdapterCloudApi.d.ts +28 -2
- package/dist/adaptercloud/hooks/useAdapterCloudApi.d.ts.map +1 -1
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js +43 -18
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
- package/dist/adaptercloud/pages/OverviewPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/OverviewPage.js +4 -4
- package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +6 -5
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +217 -104
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +6 -5
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
- package/dist/adaptercloud/pages/costs/CostsPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/costs/CostsPage.js +183 -156
- package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
- package/dist/adaptercloud/pages/drift/DriftListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/drift/DriftListPage.js +108 -107
- package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
- package/dist/adaptercloud/pages/policies/PoliciesListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js +141 -131
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +171 -117
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourcesListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js +85 -66
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +148 -121
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
- package/dist/adaptercloud/pages/violations/ViolationsListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js +145 -144
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
- package/dist/utils/pageContext.d.ts +0 -2
- package/dist/utils/pageContext.d.ts.map +1 -1
- package/dist/utils/pageContext.js +25 -10
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +8 -5
|
@@ -37,7 +37,7 @@ function N() {
|
|
|
37
37
|
}), L = o({
|
|
38
38
|
status: "PUBLISHED",
|
|
39
39
|
pagination: { limit: 100 }
|
|
40
|
-
}).data?.items ?? [], R = I.data?.items ?? [], z = I.data?.pageInfo.hasMore ?? !1, B = u(() => {
|
|
40
|
+
}).data?.items ?? [], R = u(() => I.data?.items ?? [], [I.data?.items]), z = I.data?.pageInfo.hasMore ?? !1, B = I.data?.pageInfo.totalCount, V = u(() => {
|
|
41
41
|
let e = T.trim().toLowerCase();
|
|
42
42
|
return e ? R.filter((t) => t.name.toLowerCase().includes(e) || (t.region?.toLowerCase().includes(e) ?? !1) || (t.environment?.toLowerCase().includes(e) ?? !1)) : R;
|
|
43
43
|
}, [R, T]);
|
|
@@ -113,12 +113,12 @@ function N() {
|
|
|
113
113
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
|
|
114
114
|
children: v("adaptercloud.connections.new", "New connection")
|
|
115
115
|
}),
|
|
116
|
-
children: [
|
|
116
|
+
children: [V.length === 0 ? /* @__PURE__ */ O("p", {
|
|
117
117
|
className: "rounded-xl border border-border-subtle bg-bg-surface px-4 py-10 text-center text-sm text-text-secondary",
|
|
118
118
|
children: v("adaptercloud.connections.noSearchMatch", "No connections match your search.")
|
|
119
119
|
}) : /* @__PURE__ */ O("div", {
|
|
120
120
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
121
|
-
children:
|
|
121
|
+
children: V.map((e) => /* @__PURE__ */ k(A, {
|
|
122
122
|
role: "button",
|
|
123
123
|
tabIndex: 0,
|
|
124
124
|
onClick: () => p(`/connections/${e.id}`),
|
|
@@ -205,13 +205,14 @@ function N() {
|
|
|
205
205
|
children: [/* @__PURE__ */ O(h, { className: "h-4 w-4" }), v("adaptercloud.common.prev", "Previous")]
|
|
206
206
|
}),
|
|
207
207
|
/* @__PURE__ */ k("span", {
|
|
208
|
-
className: "text-sm text-text-secondary",
|
|
208
|
+
className: "text-sm tabular-nums text-text-secondary",
|
|
209
209
|
children: [
|
|
210
210
|
v("adaptercloud.common.showing", "Showing"),
|
|
211
211
|
" ",
|
|
212
212
|
P + 1,
|
|
213
213
|
"–",
|
|
214
|
-
P + R.length
|
|
214
|
+
P + R.length,
|
|
215
|
+
B != null && ` ${v("adaptercloud.common.of", "of")} ${B}`
|
|
215
216
|
]
|
|
216
217
|
}),
|
|
217
218
|
/* @__PURE__ */ k("button", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConnectionsListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/connections/ConnectionsListPage.tsx"],"sourcesContent":["/**\n * Connections List Page\n * @module adaptercloud/pages/connections\n *\n * Every configured, authenticated target in the workspace. Lists from\n * `adaListConnections` with an adapter + status filter, a client-side search,\n * and offset pagination. Each card carries a per-adapter-kind glyph tile so the\n * fabric's reach reads at a glance (mirrors the aws/azure/gke/snowflake logos in\n * the mock).\n */\n\nimport { useState, useCallback, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Cable,\n Plus,\n ChevronLeft,\n ChevronRight,\n MapPin,\n Server,\n Search,\n Cloud,\n Radio,\n Cpu,\n Boxes,\n Network,\n Database,\n HardDrive,\n Puzzle,\n type LucideIcon,\n} 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 { useConnections, useAdapters } from '../../hooks/useAdapterCloudApi';\nimport { authMethodLabels, connectionStatusLabels } from '../../utils/domain';\nimport { formatRelativeTime } from '../../utils/formatters';\nimport type { AdaAdapterKind, AdaConnectionStatus } from '../../types';\n\nconst PAGE_SIZE = 20;\n\n/**\n * Per-adapter-kind glyph — stands in for the vendor logos (aws / azure / gke /\n * snowflake) in the mock. Keyed off the connection's bound adapter kind; every\n * `AdaAdapterKind` maps to one of the eight allowed lucide marks.\n */\nconst adapterKindIcon: Record<AdaAdapterKind, LucideIcon> = {\n PUBLIC_CLOUD: Cloud,\n PRIVATE_CLOUD: Cloud,\n SAAS: Cloud,\n ON_PREM: HardDrive,\n EDGE: Radio,\n IOT: Cpu,\n KUBERNETES: Boxes,\n NETWORK: Network,\n DATA_SYSTEM: Database,\n CUSTOM: Puzzle,\n};\n\nexport function ConnectionsListPage() {\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 [status, setStatus] = useState<AdaConnectionStatus | ''>('');\n const [adapterId, setAdapterId] = useState('');\n const [search, setSearch] = useState('');\n const [offset, setOffset] = useState(0);\n\n const query = useConnections({\n adapterId: adapterId || undefined,\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n\n // Published adapters power the adapter filter dropdown (mock's \"Adapter filter\").\n const adaptersQuery = useAdapters({ status: 'PUBLISHED', pagination: { limit: 100 } });\n const adapterOptions = adaptersQuery.data?.items ?? [];\n\n const items = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n // Client-side search over the loaded page (name / region / environment).\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return items;\n return items.filter(\n (c) =>\n c.name.toLowerCase().includes(q) ||\n (c.region?.toLowerCase().includes(q) ?? false) ||\n (c.environment?.toLowerCase().includes(q) ?? false)\n );\n }, [items, search]);\n\n return (\n <PageLayout\n title={t('adaptercloud.connections.title', 'Connections')}\n description={t(\n 'adaptercloud.connections.subtitle',\n 'Configured, authenticated targets — each connection is where the fabric actually reaches.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections/new')}\n className=\"inline-flex items-center gap-2 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 <Plus className=\"h-4 w-4\" />\n {t('adaptercloud.connections.new', 'New connection')}\n </button>\n }\n >\n <div className=\"mb-6 flex flex-wrap items-center gap-3\">\n <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as AdaConnectionStatus | '');\n setOffset(0);\n }}\n aria-label={t('adaptercloud.connections.filterByStatus', 'Filter by status')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.connections.allStatuses', 'All statuses')}</option>\n {(Object.keys(connectionStatusLabels) as AdaConnectionStatus[]).map((s) => (\n <option key={s} value={s}>\n {connectionStatusLabels[s]}\n </option>\n ))}\n </select>\n\n <select\n value={adapterId}\n onChange={(e) => {\n setAdapterId(e.target.value);\n setOffset(0);\n }}\n aria-label={t('adaptercloud.connections.filterByAdapter', 'Filter by adapter')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.connections.allAdapters', 'All adapters')}</option>\n {adapterOptions.map((a) => (\n <option key={a.id} value={a.id}>\n {a.name}\n </option>\n ))}\n </select>\n\n <div className=\"relative w-full sm:ml-auto sm:w-64\">\n <Search\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"search\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n aria-label={t('adaptercloud.connections.search', 'Search connections')}\n placeholder={t('adaptercloud.connections.searchPlaceholder', 'Search…')}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={items.length === 0}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.connections.emptyTitle', 'No connections yet')}\n emptyDescription={t(\n 'adaptercloud.connections.emptyDescription',\n 'Create a connection to a real target to start discovering resources.'\n )}\n emptyIcon={<Cable className=\"h-10 w-10\" />}\n emptyAction={\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.connections.new', 'New connection')}\n </button>\n }\n >\n {filtered.length === 0 ? (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-10 text-center text-sm text-text-secondary\">\n {t('adaptercloud.connections.noSearchMatch', 'No connections match your search.')}\n </p>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((conn) => {\n const KindIcon = conn.adapter?.kind ? adapterKindIcon[conn.adapter.kind] : Cable;\n return (\n <GlassCard\n key={conn.id}\n role=\"button\"\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 p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-accent-blue\">\n <KindIcon className=\"h-5 w-5\" aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {conn.name}\n </h3>\n {conn.adapter && (\n <p className=\"mt-0.5 truncate text-xs text-text-secondary\">\n {conn.adapter.name}\n </p>\n )}\n </div>\n <StatusBadge status={conn.status} />\n </div>\n </div>\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-3 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5\">\n {authMethodLabels[conn.authMethod]}\n </span>\n {conn.region && (\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3 w-3\" /> {conn.region}\n </span>\n )}\n {conn.environment && <span>· {conn.environment}</span>}\n </div>\n\n <div className=\"mt-4 flex items-center gap-2 text-sm text-text-primary\">\n <Server className=\"h-4 w-4 text-text-secondary\" />\n <span className=\"font-semibold tabular-nums\">{conn.resourceCount}</span>\n <span className=\"text-text-secondary\">\n {t('adaptercloud.connections.resources', 'resources')}\n </span>\n </div>\n\n {conn.lastError && (\n <p className=\"mt-3 line-clamp-2 rounded-lg bg-status-error-bg-subtle px-2 py-1 text-xs text-status-error-text\">\n {conn.lastError}\n </p>\n )}\n\n <p className=\"mt-4 text-xs text-text-secondary\">\n {conn.lastSyncedAt\n ? `${t('adaptercloud.connections.lastSynced', 'Last synced')} ${formatRelativeTime(conn.lastSyncedAt)}`\n : t('adaptercloud.connections.neverSynced', 'Never synced')}\n </p>\n </GlassCard>\n );\n })}\n </div>\n )}\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-between\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.prev', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.common.showing', 'Showing')} {offset + 1}–{offset + items.length}\n </span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {t('adaptercloud.common.next', 'Next')}\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ConnectionsListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/connections/ConnectionsListPage.tsx"],"sourcesContent":["/**\n * Connections List Page\n * @module adaptercloud/pages/connections\n *\n * Every configured, authenticated target in the workspace. Lists from\n * `adaListConnections` with an adapter + status filter, a client-side search,\n * and offset pagination. Each card carries a per-adapter-kind glyph tile so the\n * fabric's reach reads at a glance (mirrors the aws/azure/gke/snowflake logos in\n * the mock).\n */\n\nimport { useState, useCallback, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Cable,\n Plus,\n ChevronLeft,\n ChevronRight,\n MapPin,\n Server,\n Search,\n Cloud,\n Radio,\n Cpu,\n Boxes,\n Network,\n Database,\n HardDrive,\n Puzzle,\n type LucideIcon,\n} 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 { useConnections, useAdapters } from '../../hooks/useAdapterCloudApi';\nimport { authMethodLabels, connectionStatusLabels } from '../../utils/domain';\nimport { formatRelativeTime } from '../../utils/formatters';\nimport type { AdaAdapterKind, AdaConnectionStatus } from '../../types';\n\nconst PAGE_SIZE = 20;\n\n/**\n * Per-adapter-kind glyph — stands in for the vendor logos (aws / azure / gke /\n * snowflake) in the mock. Keyed off the connection's bound adapter kind; every\n * `AdaAdapterKind` maps to one of the eight allowed lucide marks.\n */\nconst adapterKindIcon: Record<AdaAdapterKind, LucideIcon> = {\n PUBLIC_CLOUD: Cloud,\n PRIVATE_CLOUD: Cloud,\n SAAS: Cloud,\n ON_PREM: HardDrive,\n EDGE: Radio,\n IOT: Cpu,\n KUBERNETES: Boxes,\n NETWORK: Network,\n DATA_SYSTEM: Database,\n CUSTOM: Puzzle,\n};\n\nexport function ConnectionsListPage() {\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 [status, setStatus] = useState<AdaConnectionStatus | ''>('');\n const [adapterId, setAdapterId] = useState('');\n const [search, setSearch] = useState('');\n const [offset, setOffset] = useState(0);\n\n const query = useConnections({\n adapterId: adapterId || undefined,\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n\n // Published adapters power the adapter filter dropdown (mock's \"Adapter filter\").\n const adaptersQuery = useAdapters({ status: 'PUBLISHED', pagination: { limit: 100 } });\n const adapterOptions = adaptersQuery.data?.items ?? [];\n\n const items = useMemo(() => query.data?.items ?? [], [query.data?.items]);\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n // Estate-wide total for the current filter. Nullable on older deployed\n // schemas — the footer then falls back to the bare offset range.\n const totalCount = query.data?.pageInfo.totalCount;\n\n // Client-side search over the loaded page (name / region / environment).\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return items;\n return items.filter(\n (c) =>\n c.name.toLowerCase().includes(q) ||\n (c.region?.toLowerCase().includes(q) ?? false) ||\n (c.environment?.toLowerCase().includes(q) ?? false)\n );\n }, [items, search]);\n\n return (\n <PageLayout\n title={t('adaptercloud.connections.title', 'Connections')}\n description={t(\n 'adaptercloud.connections.subtitle',\n 'Configured, authenticated targets — each connection is where the fabric actually reaches.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections/new')}\n className=\"inline-flex items-center gap-2 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 <Plus className=\"h-4 w-4\" />\n {t('adaptercloud.connections.new', 'New connection')}\n </button>\n }\n >\n <div className=\"mb-6 flex flex-wrap items-center gap-3\">\n <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as AdaConnectionStatus | '');\n setOffset(0);\n }}\n aria-label={t('adaptercloud.connections.filterByStatus', 'Filter by status')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.connections.allStatuses', 'All statuses')}</option>\n {(Object.keys(connectionStatusLabels) as AdaConnectionStatus[]).map((s) => (\n <option key={s} value={s}>\n {connectionStatusLabels[s]}\n </option>\n ))}\n </select>\n\n <select\n value={adapterId}\n onChange={(e) => {\n setAdapterId(e.target.value);\n setOffset(0);\n }}\n aria-label={t('adaptercloud.connections.filterByAdapter', 'Filter by adapter')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.connections.allAdapters', 'All adapters')}</option>\n {adapterOptions.map((a) => (\n <option key={a.id} value={a.id}>\n {a.name}\n </option>\n ))}\n </select>\n\n <div className=\"relative w-full sm:ml-auto sm:w-64\">\n <Search\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"search\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n aria-label={t('adaptercloud.connections.search', 'Search connections')}\n placeholder={t('adaptercloud.connections.searchPlaceholder', 'Search…')}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={items.length === 0}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.connections.emptyTitle', 'No connections yet')}\n emptyDescription={t(\n 'adaptercloud.connections.emptyDescription',\n 'Create a connection to a real target to start discovering resources.'\n )}\n emptyIcon={<Cable className=\"h-10 w-10\" />}\n emptyAction={\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.connections.new', 'New connection')}\n </button>\n }\n >\n {filtered.length === 0 ? (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-10 text-center text-sm text-text-secondary\">\n {t('adaptercloud.connections.noSearchMatch', 'No connections match your search.')}\n </p>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((conn) => {\n const KindIcon = conn.adapter?.kind ? adapterKindIcon[conn.adapter.kind] : Cable;\n return (\n <GlassCard\n key={conn.id}\n role=\"button\"\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 p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-accent-blue\">\n <KindIcon className=\"h-5 w-5\" aria-hidden=\"true\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {conn.name}\n </h3>\n {conn.adapter && (\n <p className=\"mt-0.5 truncate text-xs text-text-secondary\">\n {conn.adapter.name}\n </p>\n )}\n </div>\n <StatusBadge status={conn.status} />\n </div>\n </div>\n </div>\n\n <div className=\"mt-3 flex flex-wrap items-center gap-3 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5\">\n {authMethodLabels[conn.authMethod]}\n </span>\n {conn.region && (\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3 w-3\" /> {conn.region}\n </span>\n )}\n {conn.environment && <span>· {conn.environment}</span>}\n </div>\n\n <div className=\"mt-4 flex items-center gap-2 text-sm text-text-primary\">\n <Server className=\"h-4 w-4 text-text-secondary\" />\n <span className=\"font-semibold tabular-nums\">{conn.resourceCount}</span>\n <span className=\"text-text-secondary\">\n {t('adaptercloud.connections.resources', 'resources')}\n </span>\n </div>\n\n {conn.lastError && (\n <p className=\"mt-3 line-clamp-2 rounded-lg bg-status-error-bg-subtle px-2 py-1 text-xs text-status-error-text\">\n {conn.lastError}\n </p>\n )}\n\n <p className=\"mt-4 text-xs text-text-secondary\">\n {conn.lastSyncedAt\n ? `${t('adaptercloud.connections.lastSynced', 'Last synced')} ${formatRelativeTime(conn.lastSyncedAt)}`\n : t('adaptercloud.connections.neverSynced', 'Never synced')}\n </p>\n </GlassCard>\n );\n })}\n </div>\n )}\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-between\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.prev', 'Previous')}\n </button>\n <span className=\"text-sm tabular-nums text-text-secondary\">\n {t('adaptercloud.common.showing', 'Showing')} {offset + 1}–{offset + items.length}\n {totalCount != null && ` ${t('adaptercloud.common.of', 'of')} ${totalCount}`}\n </span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {t('adaptercloud.common.next', 'Next')}\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CostsPage.d.ts","sourceRoot":"","sources":["../../../../src/adaptercloud/pages/costs/CostsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"CostsPage.d.ts","sourceRoot":"","sources":["../../../../src/adaptercloud/pages/costs/CostsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwBH,wBAAgB,SAAS,gCAgUxB"}
|
|
@@ -6,26 +6,30 @@ import { DataState as i } from "../../components/DataState.js";
|
|
|
6
6
|
import { BarChart as a } from "../../components/viz/BarChart.js";
|
|
7
7
|
import { Sparkline as o } from "../../components/viz/Sparkline.js";
|
|
8
8
|
import "../../components/viz/index.js";
|
|
9
|
-
import {
|
|
10
|
-
import { formatDate as
|
|
11
|
-
import { useCallback as
|
|
12
|
-
import { useI18n as
|
|
13
|
-
import { DollarSign as
|
|
14
|
-
import { Fragment as
|
|
15
|
-
import { EmphasisPanel as
|
|
9
|
+
import { useConnections as s, useCostRecords as c, useCostSummary as l } from "../../hooks/useAdapterCloudApi.js";
|
|
10
|
+
import { formatDate as u } from "../../utils/formatters.js";
|
|
11
|
+
import { useCallback as d, useMemo as f, useState as p } from "react";
|
|
12
|
+
import { useI18n as m } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
13
|
+
import { DollarSign as h, Receipt as g, Search as _, X as v } from "lucide-react";
|
|
14
|
+
import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
15
|
+
import { EmphasisPanel as S, GlassCard as C, ResponsiveTable as w } from "@burdenoff/fe-libs/ui";
|
|
16
16
|
//#region src/adaptercloud/pages/costs/CostsPage.tsx
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
var T = 100;
|
|
18
|
+
function E() {
|
|
19
|
+
let { navigateTo: E } = e(), { t: D } = m(), O = d((e, t) => {
|
|
20
|
+
let n = D(e);
|
|
20
21
|
return n === e ? t : n;
|
|
21
|
-
}, [
|
|
22
|
+
}, [D]), [k, A] = p(""), [j, M] = p(""), [N, P] = p(""), F = s({ pagination: { limit: T } }).data?.items ?? [], I = l(N || void 0), L = c({
|
|
23
|
+
connectionId: N || void 0,
|
|
24
|
+
service: j || void 0
|
|
25
|
+
}), R = I.data?.currency ?? "USD", z = f(() => [...I.data?.lines ?? []].sort((e, t) => t.total - e.total), [I.data]), B = f(() => z.map((e) => ({
|
|
22
26
|
key: e.service,
|
|
23
27
|
label: e.service,
|
|
24
28
|
value: e.total,
|
|
25
|
-
display: r(e.total,
|
|
26
|
-
})), [
|
|
29
|
+
display: r(e.total, R)
|
|
30
|
+
})), [z, R]), V = f(() => {
|
|
27
31
|
let e = /* @__PURE__ */ new Map();
|
|
28
|
-
for (let t of
|
|
32
|
+
for (let t of L.data ?? []) {
|
|
29
33
|
let n = new Date(t.periodStart);
|
|
30
34
|
if (Number.isNaN(n.getTime())) continue;
|
|
31
35
|
let r = `${n.getUTCFullYear()}-${String(n.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
@@ -36,43 +40,43 @@ function w() {
|
|
|
36
40
|
points: t,
|
|
37
41
|
deltaPct: r > 0 ? Math.round((n - r) / r * 100) : null
|
|
38
42
|
};
|
|
39
|
-
}, [
|
|
40
|
-
e.preventDefault(),
|
|
41
|
-
}, [
|
|
42
|
-
|
|
43
|
-
}, []),
|
|
43
|
+
}, [L.data]), H = d((e) => {
|
|
44
|
+
e.preventDefault(), M(k.trim());
|
|
45
|
+
}, [k]), U = d(() => {
|
|
46
|
+
A(""), M("");
|
|
47
|
+
}, []), W = [
|
|
44
48
|
{
|
|
45
49
|
key: "service",
|
|
46
|
-
header:
|
|
50
|
+
header: O("adaptercloud.costs.service", "Service"),
|
|
47
51
|
priority: "primary",
|
|
48
|
-
cell: (e) => /* @__PURE__ */
|
|
52
|
+
cell: (e) => /* @__PURE__ */ b("span", {
|
|
49
53
|
className: "font-medium text-text-primary",
|
|
50
54
|
children: e.service
|
|
51
55
|
})
|
|
52
56
|
},
|
|
53
57
|
{
|
|
54
58
|
key: "amount",
|
|
55
|
-
header:
|
|
59
|
+
header: O("adaptercloud.costs.amount", "Amount"),
|
|
56
60
|
priority: "primary",
|
|
57
|
-
cell: (e) => /* @__PURE__ */
|
|
61
|
+
cell: (e) => /* @__PURE__ */ b("span", {
|
|
58
62
|
className: "tabular-nums text-text-primary",
|
|
59
63
|
children: r(e.amount, e.currency)
|
|
60
64
|
})
|
|
61
65
|
},
|
|
62
66
|
{
|
|
63
67
|
key: "period",
|
|
64
|
-
header:
|
|
68
|
+
header: O("adaptercloud.costs.period", "Period"),
|
|
65
69
|
priority: "secondary",
|
|
66
|
-
cell: (e) => /* @__PURE__ */
|
|
70
|
+
cell: (e) => /* @__PURE__ */ x("span", {
|
|
67
71
|
className: "text-sm text-text-secondary",
|
|
68
72
|
children: [
|
|
69
|
-
|
|
73
|
+
u(e.periodStart, {
|
|
70
74
|
hour: void 0,
|
|
71
75
|
minute: void 0
|
|
72
76
|
}),
|
|
73
77
|
" –",
|
|
74
78
|
" ",
|
|
75
|
-
|
|
79
|
+
u(e.periodEnd, {
|
|
76
80
|
hour: void 0,
|
|
77
81
|
minute: void 0
|
|
78
82
|
})
|
|
@@ -81,163 +85,186 @@ function w() {
|
|
|
81
85
|
},
|
|
82
86
|
{
|
|
83
87
|
key: "resource",
|
|
84
|
-
header:
|
|
88
|
+
header: O("adaptercloud.costs.resource", "Resource"),
|
|
85
89
|
priority: "secondary",
|
|
86
|
-
cell: (e) => e.resource?.name ? /* @__PURE__ */
|
|
90
|
+
cell: (e) => e.resource?.name ? /* @__PURE__ */ b("button", {
|
|
87
91
|
type: "button",
|
|
88
|
-
onClick: () => e.resourceId &&
|
|
92
|
+
onClick: () => e.resourceId && E(`/resources/${e.resourceId}`),
|
|
89
93
|
className: "max-w-[12rem] truncate text-left text-sm text-accent-blue hover:underline",
|
|
90
94
|
children: e.resource.name
|
|
91
|
-
}) : e.resourceId ? /* @__PURE__ */
|
|
95
|
+
}) : e.resourceId ? /* @__PURE__ */ x("span", {
|
|
92
96
|
className: "font-mono text-xs text-text-secondary",
|
|
93
97
|
children: [e.resourceId.slice(0, 10), "…"]
|
|
94
|
-
}) : /* @__PURE__ */
|
|
98
|
+
}) : /* @__PURE__ */ b("span", {
|
|
95
99
|
className: "text-text-tertiary",
|
|
96
100
|
children: "—"
|
|
97
101
|
})
|
|
98
102
|
},
|
|
99
103
|
{
|
|
100
104
|
key: "connection",
|
|
101
|
-
header:
|
|
105
|
+
header: O("adaptercloud.costs.connection", "Connection"),
|
|
102
106
|
priority: "hidden-on-mobile",
|
|
103
|
-
cell: (e) => e.connection?.name ? /* @__PURE__ */
|
|
107
|
+
cell: (e) => e.connection?.name ? /* @__PURE__ */ b("span", {
|
|
104
108
|
className: "max-w-[12rem] truncate text-sm text-text-primary",
|
|
105
109
|
children: e.connection.name
|
|
106
|
-
}) : /* @__PURE__ */
|
|
110
|
+
}) : /* @__PURE__ */ b("span", {
|
|
107
111
|
className: "text-text-tertiary",
|
|
108
112
|
children: "—"
|
|
109
113
|
})
|
|
110
114
|
}
|
|
111
115
|
];
|
|
112
|
-
return /* @__PURE__ */
|
|
113
|
-
title:
|
|
114
|
-
description:
|
|
115
|
-
children: [
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
label: E("adaptercloud.costs.totalSpend", "Total Spend")
|
|
135
|
-
}), P.slice(0, 3).map((e) => /* @__PURE__ */ y(n, {
|
|
136
|
-
icon: /* @__PURE__ */ y(h, { className: "h-5 w-5" }),
|
|
137
|
-
value: r(e.total, N),
|
|
138
|
-
label: e.service
|
|
139
|
-
}, e.service))]
|
|
116
|
+
return /* @__PURE__ */ x(t, {
|
|
117
|
+
title: O("adaptercloud.costs.title", "FinOps"),
|
|
118
|
+
description: O("adaptercloud.costs.subtitle", "Spend attributed to the same resources you operate — cost and topology share one model."),
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ x("div", {
|
|
121
|
+
className: "mb-6 flex flex-wrap items-center gap-3",
|
|
122
|
+
children: [/* @__PURE__ */ b("label", {
|
|
123
|
+
htmlFor: "ada-cost-connection",
|
|
124
|
+
className: "text-sm text-text-secondary",
|
|
125
|
+
children: O("adaptercloud.costs.connection", "Connection")
|
|
126
|
+
}), /* @__PURE__ */ x("select", {
|
|
127
|
+
id: "ada-cost-connection",
|
|
128
|
+
value: N,
|
|
129
|
+
onChange: (e) => P(e.target.value),
|
|
130
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
131
|
+
children: [/* @__PURE__ */ b("option", {
|
|
132
|
+
value: "",
|
|
133
|
+
children: O("adaptercloud.costs.allConnections", "All connections")
|
|
134
|
+
}), F.map((e) => /* @__PURE__ */ b("option", {
|
|
135
|
+
value: e.id,
|
|
136
|
+
children: e.name
|
|
137
|
+
}, e.id))]
|
|
140
138
|
})]
|
|
141
|
-
}),
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
139
|
+
}),
|
|
140
|
+
/* @__PURE__ */ x(i, {
|
|
141
|
+
isLoading: I.isLoading,
|
|
142
|
+
isError: I.isError,
|
|
143
|
+
error: I.error,
|
|
144
|
+
onRetry: () => void I.refetch(),
|
|
145
|
+
children: [/* @__PURE__ */ x(S, {
|
|
146
|
+
className: "mb-8",
|
|
147
|
+
children: [/* @__PURE__ */ b("div", {
|
|
148
|
+
className: "mb-4 flex items-center justify-between",
|
|
149
|
+
children: /* @__PURE__ */ b("h2", {
|
|
150
|
+
className: "text-lg font-semibold text-text-primary",
|
|
151
|
+
children: O("adaptercloud.costs.summary", "Cost Summary")
|
|
153
152
|
})
|
|
153
|
+
}), /* @__PURE__ */ x("div", {
|
|
154
|
+
className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
|
|
155
|
+
children: [/* @__PURE__ */ b(n, {
|
|
156
|
+
featured: !0,
|
|
157
|
+
icon: /* @__PURE__ */ b(h, { className: "h-5 w-5" }),
|
|
158
|
+
value: I.data ? r(I.data.total, R) : "—",
|
|
159
|
+
label: O("adaptercloud.costs.totalSpend", "Total Spend")
|
|
160
|
+
}), z.slice(0, 3).map((e) => /* @__PURE__ */ b(n, {
|
|
161
|
+
icon: /* @__PURE__ */ b(g, { className: "h-5 w-5" }),
|
|
162
|
+
value: r(e.total, R),
|
|
163
|
+
label: e.service
|
|
164
|
+
}, e.service))]
|
|
154
165
|
})]
|
|
155
|
-
}),
|
|
156
|
-
className: "mb-
|
|
157
|
-
children:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
className: "
|
|
164
|
-
children:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
I.deltaPct > 0 ? "▲" : "▼",
|
|
169
|
-
" ",
|
|
170
|
-
Math.abs(I.deltaPct),
|
|
171
|
-
"%",
|
|
172
|
-
" ",
|
|
173
|
-
E("adaptercloud.costs.vsPrev", "vs prev")
|
|
174
|
-
]
|
|
166
|
+
}), z.length > 0 && /* @__PURE__ */ x("div", {
|
|
167
|
+
className: "mb-8 grid grid-cols-1 gap-6 lg:grid-cols-3",
|
|
168
|
+
children: [/* @__PURE__ */ x("section", {
|
|
169
|
+
className: "lg:col-span-2",
|
|
170
|
+
children: [/* @__PURE__ */ b("h2", {
|
|
171
|
+
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
172
|
+
children: O("adaptercloud.costs.byService", "Spend by Service")
|
|
173
|
+
}), /* @__PURE__ */ b(C, {
|
|
174
|
+
className: "p-5",
|
|
175
|
+
children: /* @__PURE__ */ b(a, {
|
|
176
|
+
data: B,
|
|
177
|
+
labelWidthClassName: "w-24 sm:w-32"
|
|
178
|
+
})
|
|
175
179
|
})]
|
|
176
|
-
}), /* @__PURE__ */
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
})
|
|
180
|
-
className: "
|
|
181
|
-
children:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
onClick: R,
|
|
206
|
-
"aria-label": E("adaptercloud.costs.clearServiceFilter", "Clear service filter"),
|
|
207
|
-
className: "absolute right-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
208
|
-
children: /* @__PURE__ */ y(_, { className: "h-4 w-4" })
|
|
209
|
-
})]
|
|
210
|
-
}), /* @__PURE__ */ y("button", {
|
|
211
|
-
type: "submit",
|
|
212
|
-
"aria-label": E("adaptercloud.costs.applyServiceFilter", "Apply service filter"),
|
|
213
|
-
className: "inline-flex shrink-0 items-center justify-center rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-text-primary transition-colors hover:bg-accent focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
214
|
-
children: /* @__PURE__ */ y(g, { className: "h-4 w-4" })
|
|
180
|
+
}), /* @__PURE__ */ x("section", { children: [/* @__PURE__ */ b("h2", {
|
|
181
|
+
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
182
|
+
children: O("adaptercloud.costs.trend", "Monthly Trend")
|
|
183
|
+
}), /* @__PURE__ */ b(C, {
|
|
184
|
+
className: "flex h-[calc(100%-2.75rem)] flex-col justify-between p-5",
|
|
185
|
+
children: V.points.length >= 2 ? /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ x("div", {
|
|
186
|
+
className: "flex items-baseline gap-2",
|
|
187
|
+
children: [/* @__PURE__ */ b("span", {
|
|
188
|
+
className: "text-2xl font-bold tabular-nums text-text-primary",
|
|
189
|
+
children: r(V.points[V.points.length - 1] ?? 0, R)
|
|
190
|
+
}), V.deltaPct !== null && /* @__PURE__ */ x("span", {
|
|
191
|
+
className: `text-xs font-semibold tabular-nums ${V.deltaPct > 0 ? "text-status-error-text" : "text-status-success-text"}`,
|
|
192
|
+
children: [
|
|
193
|
+
V.deltaPct > 0 ? "▲" : "▼",
|
|
194
|
+
" ",
|
|
195
|
+
Math.abs(V.deltaPct),
|
|
196
|
+
"%",
|
|
197
|
+
" ",
|
|
198
|
+
O("adaptercloud.costs.vsPrev", "vs prev")
|
|
199
|
+
]
|
|
200
|
+
})]
|
|
201
|
+
}), /* @__PURE__ */ b(o, {
|
|
202
|
+
points: V.points,
|
|
203
|
+
className: "mt-4 h-16 w-full text-accent-blue"
|
|
204
|
+
})] }) : /* @__PURE__ */ b("p", {
|
|
205
|
+
className: "py-6 text-center text-sm text-text-secondary",
|
|
206
|
+
children: O("adaptercloud.costs.trendEmpty", "Ingest at least two months of cost to see a trend.")
|
|
207
|
+
})
|
|
208
|
+
})] })]
|
|
215
209
|
})]
|
|
216
|
-
})]
|
|
217
|
-
}), /* @__PURE__ */ y(i, {
|
|
218
|
-
isLoading: M.isLoading,
|
|
219
|
-
isError: M.isError,
|
|
220
|
-
isEmpty: (M.data ?? []).length === 0,
|
|
221
|
-
error: M.error,
|
|
222
|
-
onRetry: () => void M.refetch(),
|
|
223
|
-
emptyTitle: E("adaptercloud.costs.emptyTitle", "No cost records"),
|
|
224
|
-
emptyDescription: E("adaptercloud.costs.emptyDescription", "Run a cost-ingest sync against a connection to populate the ledger."),
|
|
225
|
-
emptyIcon: /* @__PURE__ */ y(h, { className: "h-10 w-10" }),
|
|
226
|
-
emptyAction: /* @__PURE__ */ y("button", {
|
|
227
|
-
type: "button",
|
|
228
|
-
onClick: () => w("/connections"),
|
|
229
|
-
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
|
|
230
|
-
children: E("adaptercloud.costs.goToConnections", "Go to connections")
|
|
231
210
|
}),
|
|
232
|
-
children: /* @__PURE__ */
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
211
|
+
/* @__PURE__ */ x("section", { children: [/* @__PURE__ */ x("div", {
|
|
212
|
+
className: "mb-4 flex flex-wrap items-center justify-between gap-3",
|
|
213
|
+
children: [/* @__PURE__ */ b("h2", {
|
|
214
|
+
className: "text-lg font-semibold text-text-primary",
|
|
215
|
+
children: O("adaptercloud.costs.records", "Cost Records")
|
|
216
|
+
}), /* @__PURE__ */ x("form", {
|
|
217
|
+
onSubmit: H,
|
|
218
|
+
role: "search",
|
|
219
|
+
className: "flex w-full items-center gap-2 sm:w-auto",
|
|
220
|
+
children: [/* @__PURE__ */ x("div", {
|
|
221
|
+
className: "relative w-full sm:w-56",
|
|
222
|
+
children: [/* @__PURE__ */ b("input", {
|
|
223
|
+
type: "text",
|
|
224
|
+
value: k,
|
|
225
|
+
onChange: (e) => A(e.target.value),
|
|
226
|
+
"aria-label": O("adaptercloud.costs.filterService", "Filter by service"),
|
|
227
|
+
placeholder: O("adaptercloud.costs.filterServicePlaceholder", "Filter by service…"),
|
|
228
|
+
className: "w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-3 pr-9 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
229
|
+
}), j && /* @__PURE__ */ b("button", {
|
|
230
|
+
type: "button",
|
|
231
|
+
onClick: U,
|
|
232
|
+
"aria-label": O("adaptercloud.costs.clearServiceFilter", "Clear service filter"),
|
|
233
|
+
className: "absolute right-2 top-1/2 -translate-y-1/2 rounded p-0.5 text-text-secondary transition-colors hover:text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
234
|
+
children: /* @__PURE__ */ b(v, { className: "h-4 w-4" })
|
|
235
|
+
})]
|
|
236
|
+
}), /* @__PURE__ */ b("button", {
|
|
237
|
+
type: "submit",
|
|
238
|
+
"aria-label": O("adaptercloud.costs.applyServiceFilter", "Apply service filter"),
|
|
239
|
+
className: "inline-flex shrink-0 items-center justify-center rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-text-primary transition-colors hover:bg-accent focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
240
|
+
children: /* @__PURE__ */ b(_, { className: "h-4 w-4" })
|
|
241
|
+
})]
|
|
242
|
+
})]
|
|
243
|
+
}), /* @__PURE__ */ b(i, {
|
|
244
|
+
isLoading: L.isLoading,
|
|
245
|
+
isError: L.isError,
|
|
246
|
+
isEmpty: (L.data ?? []).length === 0,
|
|
247
|
+
error: L.error,
|
|
248
|
+
onRetry: () => void L.refetch(),
|
|
249
|
+
emptyTitle: O("adaptercloud.costs.emptyTitle", "No cost records"),
|
|
250
|
+
emptyDescription: O("adaptercloud.costs.emptyDescription", "Run a cost-ingest sync against a connection to populate the ledger."),
|
|
251
|
+
emptyIcon: /* @__PURE__ */ b(g, { className: "h-10 w-10" }),
|
|
252
|
+
emptyAction: /* @__PURE__ */ b("button", {
|
|
253
|
+
type: "button",
|
|
254
|
+
onClick: () => E("/connections"),
|
|
255
|
+
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
|
|
256
|
+
children: O("adaptercloud.costs.goToConnections", "Go to connections")
|
|
257
|
+
}),
|
|
258
|
+
children: /* @__PURE__ */ b(w, {
|
|
259
|
+
data: L.data ?? [],
|
|
260
|
+
columns: W,
|
|
261
|
+
rowKey: (e) => e.id
|
|
262
|
+
})
|
|
263
|
+
})] })
|
|
264
|
+
]
|
|
238
265
|
});
|
|
239
266
|
}
|
|
240
267
|
//#endregion
|
|
241
|
-
export {
|
|
268
|
+
export { E as CostsPage };
|
|
242
269
|
|
|
243
270
|
//# sourceMappingURL=CostsPage.js.map
|