@burdenoff/microfe-adaptercloud 2026.706.2 → 2026.706.4

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 (40) hide show
  1. package/dist/adaptercloud/AdapterCloudRoot.js +52 -44
  2. package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
  3. package/dist/adaptercloud/AdapterCloudRoutes.js +18 -14
  4. package/dist/adaptercloud/AdapterCloudRoutes.js.map +1 -1
  5. package/dist/adaptercloud/components/ConfirmDialog.js.map +1 -1
  6. package/dist/adaptercloud/components/viz/BarChart.js +32 -0
  7. package/dist/adaptercloud/components/viz/BarChart.js.map +1 -0
  8. package/dist/adaptercloud/components/viz/Sparkline.js +50 -0
  9. package/dist/adaptercloud/components/viz/Sparkline.js.map +1 -0
  10. package/dist/adaptercloud/components/viz/StackedBar.js +1 -0
  11. package/dist/adaptercloud/components/viz/TopologyGraph.js +96 -0
  12. package/dist/adaptercloud/components/viz/TopologyGraph.js.map +1 -0
  13. package/dist/adaptercloud/components/viz/index.js +4 -0
  14. package/dist/adaptercloud/hooks/useAdapterCloudApi.js +22 -4
  15. package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
  16. package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
  17. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js +176 -123
  18. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js.map +1 -1
  19. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js +261 -179
  20. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js.map +1 -1
  21. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
  22. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +158 -94
  23. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
  24. package/dist/adaptercloud/pages/costs/CostsPage.js +160 -111
  25. package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
  26. package/dist/adaptercloud/pages/drift/DriftListPage.js +163 -84
  27. package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
  28. package/dist/adaptercloud/pages/policies/PoliciesListPage.js +177 -96
  29. package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
  30. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +145 -131
  31. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -1
  32. package/dist/adaptercloud/pages/resources/ResourcesListPage.js +10 -1
  33. package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
  34. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +226 -165
  35. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
  36. package/dist/adaptercloud/pages/topology/TopologyPage.js +397 -0
  37. package/dist/adaptercloud/pages/topology/TopologyPage.js.map +1 -0
  38. package/dist/adaptercloud/pages/violations/ViolationsListPage.js +184 -131
  39. package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
  40. package/package.json +1 -1
@@ -1,218 +1,279 @@
1
1
  import { useAdapterCloud as e } from "../../context/AdapterCloudContext.js";
2
- import { PageLayout as t } from "../../components/PageLayout.js";
3
- import { syncKindLabels as n, syncStatusLabels as r } from "../../utils/domain.js";
4
- import { StatusBadge as i } from "../../components/StatusBadge.js";
5
- import { DataState as a } from "../../components/DataState.js";
6
- import { useCancelSyncRun as o, useSyncRuns as s } from "../../hooks/useAdapterCloudApi.js";
7
- import { formatDate as c, formatRelativeTime as l } from "../../utils/formatters.js";
2
+ import { cn as t } from "../../utils/cn.js";
3
+ import { PageLayout as n } from "../../components/PageLayout.js";
4
+ import { syncKindLabels as r, syncStatusLabels as i } from "../../utils/domain.js";
5
+ import { StatusBadge as a } from "../../components/StatusBadge.js";
6
+ import { DataState as o } from "../../components/DataState.js";
7
+ import { useCancelSyncRun as s, useSyncRuns as c } from "../../hooks/useAdapterCloudApi.js";
8
+ import { formatDate as l } from "../../utils/formatters.js";
8
9
  import { ConfirmDialog as u } from "../../components/ConfirmDialog.js";
9
10
  import { useCallback as d, useState as f } from "react";
10
11
  import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
11
- import { Activity as m, ChevronLeft as h, ChevronRight as g, GitCompareArrows as _, Loader2 as v, RotateCcw as y, Server as b, ShieldAlert as x, XCircle as S } from "lucide-react";
12
- import { jsx as C, jsxs as w } from "react/jsx-runtime";
13
- import { GlassCard as T } from "@burdenoff/fe-libs/ui";
12
+ import { Activity as m, Boxes as h, ChevronLeft as g, ChevronRight as _, Cloud as v, Cpu as y, Database as b, HardDrive as x, Loader2 as S, Network as C, Puzzle as w, Radio as T, Server as E, XCircle as D } from "lucide-react";
13
+ import { jsx as O, jsxs as k } from "react/jsx-runtime";
14
+ import { ResponsiveTable as A } from "@burdenoff/fe-libs/ui";
14
15
  //#region src/adaptercloud/pages/sync-runs/SyncRunsListPage.tsx
