@burdenoff/microfe-adaptercloud 2026.825.4 → 2026.829.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.
Files changed (57) hide show
  1. package/dist/adaptercloud/AdapterCloudRoot.d.ts.map +1 -1
  2. package/dist/adaptercloud/AdapterCloudRoot.js +52 -55
  3. package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
  4. package/dist/adaptercloud/components/ActiveSwitch.d.ts +17 -0
  5. package/dist/adaptercloud/components/ActiveSwitch.d.ts.map +1 -0
  6. package/dist/adaptercloud/components/ActiveSwitch.js +28 -0
  7. package/dist/adaptercloud/components/ActiveSwitch.js.map +1 -0
  8. package/dist/adaptercloud/components/index.d.ts +1 -0
  9. package/dist/adaptercloud/components/index.d.ts.map +1 -1
  10. package/dist/adaptercloud/hooks/index.d.ts +1 -0
  11. package/dist/adaptercloud/hooks/index.d.ts.map +1 -1
  12. package/dist/adaptercloud/hooks/useAdapterCloudApi.d.ts +28 -2
  13. package/dist/adaptercloud/hooks/useAdapterCloudApi.d.ts.map +1 -1
  14. package/dist/adaptercloud/hooks/useAdapterCloudApi.js +318 -291
  15. package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
  16. package/dist/adaptercloud/hooks/useTr.d.ts +2 -0
  17. package/dist/adaptercloud/hooks/useTr.d.ts.map +1 -0
  18. package/dist/adaptercloud/hooks/useTr.js +14 -0
  19. package/dist/adaptercloud/hooks/useTr.js.map +1 -0
  20. package/dist/adaptercloud/pages/OverviewPage.d.ts.map +1 -1
  21. package/dist/adaptercloud/pages/OverviewPage.js +117 -120
  22. package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
  23. package/dist/adaptercloud/pages/adapters/AdaptersListPage.d.ts.map +1 -1
  24. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +6 -5
  25. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
  26. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.d.ts.map +1 -1
  27. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +217 -104
  28. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
  29. package/dist/adaptercloud/pages/connections/ConnectionsListPage.d.ts.map +1 -1
  30. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +6 -5
  31. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
  32. package/dist/adaptercloud/pages/costs/CostsPage.d.ts.map +1 -1
  33. package/dist/adaptercloud/pages/costs/CostsPage.js +183 -156
  34. package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
  35. package/dist/adaptercloud/pages/drift/DriftListPage.d.ts.map +1 -1
  36. package/dist/adaptercloud/pages/drift/DriftListPage.js +108 -107
  37. package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
  38. package/dist/adaptercloud/pages/policies/PoliciesListPage.d.ts.map +1 -1
  39. package/dist/adaptercloud/pages/policies/PoliciesListPage.js +141 -131
  40. package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
  41. package/dist/adaptercloud/pages/policies/PolicyDetailPage.d.ts.map +1 -1
  42. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +171 -117
  43. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -1
  44. package/dist/adaptercloud/pages/resources/ResourcesListPage.d.ts.map +1 -1
  45. package/dist/adaptercloud/pages/resources/ResourcesListPage.js +85 -66
  46. package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
  47. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.d.ts.map +1 -1
  48. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +148 -121
  49. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
  50. package/dist/adaptercloud/pages/violations/ViolationsListPage.d.ts.map +1 -1
  51. package/dist/adaptercloud/pages/violations/ViolationsListPage.js +145 -144
  52. package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
  53. package/dist/utils/pageContext.d.ts +0 -2
  54. package/dist/utils/pageContext.d.ts.map +1 -1
  55. package/dist/utils/pageContext.js +1 -3
  56. package/dist/utils/pageContext.js.map +1 -1
  57. package/package.json +1 -1
@@ -1,32 +1,29 @@
1
- import { useAdapterCloud as e } from "../context/AdapterCloudContext.js";
2
- import { PageLayout as t } from "../components/PageLayout.js";
3
- import { MetricsCard as n } from "../components/MetricsCard.js";
4
- import { formatMoney as r, severityRank as i, syncKindLabels as a } from "../utils/domain.js";
5
- import { StatusBadge as o } from "../components/StatusBadge.js";
6
- import { SeverityBadge as s } from "../components/SeverityBadge.js";
7
- import { DataState as ee } from "../components/DataState.js";
8
- import { StackedBar as c } from "../components/viz/StackedBar.js";
9
- import { BarChart as l } from "../components/viz/BarChart.js";
1
+ import { useTr as e } from "../hooks/useTr.js";
2
+ import { useAdapterCloud as t } from "../context/AdapterCloudContext.js";
3
+ import { PageLayout as n } from "../components/PageLayout.js";
4
+ import { MetricsCard as r } from "../components/MetricsCard.js";
5
+ import { formatMoney as i, severityRank as a, syncKindLabels as o } from "../utils/domain.js";
6
+ import { StatusBadge as s } from "../components/StatusBadge.js";
7
+ import { SeverityBadge as ee } from "../components/SeverityBadge.js";
8
+ import { DataState as c } from "../components/DataState.js";
9
+ import { StackedBar as l } from "../components/viz/StackedBar.js";
10
+ import { BarChart as u } from "../components/viz/BarChart.js";
10
11
  import "../components/viz/index.js";
