@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.
- package/dist/adaptercloud/AdapterCloudRoot.d.ts.map +1 -1
- package/dist/adaptercloud/AdapterCloudRoot.js +52 -55
- package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
- 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/index.d.ts +1 -0
- package/dist/adaptercloud/hooks/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 +318 -291
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
- package/dist/adaptercloud/hooks/useTr.d.ts +2 -0
- package/dist/adaptercloud/hooks/useTr.d.ts.map +1 -0
- package/dist/adaptercloud/hooks/useTr.js +14 -0
- package/dist/adaptercloud/hooks/useTr.js.map +1 -0
- package/dist/adaptercloud/pages/OverviewPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/OverviewPage.js +117 -120
- 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 +1 -3
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,32 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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
|
|
12
|
-
import { formatRelativeTime as
|
|
13
|
-
import { useCallback as
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
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
|
|
19
|
+
function F(e) {
|
|
20
20
|
return e ? String(e.pageInfo.totalCount ?? e.items.length) : "—";
|
|
21
21
|
}
|
|
22
|
-
function
|
|
23
|
-
let { navigateTo:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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:
|
|
75
|
-
})), [Z, G.data?.currency]),
|
|
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
|
-
]),
|
|
82
|
+
]), ae = [
|
|
86
83
|
{
|
|
87
|
-
icon: /* @__PURE__ */
|
|
88
|
-
value:
|
|
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: () =>
|
|
87
|
+
onClick: () => I("/adapters")
|
|
91
88
|
},
|
|
92
89
|
{
|
|
93
|
-
icon: /* @__PURE__ */
|
|
94
|
-
value:
|
|
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: () =>
|
|
93
|
+
onClick: () => I("/connections")
|
|
97
94
|
},
|
|
98
95
|
{
|
|
99
|
-
icon: /* @__PURE__ */
|
|
100
|
-
value:
|
|
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: () =>
|
|
99
|
+
onClick: () => I("/resources")
|
|
103
100
|
},
|
|
104
101
|
{
|
|
105
|
-
icon: /* @__PURE__ */ k
|
|
106
|
-
value:
|
|
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: () =>
|
|
105
|
+
onClick: () => I("/violations")
|
|
109
106
|
}
|
|
110
107
|
];
|
|
111
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
115
|
+
children: [/* @__PURE__ */ A(D, { className: "h-4 w-4" }), R("adaptercloud.common.refresh", "Refresh")]
|
|
119
116
|
}),
|
|
120
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
124
|
-
isLoading:
|
|
125
|
-
isError:
|
|
120
|
+
}), /* @__PURE__ */ j(c, {
|
|
121
|
+
isLoading: re,
|
|
122
|
+
isError: ie,
|
|
126
123
|
error: z.error,
|
|
127
124
|
onRetry: $,
|
|
128
125
|
children: [
|
|
129
|
-
/* @__PURE__ */
|
|
126
|
+
/* @__PURE__ */ j(M, {
|
|
130
127
|
className: "mb-8",
|
|
131
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
131
|
+
}), /* @__PURE__ */ A("div", {
|
|
135
132
|
className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
|
|
136
|
-
children:
|
|
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__ */
|
|
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__ */
|
|
145
|
+
/* @__PURE__ */ j("div", {
|
|
149
146
|
className: "mb-8 grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
150
|
-
children: [/* @__PURE__ */
|
|
147
|
+
children: [/* @__PURE__ */ j("section", { children: [/* @__PURE__ */ j("div", {
|
|
151
148
|
className: "mb-4 flex items-center justify-between",
|
|
152
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
152
|
+
}), /* @__PURE__ */ j("button", {
|
|
156
153
|
type: "button",
|
|
157
|
-
onClick: () =>
|
|
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__ */
|
|
159
|
+
/* @__PURE__ */ A(C, { className: "h-3.5 w-3.5" })
|
|
163
160
|
]
|
|
164
161
|
})]
|
|
165
|
-
}), /* @__PURE__ */
|
|
162
|
+
}), /* @__PURE__ */ A(N, {
|
|
166
163
|
className: "p-5",
|
|
167
|
-
children: B.data && B.data.items.length > 0 ? /* @__PURE__ */
|
|
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__ */
|
|
171
|
-
/* @__PURE__ */
|
|
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__ */
|
|
172
|
+
/* @__PURE__ */ A("button", {
|
|
176
173
|
type: "button",
|
|
177
|
-
onClick: () =>
|
|
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__ */
|
|
180
|
+
})] }), /* @__PURE__ */ j("section", { children: [/* @__PURE__ */ j("div", {
|
|
184
181
|
className: "mb-4 flex items-center justify-between",
|
|
185
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
185
|
+
}), /* @__PURE__ */ j("button", {
|
|
189
186
|
type: "button",
|
|
190
|
-
onClick: () =>
|
|
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__ */
|
|
192
|
+
/* @__PURE__ */ A(C, { className: "h-3.5 w-3.5" })
|
|
196
193
|
]
|
|
197
194
|
})]
|
|
198
|
-
}), /* @__PURE__ */
|
|
195
|
+
}), /* @__PURE__ */ j(N, {
|
|
199
196
|
className: "p-5",
|
|
200
|
-
children: [/* @__PURE__ */
|
|
197
|
+
children: [/* @__PURE__ */ j("div", {
|
|
201
198
|
className: "mb-4 flex items-baseline gap-2",
|
|
202
199
|
children: [
|
|
203
|
-
/* @__PURE__ */
|
|
204
|
-
/* @__PURE__ */
|
|
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 ?
|
|
203
|
+
children: G.data ? i(G.data.total, G.data.currency) : "—"
|
|
207
204
|
}),
|
|
208
|
-
/* @__PURE__ */
|
|
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__ */
|
|
210
|
+
}), Q.length > 0 ? /* @__PURE__ */ A(u, {
|
|
214
211
|
data: Q,
|
|
215
212
|
labelWidthClassName: "w-24 sm:w-32"
|
|
216
|
-
}) : /* @__PURE__ */
|
|
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__ */
|
|
219
|
+
/* @__PURE__ */ j("div", {
|
|
223
220
|
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
224
|
-
children: [/* @__PURE__ */
|
|
225
|
-
/* @__PURE__ */
|
|
221
|
+
children: [/* @__PURE__ */ j("section", { children: [
|
|
222
|
+
/* @__PURE__ */ j("div", {
|
|
226
223
|
className: "mb-4 flex items-center justify-between",
|
|
227
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
227
|
+
}), /* @__PURE__ */ j("button", {
|
|
231
228
|
type: "button",
|
|
232
|
-
onClick: () =>
|
|
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__ */
|
|
234
|
+
/* @__PURE__ */ A(C, { className: "h-3.5 w-3.5" })
|
|
238
235
|
]
|
|
239
236
|
})]
|
|
240
237
|
}),
|
|
241
|
-
/* @__PURE__ */
|
|
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__ */
|
|
240
|
+
children: Y.length > 0 ? Y.map((e) => /* @__PURE__ */ j("button", {
|
|
244
241
|
type: "button",
|
|
245
|
-
onClick: () =>
|
|
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__ */
|
|
249
|
-
/* @__PURE__ */
|
|
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__ */
|
|
248
|
+
children: [/* @__PURE__ */ A("p", {
|
|
252
249
|
className: "truncate text-sm font-medium text-text-primary",
|
|
253
250
|
children: e.message
|
|
254
|
-
}), /* @__PURE__ */
|
|
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
|
-
|
|
257
|
+
v(e.detectedAt)
|
|
261
258
|
]
|
|
262
259
|
})]
|
|
263
260
|
}),
|
|
264
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ A(ee, { severity: e.severity })
|
|
265
262
|
]
|
|
266
|
-
}, e.id)) : /* @__PURE__ */
|
|
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
|
|
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
|
|
271
|
+
U.data != null && U.data > 0 && /* @__PURE__ */ j("button", {
|
|
275
272
|
type: "button",
|
|
276
|
-
onClick: () =>
|
|
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__ */
|
|
280
|
-
/* @__PURE__ */
|
|
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
|
|
280
|
+
U.data,
|
|
284
281
|
" ",
|
|
285
282
|
R("adaptercloud.overview.driftDetected", "drift records detected")
|
|
286
283
|
]
|
|
287
284
|
}),
|
|
288
|
-
/* @__PURE__ */
|
|
285
|
+
/* @__PURE__ */ A(C, { className: "ml-auto h-3.5 w-3.5 text-text-secondary" })
|
|
289
286
|
]
|
|
290
287
|
})
|
|
291
|
-
] }), /* @__PURE__ */
|
|
288
|
+
] }), /* @__PURE__ */ j("section", { children: [/* @__PURE__ */ j("div", {
|
|
292
289
|
className: "mb-4 flex items-center justify-between",
|
|
293
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
293
|
+
}), /* @__PURE__ */ j("button", {
|
|
297
294
|
type: "button",
|
|
298
|
-
onClick: () =>
|
|
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__ */
|
|
300
|
+
/* @__PURE__ */ A(C, { className: "h-3.5 w-3.5" })
|
|
304
301
|
]
|
|
305
302
|
})]
|
|
306
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
305
|
+
children: X.length > 0 ? X.map((e) => /* @__PURE__ */ j("button", {
|
|
309
306
|
type: "button",
|
|
310
|
-
onClick: () =>
|
|
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__ */
|
|
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__ */
|
|
312
|
+
children: /* @__PURE__ */ A(x, { className: "h-4 w-4 text-text-secondary" })
|
|
316
313
|
}),
|
|
317
|
-
/* @__PURE__ */
|
|
314
|
+
/* @__PURE__ */ j("div", {
|
|
318
315
|
className: "min-w-0 flex-1",
|
|
319
|
-
children: [/* @__PURE__ */
|
|
316
|
+
children: [/* @__PURE__ */ A("p", {
|
|
320
317
|
className: "text-sm font-medium text-text-primary",
|
|
321
|
-
children:
|
|
322
|
-
}), /* @__PURE__ */
|
|
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
|
-
|
|
327
|
+
v(e.createdAt)
|
|
331
328
|
]
|
|
332
329
|
})]
|
|
333
330
|
}),
|
|
334
|
-
/* @__PURE__ */
|
|
331
|
+
/* @__PURE__ */ A(s, { status: e.status })
|
|
335
332
|
]
|
|
336
|
-
}, e.id)) : /* @__PURE__ */
|
|
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__ */
|
|
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 {
|
|
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,
|
|
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) =>
|
|
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) =>
|
|
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", {
|