15
- var E = 20;
16
- function D() {
17
- let { navigateTo: D } = e(), { t: O } = p(), k = d((e, t) => {
18
- let n = O(e);
16
+ var j = 20, M = {
17
+ PUBLIC_CLOUD: v,
18
+ PRIVATE_CLOUD: v,
19
+ SAAS: v,
20
+ ON_PREM: x,
21
+ EDGE: T,
22
+ IOT: y,
23
+ KUBERNETES: h,
24
+ NETWORK: C,
25
+ DATA_SYSTEM: b,
26
+ CUSTOM: w
27
+ }, N = {
28
+ DISCOVERY: "bg-accent-blue-subtle text-accent-blue",
29
+ RECONCILE: "bg-status-warning-bg-subtle text-status-warning-text",
30
+ COST_INGEST: "bg-accent-purple-subtle text-accent-purple",
31
+ FULL: "bg-bg-sunken text-text-secondary"
32
+ };
33
+ function P({ kind: e }) {
34
+ return /* @__PURE__ */ O("span", {
35
+ className: t("inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium", N[e]),
36
+ children: r[e]
37
+ });
38
+ }
39
+ function F(e, t) {
40
+ if (!e || !t) return null;
41
+ let n = new Date(t).getTime() - new Date(e).getTime();
42
+ if (!Number.isFinite(n) || n < 0) return null;
43
+ let r = Math.floor(n / 1e3), i = Math.floor(r / 3600), a = Math.floor(r % 3600 / 60), o = r % 60;
44
+ return i > 0 ? `${i}h ${a}m ${o}s` : a > 0 ? `${a}m ${o}s` : `${o}s`;
45
+ }
46
+ function I() {
47
+ let { navigateTo: t } = e(), { t: r } = p(), h = d((e, t) => {
48
+ let n = r(e);
19
49
  return n === e ? t : n;
20
- }, [O]), [A, j] = f(""), [M, N] = f(0), [P, F] = f(null), [I, L] = f(null), R = s({
21
- status: A || void 0,
50
+ }, [r]), [v, y] = f(""), [b, x] = f(0), [C, w] = f(null), [T, N] = f(null), I = c({
51
+ status: v || void 0,
22
52
  pagination: {
23
- limit: E,
24
- offset: M
53
+ limit: j,
54
+ offset: b
25
55
  }
26
- }), z = o(), B = R.data?.items ?? [], V = R.data?.pageInfo.hasMore ?? !1, H = z.isPending ? z.variables : void 0, U = d((e) => {
27
- F(null), z.mutate(e, {
28
- onSuccess: () => L(null),
29
- onError: (e) => F(e instanceof Error ? e.message : "Failed to cancel run.")
56
+ }), L = s(), R = I.data?.items ?? [], z = I.data?.pageInfo.hasMore ?? !1, B = L.isPending ? L.variables : void 0, V = d((e) => {
57
+ w(null), L.mutate(e, {
58
+ onSuccess: () => N(null),
59
+ onError: (e) => w(e instanceof Error ? e.message : "Failed to cancel run.")
30
60
  });
31
- }, [z]);
32
- return /* @__PURE__ */ w(t, {
33
- title: k("adaptercloud.syncRuns.title", "Sync Runs"),
34
- description: k("adaptercloud.syncRuns.subtitle", "Discovery, reconciliation and cost-ingest executions that keep the picture live."),
61
+ }, [L]), H = (e, t) => e.status === "QUEUED" || e.status === "RUNNING" ? /* @__PURE__ */ O("span", {
62
+ className: "text-text-tertiary",
63
+ children: ""
64
+ }) : /* @__PURE__ */ O("span", {
65
+ className: "tabular-nums text-text-primary",
66
+ children: t
67
+ }), U = [
68
+ {
69
+ key: "kind",
70
+ header: h("adaptercloud.syncRuns.kind", "Kind"),
71
+ priority: "primary",
72
+ cell: (e) => /* @__PURE__ */ O(P, { kind: e.kind })
73
+ },
74
+ {
75
+ key: "connection",
76
+ header: h("adaptercloud.syncRuns.connection", "Connection"),
77
+ priority: "secondary",
78
+ cell: (e) => {
79
+ let t = e.connection?.adapter?.kind;
80
+ return /* @__PURE__ */ k("div", {
81
+ className: "flex min-w-0 items-center gap-2",
82
+ children: [/* @__PURE__ */ O("span", {
83
+ className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-bg-sunken text-text-secondary",
84
+ children: /* @__PURE__ */ O(t ? M[t] : E, { className: "h-3.5 w-3.5" })
85
+ }), e.connection?.name ? /* @__PURE__ */ O("span", {
86
+ className: "truncate text-sm text-text-primary",
87
+ children: e.connection.name
88
+ }) : /* @__PURE__ */ O("span", {
89
+ className: "text-sm text-text-tertiary",
90
+ children: "—"
91
+ })]
92
+ });
93
+ }
94
+ },
95
+ {
96
+ key: "status",
97
+ header: h("adaptercloud.syncRuns.status", "Status"),
98
+ priority: "secondary",
99
+ cell: (e) => /* @__PURE__ */ k("div", {
100
+ className: "flex flex-col gap-1",
101
+ children: [/* @__PURE__ */ O(a, {
102
+ status: e.status,
103
+ label: i[e.status]
104
+ }), e.error && /* @__PURE__ */ O("span", {
105
+ className: "max-w-xs truncate text-xs text-status-error-text",
106
+ title: e.error,
107
+ children: e.error
108
+ })]
109
+ })
110
+ },
111
+ {
112
+ key: "discovered",
113
+ header: h("adaptercloud.syncRuns.discovered", "Discovered"),
114
+ priority: "secondary",
115
+ headerClassName: "text-right",
116
+ cellClassName: "text-right",
117
+ cell: (e) => H(e, e.discoveredCount)
118
+ },
119
+ {
120
+ key: "reconciled",
121
+ header: h("adaptercloud.syncRuns.reconciled", "Reconciled"),
122
+ priority: "secondary",
123
+ headerClassName: "text-right",
124
+ cellClassName: "text-right",
125
+ cell: (e) => H(e, e.reconciledCount)
126
+ },
127
+ {
128
+ key: "drift",
129
+ header: h("adaptercloud.syncRuns.drift", "Drift"),
130
+ priority: "secondary",
131
+ headerClassName: "text-right",
132
+ cellClassName: "text-right",
133
+ cell: (e) => H(e, e.driftCount)
134
+ },
135
+ {
136
+ key: "violations",
137
+ header: h("adaptercloud.syncRuns.violations", "Violations"),
138
+ priority: "secondary",
139
+ headerClassName: "text-right",
140
+ cellClassName: "text-right",
141
+ cell: (e) => H(e, e.violationCount)
142
+ },
143
+ {
144
+ key: "started",
145
+ header: h("adaptercloud.syncRuns.started", "Started"),
146
+ priority: "secondary",
147
+ cell: (e) => e.startedAt ? /* @__PURE__ */ O("span", {
148
+ className: "whitespace-nowrap text-xs text-text-secondary",
149
+ children: l(e.startedAt)
150
+ }) : /* @__PURE__ */ O("span", {
151
+ className: "text-xs text-text-tertiary",
152
+ children: "—"
153
+ })
154
+ },
155
+ {
156
+ key: "duration",
157
+ header: h("adaptercloud.syncRuns.duration", "Duration"),
158
+ priority: "secondary",
159
+ headerClassName: "text-right",
160
+ cellClassName: "text-right",
161
+ cell: (e) => {
162
+ let t = F(e.startedAt, e.finishedAt);
163
+ return t ? /* @__PURE__ */ O("span", {
164
+ className: "whitespace-nowrap text-xs tabular-nums text-text-secondary",
165
+ children: t
166
+ }) : /* @__PURE__ */ O("span", {
167
+ className: "text-xs text-text-tertiary",
168
+ children: "—"
169
+ });
170
+ }
171
+ }
172
+ ];
173
+ return /* @__PURE__ */ k(n, {
174
+ title: h("adaptercloud.syncRuns.title", "Sync Runs"),
175
+ description: h("adaptercloud.syncRuns.subtitle", "Discovery, reconciliation and cost-ingest executions that keep the picture live."),
35
176
  children: [
36
- /* @__PURE__ */ C("div", {
177
+ /* @__PURE__ */ O("div", {
37
178
  className: "mb-6 flex flex-wrap gap-3",
38
- children: /* @__PURE__ */ w("select", {
39
- value: A,
179
+ children: /* @__PURE__ */ k("select", {
180
+ value: v,
40
181
  onChange: (e) => {
41
- j(e.target.value), N(0);
182
+ y(e.target.value), x(0);
42
183
  },
43
- "aria-label": k("adaptercloud.syncRuns.filterByStatus", "Filter by status"),
184
+ "aria-label": h("adaptercloud.syncRuns.filterByStatus", "Filter by status"),
44
185
  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)]",
45
- children: [/* @__PURE__ */ C("option", {
186
+ children: [/* @__PURE__ */ O("option", {
46
187
  value: "",
47
- children: k("adaptercloud.syncRuns.allStatuses", "All statuses")
48
- }), Object.keys(r).map((e) => /* @__PURE__ */ C("option", {
188
+ children: h("adaptercloud.syncRuns.allStatuses", "All statuses")
189
+ }), Object.keys(i).map((e) => /* @__PURE__ */ O("option", {
49
190
  value: e,
50
- children: r[e]
191
+ children: i[e]
51
192
  }, e))]
52
193
  })
53
194
  }),
54
- P && /* @__PURE__ */ C("p", {
195
+ C && /* @__PURE__ */ O("p", {
55
196
  role: "alert",
56
197
  "aria-live": "polite",
57
198
  className: "mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
58
- children: P
199
+ children: C
59
200
  }),
60
- /* @__PURE__ */ w(a, {
61
- isLoading: R.isLoading,
62
- isError: R.isError,
63
- isEmpty: B.length === 0,
64
- error: R.error,
65
- onRetry: () => void R.refetch(),
66
- emptyTitle: k("adaptercloud.syncRuns.emptyTitle", "No sync runs yet"),
67
- emptyDescription: k("adaptercloud.syncRuns.emptyDescription", "Start a sync run from a connection to keep the inventory current."),
68
- emptyIcon: /* @__PURE__ */ C(m, { className: "h-10 w-10" }),
69
- emptyAction: /* @__PURE__ */ C("button", {
201
+ /* @__PURE__ */ k(o, {
202
+ isLoading: I.isLoading,
203
+ isError: I.isError,
204
+ isEmpty: R.length === 0,
205
+ error: I.error,
206
+ onRetry: () => void I.refetch(),
207
+ emptyTitle: h("adaptercloud.syncRuns.emptyTitle", "No sync runs yet"),
208
+ emptyDescription: h("adaptercloud.syncRuns.emptyDescription", "Start a sync run from a connection to keep the inventory current."),
209
+ emptyIcon: /* @__PURE__ */ O(m, { className: "h-10 w-10" }),
210
+ emptyAction: /* @__PURE__ */ O("button", {
70
211
  type: "button",
71
- onClick: () => D("/connections"),
212
+ onClick: () => t("/connections"),
72
213
  className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
73
- children: k("adaptercloud.syncRuns.goToConnections", "Go to connections")
214
+ children: h("adaptercloud.syncRuns.goToConnections", "Go to connections")
74
215
  }),
75
- children: [/* @__PURE__ */ C("div", {
76
- className: "space-y-3",
77
- children: B.map((e) => {
78
- let t = e.status === "QUEUED" || e.status === "RUNNING";
79
- return /* @__PURE__ */ C(T, {
80
- className: "p-5",
81
- children: /* @__PURE__ */ w("div", {
82
- className: "flex flex-wrap items-start justify-between gap-3",
83
- children: [/* @__PURE__ */ w("div", {
84
- className: "min-w-0 flex-1",
85
- children: [
86
- /* @__PURE__ */ w("div", {
87
- className: "mb-1.5 flex flex-wrap items-center gap-2",
88
- children: [/* @__PURE__ */ C("span", {
89
- className: "text-sm font-semibold text-text-primary",
90
- children: n[e.kind]
91
- }), /* @__PURE__ */ C(i, { status: e.status })]
92
- }),
93
- /* @__PURE__ */ w("div", {
94
- className: "flex flex-wrap items-center gap-4 text-xs text-text-secondary",
95
- children: [
96
- /* @__PURE__ */ w("span", {
97
- className: "flex items-center gap-1",
98
- children: [
99
- /* @__PURE__ */ C(b, { className: "h-3.5 w-3.5" }),
100
- " ",
101
- e.discoveredCount,
102
- " ",
103
- k("adaptercloud.syncRuns.discovered", "discovered")
104
- ]
105
- }),
106
- /* @__PURE__ */ w("span", {
107
- className: "flex items-center gap-1",
108
- children: [
109
- /* @__PURE__ */ C(y, { className: "h-3.5 w-3.5" }),
110
- " ",
111
- e.reconciledCount,
112
- " ",
113
- k("adaptercloud.syncRuns.reconciled", "reconciled")
114
- ]
115
- }),
116
- /* @__PURE__ */ w("span", {
117
- className: "flex items-center gap-1",
118
- children: [
119
- /* @__PURE__ */ C(_, { className: "h-3.5 w-3.5" }),
120
- " ",
121
- e.driftCount,
122
- " ",
123
- k("adaptercloud.syncRuns.drift", "drift")
124
- ]
125
- }),
126
- /* @__PURE__ */ w("span", {
127
- className: "flex items-center gap-1",
128
- children: [
129
- /* @__PURE__ */ C(x, { className: "h-3.5 w-3.5" }),
130
- " ",
131
- e.violationCount,
132
- " ",
133
- k("adaptercloud.syncRuns.violations", "violations")
134
- ]
135
- })
136
- ]
137
- }),
138
- /* @__PURE__ */ w("p", {
139
- className: "mt-2 text-xs text-text-secondary",
140
- children: [
141
- e.startedAt ? `${k("adaptercloud.syncRuns.started", "Started")} ${c(e.startedAt)}` : `${k("adaptercloud.syncRuns.created", "Created")} ${l(e.createdAt)}`,
142
- e.finishedAt && ` · ${k("adaptercloud.syncRuns.finished", "Finished")} ${c(e.finishedAt)}`,
143
- " · ",
144
- /* @__PURE__ */ C("button", {
145
- type: "button",
146
- onClick: () => D(`/connections/${e.connectionId}`),
147
- className: "hover:underline",
148
- children: k("adaptercloud.syncRuns.viewConnection", "View connection")
149
- })
150
- ]
151
- }),
152
- e.error && /* @__PURE__ */ C("p", {
153
- className: "mt-2 rounded-lg bg-status-error-bg-subtle px-3 py-1.5 text-xs text-status-error-text",
154
- children: e.error
155
- })
156
- ]
157
- }), t && /* @__PURE__ */ w("button", {
158
- type: "button",
159
- disabled: H === e.id,
160
- onClick: () => {
161
- F(null), L(e);
162
- },
163
- className: "inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50",
164
- children: [H === e.id ? /* @__PURE__ */ C(v, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ C(S, { className: "h-3.5 w-3.5" }), k("adaptercloud.syncRuns.cancel", "Cancel")]
165
- })]
166
- })
167
- }, e.id);
168
- })
169
- }), (M > 0 || V) && /* @__PURE__ */ w("div", {
216
+ children: [/* @__PURE__ */ O(A, {
217
+ data: R,
218
+ columns: U,
219
+ rowKey: (e) => e.id,
220
+ onRowClick: (e) => t(`/connections/${e.connectionId}`),
221
+ rowActions: (e) => e.status === "QUEUED" || e.status === "RUNNING" ? /* @__PURE__ */ k("button", {
222
+ type: "button",
223
+ disabled: B === e.id,
224
+ onClick: () => {
225
+ w(null), N(e);
226
+ },
227
+ className: "inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50",
228
+ children: [B === e.id ? /* @__PURE__ */ O(S, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ O(D, { className: "h-3.5 w-3.5" }), h("adaptercloud.syncRuns.cancel", "Cancel")]
229
+ }) : null
230
+ }), (b > 0 || z) && /* @__PURE__ */ k("div", {
170
231
  className: "mt-6 flex items-center justify-between",
171
232
  children: [
172
- /* @__PURE__ */ w("button", {
233
+ /* @__PURE__ */ k("button", {
173
234
  type: "button",
174
- disabled: M === 0,
175
- onClick: () => N((e) => Math.max(0, e - E)),
235
+ disabled: b === 0,
236
+ onClick: () => x((e) => Math.max(0, e - j)),
176
237
  className: "inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50",
177
- children: [/* @__PURE__ */ C(h, { className: "h-4 w-4" }), k("adaptercloud.common.prev", "Previous")]
238
+ children: [/* @__PURE__ */ O(g, { className: "h-4 w-4" }), h("adaptercloud.common.prev", "Previous")]
178
239
  }),
179
- /* @__PURE__ */ w("span", {
240
+ /* @__PURE__ */ k("span", {
180
241
  className: "text-sm text-text-secondary",
181
242
  children: [
182
- k("adaptercloud.common.showing", "Showing"),
243
+ h("adaptercloud.common.showing", "Showing"),
183
244
  " ",
184
- M + 1,
245
+ b + 1,
185
246
  "–",
186
- M + B.length
247
+ b + R.length
187
248
  ]
188
249
  }),
189
- /* @__PURE__ */ w("button", {
250
+ /* @__PURE__ */ k("button", {
190
251
  type: "button",
191
- disabled: !V,
192
- onClick: () => N((e) => e + E),
252
+ disabled: !z,
253
+ onClick: () => x((e) => e + j),
193
254
  className: "inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50",
194
- children: [k("adaptercloud.common.next", "Next"), /* @__PURE__ */ C(g, { className: "h-4 w-4" })]
255
+ children: [h("adaptercloud.common.next", "Next"), /* @__PURE__ */ O(_, { className: "h-4 w-4" })]
195
256
  })
196
257
  ]
197
258
  })]
198
259
  }),
199
- /* @__PURE__ */ C(u, {
200
- open: !!I,
260
+ /* @__PURE__ */ O(u, {
261
+ open: !!T,
201
262
  onOpenChange: (e) => {
202
- e || L(null);
263
+ e || N(null);
203
264
  },
204
- title: k("adaptercloud.syncRuns.cancelTitle", "Cancel this sync run?"),
205
- description: k("adaptercloud.syncRuns.cancelPrompt", "The run will stop and transition to Cancelled. Any work already done is kept, but it will not finish."),
206
- confirmLabel: k("adaptercloud.syncRuns.confirmCancel", "Cancel run"),
207
- cancelLabel: k("adaptercloud.syncRuns.keepRunning", "Keep running"),
208
- onConfirm: () => I && U(I.id),
209
- isPending: !!I && H === I?.id,
265
+ title: h("adaptercloud.syncRuns.cancelTitle", "Cancel this sync run?"),
266
+ description: h("adaptercloud.syncRuns.cancelPrompt", "The run will stop and transition to Cancelled. Any work already done is kept, but it will not finish."),
267
+ confirmLabel: h("adaptercloud.syncRuns.confirmCancel", "Cancel run"),
268
+ cancelLabel: h("adaptercloud.syncRuns.keepRunning", "Keep running"),
269
+ onConfirm: () => T && V(T.id),
270
+ isPending: !!T && B === T?.id,
210
271
  tone: "danger"
211
272
  })
212
273
  ]
213
274
  });
214
275
  }
215
276
  //#endregion
216
- export { D as SyncRunsListPage };
277
+ export { I as SyncRunsListPage };
217
278
 
218
279
  //# sourceMappingURL=SyncRunsListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SyncRunsListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/sync-runs/SyncRunsListPage.tsx"],"sourcesContent":["/**\n * Sync Runs History Page\n * @module adaptercloud/pages/sync-runs\n *\n * The operations log (`adaListSyncRuns`) — every discovery / reconcile /\n * cost-ingest / full execution with its lifecycle and counters. Filters by\n * status; queued/running runs can be cancelled via `adaCancelSyncRun`.\n */\n\nimport { useState, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Activity,\n ChevronLeft,\n ChevronRight,\n XCircle,\n Loader2,\n Server,\n GitCompareArrows,\n ShieldAlert,\n RotateCcw,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataState } from '../../components/DataState';\nimport { ConfirmDialog } from '../../components/ConfirmDialog';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useSyncRuns, useCancelSyncRun } from '../../hooks/useAdapterCloudApi';\nimport { syncKindLabels, syncStatusLabels } from '../../utils/domain';\nimport { formatDate, formatRelativeTime } from '../../utils/formatters';\nimport type { AdaSyncRun, AdaSyncStatus } from '../../types';\n\nconst PAGE_SIZE = 20;\n\nexport function SyncRunsListPage() {\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const [status, setStatus] = useState<AdaSyncStatus | ''>('');\n const [offset, setOffset] = useState(0);\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmTarget, setConfirmTarget] = useState<AdaSyncRun | null>(null);\n\n const query = useSyncRuns({\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n const cancel = useCancelSyncRun();\n\n const items = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n // Per-id pending so cancelling one run only disables/spins that row's button.\n const pendingId = cancel.isPending ? cancel.variables : undefined;\n\n const handleCancel = useCallback(\n (id: string) => {\n setActionError(null);\n cancel.mutate(id, {\n onSuccess: () => setConfirmTarget(null),\n onError: (err) =>\n setActionError(err instanceof Error ? err.message : 'Failed to cancel run.'),\n });\n },\n [cancel]\n );\n\n return (\n <PageLayout\n title={t('adaptercloud.syncRuns.title', 'Sync Runs')}\n description={t(\n 'adaptercloud.syncRuns.subtitle',\n 'Discovery, reconciliation and cost-ingest executions that keep the picture live.'\n )}\n >\n <div className=\"mb-6 flex flex-wrap gap-3\">\n <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as AdaSyncStatus | '');\n setOffset(0);\n }}\n aria-label={t('adaptercloud.syncRuns.filterByStatus', 'Filter by status')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.syncRuns.allStatuses', 'All statuses')}</option>\n {(Object.keys(syncStatusLabels) as AdaSyncStatus[]).map((s) => (\n <option key={s} value={s}>\n {syncStatusLabels[s]}\n </option>\n ))}\n </select>\n </div>\n\n {actionError && (\n <p\n role=\"alert\"\n aria-live=\"polite\"\n className=\"mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {actionError}\n </p>\n )}\n\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={items.length === 0}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.syncRuns.emptyTitle', 'No sync runs yet')}\n emptyDescription={t(\n 'adaptercloud.syncRuns.emptyDescription',\n 'Start a sync run from a connection to keep the inventory current.'\n )}\n emptyIcon={<Activity className=\"h-10 w-10\" />}\n emptyAction={\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections')}\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.syncRuns.goToConnections', 'Go to connections')}\n </button>\n }\n >\n <div className=\"space-y-3\">\n {items.map((run) => {\n const cancellable = run.status === 'QUEUED' || run.status === 'RUNNING';\n return (\n <GlassCard key={run.id} className=\"p-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"mb-1.5 flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {syncKindLabels[run.kind]}\n </span>\n <StatusBadge status={run.status} />\n </div>\n <div className=\"flex flex-wrap items-center gap-4 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Server className=\"h-3.5 w-3.5\" /> {run.discoveredCount}{' '}\n {t('adaptercloud.syncRuns.discovered', 'discovered')}\n </span>\n <span className=\"flex items-center gap-1\">\n <RotateCcw className=\"h-3.5 w-3.5\" /> {run.reconciledCount}{' '}\n {t('adaptercloud.syncRuns.reconciled', 'reconciled')}\n </span>\n <span className=\"flex items-center gap-1\">\n <GitCompareArrows className=\"h-3.5 w-3.5\" /> {run.driftCount}{' '}\n {t('adaptercloud.syncRuns.drift', 'drift')}\n </span>\n <span className=\"flex items-center gap-1\">\n <ShieldAlert className=\"h-3.5 w-3.5\" /> {run.violationCount}{' '}\n {t('adaptercloud.syncRuns.violations', 'violations')}\n </span>\n </div>\n <p className=\"mt-2 text-xs text-text-secondary\">\n {run.startedAt\n ? `${t('adaptercloud.syncRuns.started', 'Started')} ${formatDate(run.startedAt)}`\n : `${t('adaptercloud.syncRuns.created', 'Created')} ${formatRelativeTime(run.createdAt)}`}\n {run.finishedAt &&\n ` · ${t('adaptercloud.syncRuns.finished', 'Finished')} ${formatDate(run.finishedAt)}`}\n {' · '}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/connections/${run.connectionId}`)}\n className=\"hover:underline\"\n >\n {t('adaptercloud.syncRuns.viewConnection', 'View connection')}\n </button>\n </p>\n {run.error && (\n <p className=\"mt-2 rounded-lg bg-status-error-bg-subtle px-3 py-1.5 text-xs text-status-error-text\">\n {run.error}\n </p>\n )}\n </div>\n\n {cancellable && (\n <button\n type=\"button\"\n disabled={pendingId === run.id}\n onClick={() => {\n setActionError(null);\n setConfirmTarget(run);\n }}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {pendingId === run.id ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <XCircle className=\"h-3.5 w-3.5\" />\n )}\n {t('adaptercloud.syncRuns.cancel', 'Cancel')}\n </button>\n )}\n </div>\n </GlassCard>\n );\n })}\n </div>\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-between\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.prev', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.common.showing', 'Showing')} {offset + 1}–{offset + items.length}\n </span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {t('adaptercloud.common.next', 'Next')}\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </DataState>\n\n <ConfirmDialog\n open={Boolean(confirmTarget)}\n onOpenChange={(open) => {\n if (!open) setConfirmTarget(null);\n }}\n title={t('adaptercloud.syncRuns.cancelTitle', 'Cancel this sync run?')}\n description={t(\n 'adaptercloud.syncRuns.cancelPrompt',\n 'The run will stop and transition to Cancelled. Any work already done is kept, but it will not finish.'\n )}\n confirmLabel={t('adaptercloud.syncRuns.confirmCancel', 'Cancel run')}\n cancelLabel={t('adaptercloud.syncRuns.keepRunning', 'Keep running')}\n onConfirm={() => confirmTarget && handleCancel(confirmTarget.id)}\n isPending={Boolean(confirmTarget) && pendingId === confirmTarget?.id}\n tone=\"danger\"\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SyncRunsListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/sync-runs/SyncRunsListPage.tsx"],"sourcesContent":["/**\n * Sync Runs History Page\n * @module adaptercloud/pages/sync-runs\n *\n * The operations log (`adaListSyncRuns`) — every discovery / reconcile /\n * cost-ingest / full execution with its lifecycle and counters. On md+ this\n * renders a dense operations table (Kind · Connection · Status · counters ·\n * Started · Duration) so runs compare at a glance; below 768px it reflows to a\n * card per run (via fe-libs `ResponsiveTable`). Filters by status; queued/running\n * runs can be cancelled via `adaCancelSyncRun`.\n */\n\nimport { useState, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Activity,\n ChevronLeft,\n ChevronRight,\n XCircle,\n Loader2,\n Server,\n Cloud,\n Radio,\n Cpu,\n Boxes,\n Network,\n Database,\n HardDrive,\n Puzzle,\n type LucideIcon,\n} from 'lucide-react';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { DataState } from '../../components/DataState';\nimport { ConfirmDialog } from '../../components/ConfirmDialog';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useSyncRuns, useCancelSyncRun } from '../../hooks/useAdapterCloudApi';\nimport { syncKindLabels, syncStatusLabels } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { AdaAdapterKind, AdaSyncKind, AdaSyncRun, AdaSyncStatus } from '../../types';\n\nconst PAGE_SIZE = 20;\n\n/**\n * Per-adapter-kind glyph — stands in for the vendor logos (aws / gcp / azure /\n * snowflake) in the mock. Keyed off the run's bound connection's adapter kind.\n */\nconst adapterKindIcon: Record<AdaAdapterKind, LucideIcon> = {\n PUBLIC_CLOUD: Cloud,\n PRIVATE_CLOUD: Cloud,\n SAAS: Cloud,\n ON_PREM: HardDrive,\n EDGE: Radio,\n IOT: Cpu,\n KUBERNETES: Boxes,\n NETWORK: Network,\n DATA_SYSTEM: Database,\n CUSTOM: Puzzle,\n};\n\n/** Colored kind pill (semantic tokens only), mirroring the mock's kind badges. */\nconst kindBadgeClass: Record<AdaSyncKind, string> = {\n DISCOVERY: 'bg-accent-blue-subtle text-accent-blue',\n RECONCILE: 'bg-status-warning-bg-subtle text-status-warning-text',\n COST_INGEST: 'bg-accent-purple-subtle text-accent-purple',\n FULL: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction KindBadge({ kind }: { kind: AdaSyncKind }) {\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n kindBadgeClass[kind]\n )}\n >\n {syncKindLabels[kind]}\n </span>\n );\n}\n\n/** Elapsed wall-clock between start and finish, e.g. \"3m 12s\". Null if unfinished. */\nfunction formatDuration(startedAt: string | null, finishedAt: string | null): string | null {\n if (!startedAt || !finishedAt) return null;\n const ms = new Date(finishedAt).getTime() - new Date(startedAt).getTime();\n if (!Number.isFinite(ms) || ms < 0) return null;\n const totalSeconds = Math.floor(ms / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;\n if (minutes > 0) return `${minutes}m ${seconds}s`;\n return `${seconds}s`;\n}\n\nexport function SyncRunsListPage() {\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const [status, setStatus] = useState<AdaSyncStatus | ''>('');\n const [offset, setOffset] = useState(0);\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmTarget, setConfirmTarget] = useState<AdaSyncRun | null>(null);\n\n const query = useSyncRuns({\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n const cancel = useCancelSyncRun();\n\n const items = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n // Per-id pending so cancelling one run only disables/spins that row's button.\n const pendingId = cancel.isPending ? cancel.variables : undefined;\n\n const handleCancel = useCallback(\n (id: string) => {\n setActionError(null);\n cancel.mutate(id, {\n onSuccess: () => setConfirmTarget(null),\n onError: (err) =>\n setActionError(err instanceof Error ? err.message : 'Failed to cancel run.'),\n });\n },\n [cancel]\n );\n\n // Counters are meaningless until a run has produced results — dim them to an\n // em-dash while queued/running, matching the mock.\n const counterCell = (run: AdaSyncRun, value: number) =>\n run.status === 'QUEUED' || run.status === 'RUNNING' ? (\n <span className=\"text-text-tertiary\">—</span>\n ) : (\n <span className=\"tabular-nums text-text-primary\">{value}</span>\n );\n\n const columns: ResponsiveTableColumn<AdaSyncRun>[] = [\n {\n key: 'kind',\n header: t('adaptercloud.syncRuns.kind', 'Kind'),\n priority: 'primary',\n cell: (run) => <KindBadge kind={run.kind} />,\n },\n {\n key: 'connection',\n header: t('adaptercloud.syncRuns.connection', 'Connection'),\n priority: 'secondary',\n cell: (run) => {\n const kind = run.connection?.adapter?.kind;\n const Icon = kind ? adapterKindIcon[kind] : Server;\n return (\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-bg-sunken text-text-secondary\">\n <Icon className=\"h-3.5 w-3.5\" />\n </span>\n {run.connection?.name ? (\n <span className=\"truncate text-sm text-text-primary\">{run.connection.name}</span>\n ) : (\n <span className=\"text-sm text-text-tertiary\">—</span>\n )}\n </div>\n );\n },\n },\n {\n key: 'status',\n header: t('adaptercloud.syncRuns.status', 'Status'),\n priority: 'secondary',\n cell: (run) => (\n <div className=\"flex flex-col gap-1\">\n <StatusBadge status={run.status} label={syncStatusLabels[run.status]} />\n {run.error && (\n <span className=\"max-w-xs truncate text-xs text-status-error-text\" title={run.error}>\n {run.error}\n </span>\n )}\n </div>\n ),\n },\n {\n key: 'discovered',\n header: t('adaptercloud.syncRuns.discovered', 'Discovered'),\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (run) => counterCell(run, run.discoveredCount),\n },\n {\n key: 'reconciled',\n header: t('adaptercloud.syncRuns.reconciled', 'Reconciled'),\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (run) => counterCell(run, run.reconciledCount),\n },\n {\n key: 'drift',\n header: t('adaptercloud.syncRuns.drift', 'Drift'),\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (run) => counterCell(run, run.driftCount),\n },\n {\n key: 'violations',\n header: t('adaptercloud.syncRuns.violations', 'Violations'),\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (run) => counterCell(run, run.violationCount),\n },\n {\n key: 'started',\n header: t('adaptercloud.syncRuns.started', 'Started'),\n priority: 'secondary',\n cell: (run) =>\n run.startedAt ? (\n <span className=\"whitespace-nowrap text-xs text-text-secondary\">\n {formatDate(run.startedAt)}\n </span>\n ) : (\n <span className=\"text-xs text-text-tertiary\">—</span>\n ),\n },\n {\n key: 'duration',\n header: t('adaptercloud.syncRuns.duration', 'Duration'),\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (run) => {\n const duration = formatDuration(run.startedAt, run.finishedAt);\n return duration ? (\n <span className=\"whitespace-nowrap text-xs tabular-nums text-text-secondary\">\n {duration}\n </span>\n ) : (\n <span className=\"text-xs text-text-tertiary\">—</span>\n );\n },\n },\n ];\n\n return (\n <PageLayout\n title={t('adaptercloud.syncRuns.title', 'Sync Runs')}\n description={t(\n 'adaptercloud.syncRuns.subtitle',\n 'Discovery, reconciliation and cost-ingest executions that keep the picture live.'\n )}\n >\n <div className=\"mb-6 flex flex-wrap gap-3\">\n <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as AdaSyncStatus | '');\n setOffset(0);\n }}\n aria-label={t('adaptercloud.syncRuns.filterByStatus', 'Filter by status')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.syncRuns.allStatuses', 'All statuses')}</option>\n {(Object.keys(syncStatusLabels) as AdaSyncStatus[]).map((s) => (\n <option key={s} value={s}>\n {syncStatusLabels[s]}\n </option>\n ))}\n </select>\n </div>\n\n {actionError && (\n <p\n role=\"alert\"\n aria-live=\"polite\"\n className=\"mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {actionError}\n </p>\n )}\n\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={items.length === 0}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.syncRuns.emptyTitle', 'No sync runs yet')}\n emptyDescription={t(\n 'adaptercloud.syncRuns.emptyDescription',\n 'Start a sync run from a connection to keep the inventory current.'\n )}\n emptyIcon={<Activity className=\"h-10 w-10\" />}\n emptyAction={\n <button\n type=\"button\"\n onClick={() => navigateTo('/connections')}\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.syncRuns.goToConnections', 'Go to connections')}\n </button>\n }\n >\n <ResponsiveTable\n data={items}\n columns={columns}\n rowKey={(run) => run.id}\n onRowClick={(run) => navigateTo(`/connections/${run.connectionId}`)}\n rowActions={(run) => {\n const cancellable = run.status === 'QUEUED' || run.status === 'RUNNING';\n if (!cancellable) return null;\n return (\n <button\n type=\"button\"\n disabled={pendingId === run.id}\n onClick={() => {\n setActionError(null);\n setConfirmTarget(run);\n }}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n {pendingId === run.id ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <XCircle className=\"h-3.5 w-3.5\" />\n )}\n {t('adaptercloud.syncRuns.cancel', 'Cancel')}\n </button>\n );\n }}\n />\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-between\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.prev', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.common.showing', 'Showing')} {offset + 1}–{offset + items.length}\n </span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {t('adaptercloud.common.next', 'Next')}\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </DataState>\n\n <ConfirmDialog\n open={Boolean(confirmTarget)}\n onOpenChange={(open) => {\n if (!open) setConfirmTarget(null);\n }}\n title={t('adaptercloud.syncRuns.cancelTitle', 'Cancel this sync run?')}\n description={t(\n 'adaptercloud.syncRuns.cancelPrompt',\n 'The run will stop and transition to Cancelled. Any work already done is kept, but it will not finish.'\n )}\n confirmLabel={t('adaptercloud.syncRuns.confirmCancel', 'Cancel run')}\n cancelLabel={t('adaptercloud.syncRuns.keepRunning', 'Keep running')}\n onConfirm={() => confirmTarget && handleCancel(confirmTarget.id)}\n isPending={Boolean(confirmTarget) && pendingId === confirmTarget?.id}\n tone=\"danger\"\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}