11
- import { useAdapters as u, useConnections as d, useCostSummary as f, useDriftRecords as p, useResources as m, useSyncRuns as h, useViolations as g } from "../hooks/useAdapterCloudApi.js";
12
- import { formatRelativeTime as _ } from "../utils/formatters.js";
13
- import { useCallback as v, useMemo as y } from "react";
14
- import { useI18n as te } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
15
- import { Activity as b, AlertCircle as x, ArrowRight as S, Boxes as C, Cable as w, DollarSign as T, GitCompareArrows as E, Plug as ne, RefreshCw as D, Server as re, ShieldAlert as O } from "lucide-react";
16
- import { jsx as k, jsxs as A } from "react/jsx-runtime";
17
- import { EmphasisPanel as j, GlassCard as M, PagePurpose as N } from "@burdenoff/fe-libs/ui";
12
+ import { useAdapters as d, useConnections as f, useCostSummary as p, useDriftCount as m, useResources as h, useSyncRuns as g, useViolations as _ } from "../hooks/useAdapterCloudApi.js";
13
+ import { formatRelativeTime as v } from "../utils/formatters.js";
14
+ import { useCallback as y, useMemo as b } from "react";
15
+ import { Activity as x, AlertCircle as S, ArrowRight as C, Boxes as w, Cable as T, DollarSign as E, GitCompareArrows as te, Plug as ne, RefreshCw as D, Server as O, ShieldAlert as k } from "lucide-react";
16
+ import { jsx as A, jsxs as j } from "react/jsx-runtime";
17
+ import { EmphasisPanel as M, GlassCard as N, PagePurpose as P } from "@burdenoff/fe-libs/ui";
18
18
  //#region src/adaptercloud/pages/OverviewPage.tsx
19
- function P(e) {
19
+ function F(e) {
20
20
  return e ? String(e.pageInfo.totalCount ?? e.items.length) : "—";
21
21
  }
22
- function F() {
23
- let { navigateTo: F, currentUser: I } = e(), { t: L } = te(), R = v((e, t) => {
24
- let n = L(e);
25
- return n === e ? t : n;
26
- }, [L]), z = u({ pagination: { limit: 100 } }), B = d({ pagination: { limit: 100 } }), V = m({ pagination: { limit: 100 } }), H = g({
22
+ function I() {
23
+ let { navigateTo: I, currentUser: L } = t(), R = e(), z = d({ pagination: { limit: 100 } }), B = f({ pagination: { limit: 100 } }), V = h({ pagination: { limit: 100 } }), H = _({
27
24
  status: "OPEN",
28
25
  pagination: { limit: 50 }
29
- }), U = p({ status: "DETECTED" }), W = h({ pagination: { limit: 5 } }), G = f(), K = I?.firstName ?? I?.name ?? "there", q = y(() => {
26
+ }), U = m("DETECTED"), W = g({ pagination: { limit: 5 } }), G = p(), K = L?.firstName ?? L?.name ?? "there", q = b(() => {
30
27
  let e = B.data?.items ?? [], t = {
31
28
  CONNECTED: 0,
32
29
  DEGRADED: 0,
@@ -36,7 +33,7 @@ function F() {
36
33
  };
37
34
  for (let n of e) t[n.status] += 1;
38
35
  return t;
39
- }, [B.data]), J = y(() => [
36
+ }, [B.data]), J = b(() => [
40
37
  {
41
38
  key: "CONNECTED",
42
39
  label: "Connected",
@@ -67,12 +64,12 @@ function F() {
67
64
  value: q.DISABLED,
68
65
  className: "bg-text-secondary"
69
66
  }
70
- ], [q]), Y = y(() => [...H.data?.items ?? []].sort((e, t) => i[t.severity] - i[e.severity]).slice(0, 5), [H.data]), X = W.data?.items ?? [], Z = y(() => [...G.data?.lines ?? []].sort((e, t) => t.total - e.total).slice(0, 6), [G.data]), Q = y(() => Z.map((e) => ({
67
+ ], [q]), Y = b(() => [...H.data?.items ?? []].sort((e, t) => a[t.severity] - a[e.severity]).slice(0, 5), [H.data]), X = W.data?.items ?? [], Z = b(() => [...G.data?.lines ?? []].sort((e, t) => t.total - e.total).slice(0, 6), [G.data]), Q = b(() => Z.map((e) => ({
71
68
  key: e.service,
72
69
  label: e.service,
73
70
  value: e.total,
74
- display: r(e.total, G.data?.currency ?? "USD")
75
- })), [Z, G.data?.currency]), ie = z.isLoading || B.isLoading || V.isLoading || H.isLoading || U.isLoading || G.isLoading, ae = z.isError || B.isError || V.isError || H.isError || U.isError || G.isError, $ = v(() => {
71
+ display: i(e.total, G.data?.currency ?? "USD")
72
+ })), [Z, G.data?.currency]), re = z.isLoading || B.isLoading || V.isLoading || H.isLoading || U.isLoading || G.isLoading, ie = z.isError || B.isError || V.isError || H.isError || U.isError || G.isError, $ = y(() => {
76
73
  z.refetch(), B.refetch(), V.refetch(), H.refetch(), U.refetch(), W.refetch(), G.refetch();
77
74
  }, [
78
75
  z,
@@ -82,62 +79,62 @@ function F() {
82
79
  U,
83
80
  W,
84
81
  G
85
- ]), oe = [
82
+ ]), ae = [
86
83
  {
87
- icon: /* @__PURE__ */ k(C, { className: "h-5 w-5" }),
88
- value: P(z.data),
84
+ icon: /* @__PURE__ */ A(w, { className: "h-5 w-5" }),
85
+ value: F(z.data),
89
86
  label: R("adaptercloud.overview.adapters", "Adapters"),
90
- onClick: () => F("/adapters")
87
+ onClick: () => I("/adapters")
91
88
  },
92
89
  {
93
- icon: /* @__PURE__ */ k(w, { className: "h-5 w-5" }),
94
- value: P(B.data),
90
+ icon: /* @__PURE__ */ A(T, { className: "h-5 w-5" }),
91
+ value: F(B.data),
95
92
  label: R("adaptercloud.overview.connections", "Connections"),
96
- onClick: () => F("/connections")
93
+ onClick: () => I("/connections")
97
94
  },
98
95
  {
99
- icon: /* @__PURE__ */ k(re, { className: "h-5 w-5" }),
100
- value: P(V.data),
96
+ icon: /* @__PURE__ */ A(O, { className: "h-5 w-5" }),
97
+ value: F(V.data),
101
98
  label: R("adaptercloud.overview.resources", "Resources"),
102
- onClick: () => F("/resources")
99
+ onClick: () => I("/resources")
103
100
  },
104
101
  {
105
- icon: /* @__PURE__ */ k(O, { className: "h-5 w-5" }),
106
- value: P(H.data),
102
+ icon: /* @__PURE__ */ A(k, { className: "h-5 w-5" }),
103
+ value: F(H.data),
107
104
  label: R("adaptercloud.overview.openViolations", "Open Violations"),
108
- onClick: () => F("/violations")
105
+ onClick: () => I("/violations")
109
106
  }
110
107
  ];
111
- return /* @__PURE__ */ A(t, {
108
+ return /* @__PURE__ */ j(n, {
112
109
  title: `${R("adaptercloud.overview.hello", "Welcome back")}, ${K}`,
113
110
  description: R("adaptercloud.overview.subtitle", "Your infrastructure continuum at a glance — fabric, governance, operations and cost."),
114
- actions: /* @__PURE__ */ A("button", {
111
+ actions: /* @__PURE__ */ j("button", {
115
112
  type: "button",
116
113
  onClick: $,
117
114
  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",
118
- children: [/* @__PURE__ */ k(D, { className: "h-4 w-4" }), R("adaptercloud.common.refresh", "Refresh")]
115
+ children: [/* @__PURE__ */ A(D, { className: "h-4 w-4" }), R("adaptercloud.common.refresh", "Refresh")]
119
116
  }),
120
- children: [/* @__PURE__ */ k(N, {
117
+ children: [/* @__PURE__ */ A(P, {
121
118
  className: "mb-6",
122
119
  children: R("adaptercloud.overview.purpose", "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.")
123
- }), /* @__PURE__ */ A(ee, {
124
- isLoading: ie,
125
- isError: ae,
120
+ }), /* @__PURE__ */ j(c, {
121
+ isLoading: re,
122
+ isError: ie,
126
123
  error: z.error,
127
124
  onRetry: $,
128
125
  children: [
129
- /* @__PURE__ */ A(j, {
126
+ /* @__PURE__ */ j(M, {
130
127
  className: "mb-8",
131
- children: [/* @__PURE__ */ k("h2", {
128
+ children: [/* @__PURE__ */ A("h2", {
132
129
  className: "mb-4 text-lg font-semibold text-text-primary",
133
130
  children: R("adaptercloud.overview.estate", "Estate Summary")
134
- }), /* @__PURE__ */ k("div", {
131
+ }), /* @__PURE__ */ A("div", {
135
132
  className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
136
- children: oe.map((e) => /* @__PURE__ */ k("button", {
133
+ children: ae.map((e) => /* @__PURE__ */ A("button", {
137
134
  type: "button",
138
135
  onClick: e.onClick,
139
136
  className: "text-left",
140
- children: /* @__PURE__ */ k(n, {
137
+ children: /* @__PURE__ */ A(r, {
141
138
  icon: e.icon,
142
139
  value: e.value,
143
140
  label: e.label
@@ -145,181 +142,181 @@ function F() {
145
142
  }, e.label))
146
143
  })]
147
144
  }),
148
- /* @__PURE__ */ A("div", {
145
+ /* @__PURE__ */ j("div", {
149
146
  className: "mb-8 grid grid-cols-1 gap-6 lg:grid-cols-2",
150
- children: [/* @__PURE__ */ A("section", { children: [/* @__PURE__ */ A("div", {
147
+ children: [/* @__PURE__ */ j("section", { children: [/* @__PURE__ */ j("div", {
151
148
  className: "mb-4 flex items-center justify-between",
152
- children: [/* @__PURE__ */ k("h2", {
149
+ children: [/* @__PURE__ */ A("h2", {
153
150
  className: "text-lg font-semibold text-text-primary",
154
151
  children: R("adaptercloud.overview.connectionHealth", "Connection Health")
155
- }), /* @__PURE__ */ A("button", {
152
+ }), /* @__PURE__ */ j("button", {
156
153
  type: "button",
157
- onClick: () => F("/connections"),
154
+ onClick: () => I("/connections"),
158
155
  className: "flex items-center gap-1 text-sm font-medium text-accent-blue hover:underline",
159
156
  children: [
160
157
  R("adaptercloud.common.viewAll", "View all"),
161
158
  " ",
162
- /* @__PURE__ */ k(S, { className: "h-3.5 w-3.5" })
159
+ /* @__PURE__ */ A(C, { className: "h-3.5 w-3.5" })
163
160
  ]
164
161
  })]
165
- }), /* @__PURE__ */ k(M, {
162
+ }), /* @__PURE__ */ A(N, {
166
163
  className: "p-5",
167
- children: B.data && B.data.items.length > 0 ? /* @__PURE__ */ k(c, { segments: J }) : /* @__PURE__ */ A("div", {
164
+ children: B.data && B.data.items.length > 0 ? /* @__PURE__ */ A(l, { segments: J }) : /* @__PURE__ */ j("div", {
168
165
  className: "flex flex-col items-center gap-3 py-6 text-center",
169
166
  children: [
170
- /* @__PURE__ */ k(ne, { className: "h-8 w-8 text-text-secondary" }),
171
- /* @__PURE__ */ k("p", {
167
+ /* @__PURE__ */ A(ne, { className: "h-8 w-8 text-text-secondary" }),
168
+ /* @__PURE__ */ A("p", {
172
169
  className: "text-sm text-text-secondary",
173
170
  children: R("adaptercloud.overview.noConnections", "No connections yet. Connect a target to start discovering resources.")
174
171
  }),
175
- /* @__PURE__ */ k("button", {
172
+ /* @__PURE__ */ A("button", {
176
173
  type: "button",
177
- onClick: () => F("/connections/new"),
174
+ onClick: () => I("/connections/new"),
178
175
  className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
179
176
  children: R("adaptercloud.overview.addConnection", "Add connection")
180
177
  })
181
178
  ]
182
179
  })
183
- })] }), /* @__PURE__ */ A("section", { children: [/* @__PURE__ */ A("div", {
180
+ })] }), /* @__PURE__ */ j("section", { children: [/* @__PURE__ */ j("div", {
184
181
  className: "mb-4 flex items-center justify-between",
185
- children: [/* @__PURE__ */ k("h2", {
182
+ children: [/* @__PURE__ */ A("h2", {
186
183
  className: "text-lg font-semibold text-text-primary",
187
184
  children: R("adaptercloud.overview.costByService", "Cost by Service")
188
- }), /* @__PURE__ */ A("button", {
185
+ }), /* @__PURE__ */ j("button", {
189
186
  type: "button",
190
- onClick: () => F("/costs"),
187
+ onClick: () => I("/costs"),
191
188
  className: "flex items-center gap-1 text-sm font-medium text-accent-blue hover:underline",
192
189
  children: [
193
190
  R("adaptercloud.overview.finops", "FinOps"),
194
191
  " ",
195
- /* @__PURE__ */ k(S, { className: "h-3.5 w-3.5" })
192
+ /* @__PURE__ */ A(C, { className: "h-3.5 w-3.5" })
196
193
  ]
197
194
  })]
198
- }), /* @__PURE__ */ A(M, {
195
+ }), /* @__PURE__ */ j(N, {
199
196
  className: "p-5",
200
- children: [/* @__PURE__ */ A("div", {
197
+ children: [/* @__PURE__ */ j("div", {
201
198
  className: "mb-4 flex items-baseline gap-2",
202
199
  children: [
203
- /* @__PURE__ */ k(T, { className: "h-5 w-5 text-text-secondary" }),
204
- /* @__PURE__ */ k("span", {
200
+ /* @__PURE__ */ A(E, { className: "h-5 w-5 text-text-secondary" }),
201
+ /* @__PURE__ */ A("span", {
205
202
  className: "text-2xl font-bold tabular-nums text-text-primary",
206
- children: G.data ? r(G.data.total, G.data.currency) : "—"
203
+ children: G.data ? i(G.data.total, G.data.currency) : "—"
207
204
  }),
208
- /* @__PURE__ */ k("span", {
205
+ /* @__PURE__ */ A("span", {
209
206
  className: "text-sm text-text-secondary",
210
207
  children: R("adaptercloud.overview.totalSpend", "total spend")
211
208
  })
212
209
  ]
213
- }), Q.length > 0 ? /* @__PURE__ */ k(l, {
210
+ }), Q.length > 0 ? /* @__PURE__ */ A(u, {
214
211
  data: Q,
215
212
  labelWidthClassName: "w-24 sm:w-32"
216
- }) : /* @__PURE__ */ k("p", {
213
+ }) : /* @__PURE__ */ A("p", {
217
214
  className: "py-4 text-center text-sm text-text-secondary",
218
215
  children: R("adaptercloud.overview.noCost", "No cost records ingested yet. Run a cost-ingest sync to populate FinOps.")
219
216
  })]
220
217
  })] })]
221
218
  }),
222
- /* @__PURE__ */ A("div", {
219
+ /* @__PURE__ */ j("div", {
223
220
  className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
224
- children: [/* @__PURE__ */ A("section", { children: [
225
- /* @__PURE__ */ A("div", {
221
+ children: [/* @__PURE__ */ j("section", { children: [
222
+ /* @__PURE__ */ j("div", {
226
223
  className: "mb-4 flex items-center justify-between",
227
- children: [/* @__PURE__ */ k("h2", {
224
+ children: [/* @__PURE__ */ A("h2", {
228
225
  className: "text-lg font-semibold text-text-primary",
229
226
  children: R("adaptercloud.overview.priorityViolations", "Priority Violations")
230
- }), /* @__PURE__ */ A("button", {
227
+ }), /* @__PURE__ */ j("button", {
231
228
  type: "button",
232
- onClick: () => F("/violations"),
229
+ onClick: () => I("/violations"),
233
230
  className: "flex items-center gap-1 text-sm font-medium text-accent-blue hover:underline",
234
231
  children: [
235
232
  R("adaptercloud.overview.queue", "Queue"),
236
233
  " ",
237
- /* @__PURE__ */ k(S, { className: "h-3.5 w-3.5" })
234
+ /* @__PURE__ */ A(C, { className: "h-3.5 w-3.5" })
238
235
  ]
239
236
  })]
240
237
  }),
241
- /* @__PURE__ */ k("div", {
238
+ /* @__PURE__ */ A("div", {
242
239
  className: "divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface",
243
- children: Y.length > 0 ? Y.map((e) => /* @__PURE__ */ A("button", {
240
+ children: Y.length > 0 ? Y.map((e) => /* @__PURE__ */ j("button", {
244
241
  type: "button",
245
- onClick: () => F("/violations"),
242
+ onClick: () => I("/violations"),
246
243
  className: "flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50",
247
244
  children: [
248
- /* @__PURE__ */ k(x, { className: "mt-0.5 h-4 w-4 shrink-0 text-status-error-text" }),
249
- /* @__PURE__ */ A("div", {
245
+ /* @__PURE__ */ A(S, { className: "mt-0.5 h-4 w-4 shrink-0 text-status-error-text" }),
246
+ /* @__PURE__ */ j("div", {
250
247
  className: "min-w-0 flex-1",
251
- children: [/* @__PURE__ */ k("p", {
248
+ children: [/* @__PURE__ */ A("p", {
252
249
  className: "truncate text-sm font-medium text-text-primary",
253
250
  children: e.message
254
- }), /* @__PURE__ */ A("p", {
251
+ }), /* @__PURE__ */ j("p", {
255
252
  className: "mt-0.5 text-xs text-text-secondary",
256
253
  children: [
257
254
  e.policy?.name ?? R("adaptercloud.overview.policy", "Policy"),
258
255
  " ·",
259
256
  " ",
260
- _(e.detectedAt)
257
+ v(e.detectedAt)
261
258
  ]
262
259
  })]
263
260
  }),
264
- /* @__PURE__ */ k(s, { severity: e.severity })
261
+ /* @__PURE__ */ A(ee, { severity: e.severity })
265
262
  ]
266
- }, e.id)) : /* @__PURE__ */ A("div", {
263
+ }, e.id)) : /* @__PURE__ */ j("div", {
267
264
  className: "flex flex-col items-center gap-2 px-4 py-8 text-center",
268
- children: [/* @__PURE__ */ k(O, { className: "h-8 w-8 text-status-success-text" }), /* @__PURE__ */ k("p", {
265
+ children: [/* @__PURE__ */ A(k, { className: "h-8 w-8 text-status-success-text" }), /* @__PURE__ */ A("p", {
269
266
  className: "text-sm text-text-secondary",
270
267
  children: R("adaptercloud.overview.noViolations", "No open violations. Estate is clean.")
271
268
  })]
272
269
  })
273
270
  }),
274
- U.data && U.data.length > 0 && /* @__PURE__ */ A("button", {
271
+ U.data != null && U.data > 0 && /* @__PURE__ */ j("button", {
275
272
  type: "button",
276
- onClick: () => F("/drift"),
273
+ onClick: () => I("/drift"),
277
274
  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",
278
275
  children: [
279
- /* @__PURE__ */ k(E, { className: "h-4 w-4 text-status-warning-text" }),
280
- /* @__PURE__ */ A("span", {
276
+ /* @__PURE__ */ A(te, { className: "h-4 w-4 text-status-warning-text" }),
277
+ /* @__PURE__ */ j("span", {
281
278
  className: "text-text-primary",
282
279
  children: [
283
- U.data.length,
280
+ U.data,
284
281
  " ",
285
282
  R("adaptercloud.overview.driftDetected", "drift records detected")
286
283
  ]
287
284
  }),
288
- /* @__PURE__ */ k(S, { className: "ml-auto h-3.5 w-3.5 text-text-secondary" })
285
+ /* @__PURE__ */ A(C, { className: "ml-auto h-3.5 w-3.5 text-text-secondary" })
289
286
  ]
290
287
  })
291
- ] }), /* @__PURE__ */ A("section", { children: [/* @__PURE__ */ A("div", {
288
+ ] }), /* @__PURE__ */ j("section", { children: [/* @__PURE__ */ j("div", {
292
289
  className: "mb-4 flex items-center justify-between",
293
- children: [/* @__PURE__ */ k("h2", {
290
+ children: [/* @__PURE__ */ A("h2", {
294
291
  className: "text-lg font-semibold text-text-primary",
295
292
  children: R("adaptercloud.overview.recentRuns", "Recent Sync Runs")
296
- }), /* @__PURE__ */ A("button", {
293
+ }), /* @__PURE__ */ j("button", {
297
294
  type: "button",
298
- onClick: () => F("/sync-runs"),
295
+ onClick: () => I("/sync-runs"),
299
296
  className: "flex items-center gap-1 text-sm font-medium text-accent-blue hover:underline",
300
297
  children: [
301
298
  R("adaptercloud.common.viewAll", "View all"),
302
299
  " ",
303
- /* @__PURE__ */ k(S, { className: "h-3.5 w-3.5" })
300
+ /* @__PURE__ */ A(C, { className: "h-3.5 w-3.5" })
304
301
  ]
305
302
  })]
306
- }), /* @__PURE__ */ k("div", {
303
+ }), /* @__PURE__ */ A("div", {
307
304
  className: "divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface",
308
- children: X.length > 0 ? X.map((e) => /* @__PURE__ */ A("button", {
305
+ children: X.length > 0 ? X.map((e) => /* @__PURE__ */ j("button", {
309
306
  type: "button",
310
- onClick: () => F("/sync-runs"),
307
+ onClick: () => I("/sync-runs"),
311
308
  className: "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50",
312
309
  children: [
313
- /* @__PURE__ */ k("div", {
310
+ /* @__PURE__ */ A("div", {
314
311
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken",
315
- children: /* @__PURE__ */ k(b, { className: "h-4 w-4 text-text-secondary" })
312
+ children: /* @__PURE__ */ A(x, { className: "h-4 w-4 text-text-secondary" })
316
313
  }),
317
- /* @__PURE__ */ A("div", {
314
+ /* @__PURE__ */ j("div", {
318
315
  className: "min-w-0 flex-1",
319
- children: [/* @__PURE__ */ k("p", {
316
+ children: [/* @__PURE__ */ A("p", {
320
317
  className: "text-sm font-medium text-text-primary",
321
- children: a[e.kind]
322
- }), /* @__PURE__ */ A("p", {
318
+ children: o[e.kind]
319
+ }), /* @__PURE__ */ j("p", {
323
320
  className: "mt-0.5 text-xs text-text-secondary",
324
321
  children: [
325
322
  e.discoveredCount,
@@ -327,15 +324,15 @@ function F() {
327
324
  R("adaptercloud.overview.discovered", "discovered"),
328
325
  " ",
329
326
  "· ",
330
- _(e.createdAt)
327
+ v(e.createdAt)
331
328
  ]
332
329
  })]
333
330
  }),
334
- /* @__PURE__ */ k(o, { status: e.status })
331
+ /* @__PURE__ */ A(s, { status: e.status })
335
332
  ]
336
- }, e.id)) : /* @__PURE__ */ A("div", {
333
+ }, e.id)) : /* @__PURE__ */ j("div", {
337
334
  className: "flex flex-col items-center gap-2 px-4 py-8 text-center",
338
- children: [/* @__PURE__ */ k(D, { className: "h-8 w-8 text-text-secondary" }), /* @__PURE__ */ k("p", {
335
+ children: [/* @__PURE__ */ A(D, { className: "h-8 w-8 text-text-secondary" }), /* @__PURE__ */ A("p", {
339
336
  className: "text-sm text-text-secondary",
340
337
  children: R("adaptercloud.overview.noRuns", "No sync runs yet.")
341
338
  })]
@@ -347,6 +344,6 @@ function F() {
347
344
  });
348
345
  }
349
346
  //#endregion
350
- export { F as OverviewPage };
347
+ export { I as OverviewPage };
351
348
 
352
349
  //# sourceMappingURL=OverviewPage.js.map
@@ -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 { StackedBar, BarChart, type BarDatum, type StackedSegment } from '../components/viz';\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 { 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\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 connectionHealthSegments: StackedSegment[] = useMemo(\n () => [\n {\n key: 'CONNECTED',\n label: 'Connected',\n value: connectionHealth.CONNECTED,\n className: 'bg-status-success-text',\n },\n {\n key: 'DEGRADED',\n label: 'Degraded',\n value: connectionHealth.DEGRADED,\n className: 'bg-status-warning-text',\n },\n {\n key: 'ERROR',\n label: 'Error',\n value: connectionHealth.ERROR,\n className: 'bg-status-error-text',\n },\n {\n key: 'PENDING',\n label: 'Pending',\n value: connectionHealth.PENDING,\n className: 'bg-accent-blue',\n },\n {\n key: 'DISABLED',\n label: 'Disabled',\n value: connectionHealth.DISABLED,\n className: 'bg-text-secondary',\n },\n ],\n [connectionHealth]\n );\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 costBarData: BarDatum[] = useMemo(\n () =>\n topCostLines.map((line) => ({\n key: line.service,\n label: line.service,\n value: line.total,\n display: formatMoney(line.total, costSummary.data?.currency ?? 'USD'),\n })),\n [topCostLines, costSummary.data?.currency]\n );\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 <StackedBar segments={connectionHealthSegments} />\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 {costBarData.length > 0 ? (\n <BarChart data={costBarData} labelWidthClassName=\"w-24 sm:w-32\" />\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
+ {"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 { useTr } from '../hooks/useTr';\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 { StackedBar, BarChart, type BarDatum, type StackedSegment } from '../components/viz';\nimport { useAdapterCloud } from '../context/AdapterCloudContext';\nimport {\n useAdapters,\n useConnections,\n useResources,\n useViolations,\n useDriftCount,\n useSyncRuns,\n useCostSummary,\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\nexport function OverviewPage() {\n const { navigateTo, currentUser } = useAdapterCloud();\n const t = useTr();\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 // Exact estate-wide count (via `pageInfo.totalCount`) rather than the length\n // of a page-capped fetch, so the callout cannot plateau at the page ceiling.\n const drift = useDriftCount('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 connectionHealthSegments: StackedSegment[] = useMemo(\n () => [\n {\n key: 'CONNECTED',\n label: 'Connected',\n value: connectionHealth.CONNECTED,\n className: 'bg-status-success-text',\n },\n {\n key: 'DEGRADED',\n label: 'Degraded',\n value: connectionHealth.DEGRADED,\n className: 'bg-status-warning-text',\n },\n {\n key: 'ERROR',\n label: 'Error',\n value: connectionHealth.ERROR,\n className: 'bg-status-error-text',\n },\n {\n key: 'PENDING',\n label: 'Pending',\n value: connectionHealth.PENDING,\n className: 'bg-accent-blue',\n },\n {\n key: 'DISABLED',\n label: 'Disabled',\n value: connectionHealth.DISABLED,\n className: 'bg-text-secondary',\n },\n ],\n [connectionHealth]\n );\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 costBarData: BarDatum[] = useMemo(\n () =>\n topCostLines.map((line) => ({\n key: line.service,\n label: line.service,\n value: line.total,\n display: formatMoney(line.total, costSummary.data?.currency ?? 'USD'),\n })),\n [topCostLines, costSummary.data?.currency]\n );\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 <StackedBar segments={connectionHealthSegments} />\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 {costBarData.length > 0 ? (\n <BarChart data={costBarData} labelWidthClassName=\"w-24 sm:w-32\" />\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 != null && drift.data > 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} {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 +1 @@
1
- {"version":3,"file":"AdaptersListPage.d.ts","sourceRoot":"","sources":["../../../../src/adaptercloud/pages/adapters/AdaptersListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6CH,wBAAgB,gBAAgB,gCA2M/B"}
1
+ {"version":3,"file":"AdaptersListPage.d.ts","sourceRoot":"","sources":["../../../../src/adaptercloud/pages/adapters/AdaptersListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA6CH,wBAAgB,gBAAgB,gCA+M/B"}
@@ -34,7 +34,7 @@ function O() {
34
34
  limit: E,
35
35
  offset: S
36
36
  }
37
- }), A = k.data?.items ?? [], j = k.data?.pageInfo.hasMore ?? !1, M = c((e) => (t) => {
37
+ }), A = k.data?.items ?? [], j = k.data?.pageInfo.hasMore ?? !1, M = k.data?.pageInfo.totalCount, N = c((e) => (t) => {
38
38
  e(t), O(0);
39
39
  }, []);
40
40
  return /* @__PURE__ */ w(t, {
@@ -50,7 +50,7 @@ function O() {
50
50
  className: "mb-6 flex flex-wrap gap-3",
51
51
  children: [/* @__PURE__ */ w("select", {
52
52
  value: _,
53
- onChange: (e) => M(v)(e.target.value),
53
+ onChange: (e) => N(v)(e.target.value),
54
54
  "aria-label": g("adaptercloud.adapters.filterByKind", "Filter by kind"),
55
55
  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)]",
56
56
  children: [/* @__PURE__ */ C("option", {
@@ -62,7 +62,7 @@ function O() {
62
62
  }, e))]
63
63
  }), /* @__PURE__ */ w("select", {
64
64
  value: b,
65
- onChange: (e) => M(x)(e.target.value),
65
+ onChange: (e) => N(x)(e.target.value),
66
66
  "aria-label": g("adaptercloud.adapters.filterByStatus", "Filter by status"),
67
67
  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)]",
68
68
  children: [/* @__PURE__ */ C("option", {
@@ -167,13 +167,14 @@ function O() {
167
167
  children: [/* @__PURE__ */ C(f, { className: "h-4 w-4" }), g("adaptercloud.common.prev", "Previous")]
168
168
  }),
169
169
  /* @__PURE__ */ w("span", {
170
- className: "text-sm text-text-secondary",
170
+ className: "text-sm tabular-nums text-text-secondary",
171
171
  children: [
172
172
  g("adaptercloud.common.showing", "Showing"),
173
173
  " ",
174
174
  S + 1,
175
175
  "–",
176
- S + A.length
176
+ S + A.length,
177
+ M != null && ` ${g("adaptercloud.common.of", "of")} ${M}`
177
178
  ]
178
179
  }),
179
180
  /* @__PURE__ */ w("button", {