@burdenoff/microfe-adaptercloud 2026.706.1 → 2026.706.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) 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 +22 -14
  4. package/dist/adaptercloud/AdapterCloudRoutes.js.map +1 -1
  5. package/dist/adaptercloud/components/ConfirmDialog.js +27 -0
  6. package/dist/adaptercloud/components/ConfirmDialog.js.map +1 -0
  7. package/dist/adaptercloud/components/DataState.js +1 -1
  8. package/dist/adaptercloud/components/DataState.js.map +1 -1
  9. package/dist/adaptercloud/components/viz/BarChart.js +32 -0
  10. package/dist/adaptercloud/components/viz/BarChart.js.map +1 -0
  11. package/dist/adaptercloud/components/viz/Sparkline.js +50 -0
  12. package/dist/adaptercloud/components/viz/Sparkline.js.map +1 -0
  13. package/dist/adaptercloud/components/viz/StackedBar.js +1 -0
  14. package/dist/adaptercloud/components/viz/TopologyGraph.js +96 -0
  15. package/dist/adaptercloud/components/viz/TopologyGraph.js.map +1 -0
  16. package/dist/adaptercloud/components/viz/index.js +4 -0
  17. package/dist/adaptercloud/hooks/useAdapterCloudApi.js +71 -34
  18. package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
  19. package/dist/adaptercloud/pages/OverviewPage.js +159 -156
  20. package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
  21. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js +179 -113
  22. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js.map +1 -1
  23. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js +263 -179
  24. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js.map +1 -1
  25. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +2 -0
  26. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
  27. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +141 -124
  28. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
  29. package/dist/adaptercloud/pages/connections/ConnectionFormPage.js +2 -0
  30. package/dist/adaptercloud/pages/connections/ConnectionFormPage.js.map +1 -1
  31. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +158 -93
  32. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
  33. package/dist/adaptercloud/pages/costs/CostsPage.js +183 -109
  34. package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
  35. package/dist/adaptercloud/pages/drift/DriftListPage.js +172 -71
  36. package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
  37. package/dist/adaptercloud/pages/policies/PoliciesListPage.js +179 -96
  38. package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
  39. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +109 -91
  40. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -1
  41. package/dist/adaptercloud/pages/policies/PolicyFormPage.js +211 -184
  42. package/dist/adaptercloud/pages/policies/PolicyFormPage.js.map +1 -1
  43. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +183 -127
  44. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -1
  45. package/dist/adaptercloud/pages/resources/ResourcesListPage.js +121 -84
  46. package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
  47. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +236 -153
  48. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
  49. package/dist/adaptercloud/pages/topology/TopologyPage.js +397 -0
  50. package/dist/adaptercloud/pages/topology/TopologyPage.js.map +1 -0
  51. package/dist/adaptercloud/pages/violations/ViolationsListPage.js +192 -131
  52. package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
  53. package/package.json +1 -1
@@ -1,196 +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";
8
- import { useCallback as u, useState as d } from "react";
9
- import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
- import { Activity as p, ChevronLeft as m, ChevronRight as h, GitCompareArrows as g, Loader2 as _, RotateCcw as v, Server as y, ShieldAlert as b, XCircle as x } from "lucide-react";
11
- import { jsx as S, jsxs as C } from "react/jsx-runtime";
12
- import { GlassCard as w } from "@burdenoff/fe-libs/ui";
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";
9
+ import { ConfirmDialog as u } from "../../components/ConfirmDialog.js";
10
+ import { useCallback as d, useState as f } from "react";
11
+ import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
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";
13
15
  //#region src/adaptercloud/pages/sync-runs/SyncRunsListPage.tsx
14
- var T = 20;
15
- function E() {
16
- let { navigateTo: E } = e(), { t: D } = f(), O = u((e, t) => {
17
- let n = D(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);
18
49
  return n === e ? t : n;
19
- }, [D]), [k, A] = d(""), [j, M] = d(0), [N, P] = d(null), F = s({
20
- status: k || 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,
21
52
  pagination: {
22
- limit: T,
23
- offset: j
53
+ limit: j,
54
+ offset: b
55
+ }
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.")
60
+ });
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
+ }
24
171
  }
25
- }), I = o(), L = F.data?.items ?? [], R = F.data?.pageInfo.hasMore ?? !1, z = u((e) => {
26
- P(null), I.mutate(e, { onError: (e) => P(e instanceof Error ? e.message : "Failed to cancel run.") });
27
- }, [I]);
28
- return /* @__PURE__ */ C(t, {
29
- title: O("adaptercloud.syncRuns.title", "Sync Runs"),
30
- description: O("adaptercloud.syncRuns.subtitle", "Discovery, reconciliation and cost-ingest executions that keep the picture live."),
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."),
31
176
  children: [
32
- /* @__PURE__ */ S("div", {
177
+ /* @__PURE__ */ O("div", {
33
178
  className: "mb-6 flex flex-wrap gap-3",
34
- children: /* @__PURE__ */ C("select", {
35
- value: k,
179
+ children: /* @__PURE__ */ k("select", {
180
+ value: v,
36
181
  onChange: (e) => {
37
- A(e.target.value), M(0);
182
+ y(e.target.value), x(0);
38
183
  },
184
+ "aria-label": h("adaptercloud.syncRuns.filterByStatus", "Filter by status"),
39
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)]",
40
- children: [/* @__PURE__ */ S("option", {
186
+ children: [/* @__PURE__ */ O("option", {
41
187
  value: "",
42
- children: O("adaptercloud.syncRuns.allStatuses", "All statuses")
43
- }), Object.keys(r).map((e) => /* @__PURE__ */ S("option", {
188
+ children: h("adaptercloud.syncRuns.allStatuses", "All statuses")
189
+ }), Object.keys(i).map((e) => /* @__PURE__ */ O("option", {
44
190
  value: e,
45
- children: r[e]
191
+ children: i[e]
46
192
  }, e))]
47
193
  })
48
194
  }),
49
- N && /* @__PURE__ */ S("p", {
195
+ C && /* @__PURE__ */ O("p", {
196
+ role: "alert",
197
+ "aria-live": "polite",
50
198
  className: "mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
51
- children: N
199
+ children: C
52
200
  }),
53
- /* @__PURE__ */ C(a, {
54
- isLoading: F.isLoading,
55
- isError: F.isError,
56
- isEmpty: L.length === 0,
57
- error: F.error,
58
- onRetry: () => void F.refetch(),
59
- emptyTitle: O("adaptercloud.syncRuns.emptyTitle", "No sync runs yet"),
60
- emptyDescription: O("adaptercloud.syncRuns.emptyDescription", "Start a sync run from a connection to keep the inventory current."),
61
- emptyIcon: /* @__PURE__ */ S(p, { className: "h-10 w-10" }),
62
- emptyAction: /* @__PURE__ */ S("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", {
63
211
  type: "button",
64
- onClick: () => E("/connections"),
212
+ onClick: () => t("/connections"),
65
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",
66
- children: O("adaptercloud.syncRuns.goToConnections", "Go to connections")
214
+ children: h("adaptercloud.syncRuns.goToConnections", "Go to connections")
67
215
  }),
68
- children: [/* @__PURE__ */ S("div", {
69
- className: "space-y-3",
70
- children: L.map((e) => {
71
- let t = e.status === "QUEUED" || e.status === "RUNNING";
72
- return /* @__PURE__ */ S(w, {
73
- className: "p-5",
74
- children: /* @__PURE__ */ C("div", {
75
- className: "flex flex-wrap items-start justify-between gap-3",
76
- children: [/* @__PURE__ */ C("div", {
77
- className: "min-w-0 flex-1",
78
- children: [
79
- /* @__PURE__ */ C("div", {
80
- className: "mb-1.5 flex flex-wrap items-center gap-2",
81
- children: [/* @__PURE__ */ S("span", {
82
- className: "text-sm font-semibold text-text-primary",
83
- children: n[e.kind]
84
- }), /* @__PURE__ */ S(i, { status: e.status })]
85
- }),
86
- /* @__PURE__ */ C("div", {
87
- className: "flex flex-wrap items-center gap-4 text-xs text-text-secondary",
88
- children: [
89
- /* @__PURE__ */ C("span", {
90
- className: "flex items-center gap-1",
91
- children: [
92
- /* @__PURE__ */ S(y, { className: "h-3.5 w-3.5" }),
93
- " ",
94
- e.discoveredCount,
95
- " ",
96
- O("adaptercloud.syncRuns.discovered", "discovered")
97
- ]
98
- }),
99
- /* @__PURE__ */ C("span", {
100
- className: "flex items-center gap-1",
101
- children: [
102
- /* @__PURE__ */ S(v, { className: "h-3.5 w-3.5" }),
103
- " ",
104
- e.reconciledCount,
105
- " ",
106
- O("adaptercloud.syncRuns.reconciled", "reconciled")
107
- ]
108
- }),
109
- /* @__PURE__ */ C("span", {
110
- className: "flex items-center gap-1",
111
- children: [
112
- /* @__PURE__ */ S(g, { className: "h-3.5 w-3.5" }),
113
- " ",
114
- e.driftCount,
115
- " ",
116
- O("adaptercloud.syncRuns.drift", "drift")
117
- ]
118
- }),
119
- /* @__PURE__ */ C("span", {
120
- className: "flex items-center gap-1",
121
- children: [
122
- /* @__PURE__ */ S(b, { className: "h-3.5 w-3.5" }),
123
- " ",
124
- e.violationCount,
125
- " ",
126
- O("adaptercloud.syncRuns.violations", "violations")
127
- ]
128
- })
129
- ]
130
- }),
131
- /* @__PURE__ */ C("p", {
132
- className: "mt-2 text-xs text-text-secondary",
133
- children: [
134
- e.startedAt ? `${O("adaptercloud.syncRuns.started", "Started")} ${c(e.startedAt)}` : `${O("adaptercloud.syncRuns.created", "Created")} ${l(e.createdAt)}`,
135
- e.finishedAt && ` · ${O("adaptercloud.syncRuns.finished", "Finished")} ${c(e.finishedAt)}`,
136
- " · ",
137
- /* @__PURE__ */ S("button", {
138
- type: "button",
139
- onClick: () => E(`/connections/${e.connectionId}`),
140
- className: "hover:underline",
141
- children: O("adaptercloud.syncRuns.viewConnection", "View connection")
142
- })
143
- ]
144
- }),
145
- e.error && /* @__PURE__ */ S("p", {
146
- className: "mt-2 rounded-lg bg-status-error-bg-subtle px-3 py-1.5 text-xs text-status-error-text",
147
- children: e.error
148
- })
149
- ]
150
- }), t && /* @__PURE__ */ C("button", {
151
- type: "button",
152
- disabled: I.isPending,
153
- onClick: () => z(e.id),
154
- 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",
155
- children: [I.isPending ? /* @__PURE__ */ S(_, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ S(x, { className: "h-3.5 w-3.5" }), O("adaptercloud.syncRuns.cancel", "Cancel")]
156
- })]
157
- })
158
- }, e.id);
159
- })
160
- }), (j > 0 || R) && /* @__PURE__ */ C("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", {
161
231
  className: "mt-6 flex items-center justify-between",
162
232
  children: [
163
- /* @__PURE__ */ C("button", {
233
+ /* @__PURE__ */ k("button", {
164
234
  type: "button",
165
- disabled: j === 0,
166
- onClick: () => M((e) => Math.max(0, e - T)),
235
+ disabled: b === 0,
236
+ onClick: () => x((e) => Math.max(0, e - j)),
167
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",
168
- children: [/* @__PURE__ */ S(m, { className: "h-4 w-4" }), O("adaptercloud.common.prev", "Previous")]
238
+ children: [/* @__PURE__ */ O(g, { className: "h-4 w-4" }), h("adaptercloud.common.prev", "Previous")]
169
239
  }),
170
- /* @__PURE__ */ C("span", {
240
+ /* @__PURE__ */ k("span", {
171
241
  className: "text-sm text-text-secondary",
172
242
  children: [
173
- O("adaptercloud.common.showing", "Showing"),
243
+ h("adaptercloud.common.showing", "Showing"),
174
244
  " ",
175
- j + 1,
245
+ b + 1,
176
246
  "–",
177
- j + L.length
247
+ b + R.length
178
248
  ]
179
249
  }),
180
- /* @__PURE__ */ C("button", {
250
+ /* @__PURE__ */ k("button", {
181
251
  type: "button",
182
- disabled: !R,
183
- onClick: () => M((e) => e + T),
252
+ disabled: !z,
253
+ onClick: () => x((e) => e + j),
184
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",
185
- children: [O("adaptercloud.common.next", "Next"), /* @__PURE__ */ S(h, { className: "h-4 w-4" })]
255
+ children: [h("adaptercloud.common.next", "Next"), /* @__PURE__ */ O(_, { className: "h-4 w-4" })]
186
256
  })
187
257
  ]
188
258
  })]
259
+ }),
260
+ /* @__PURE__ */ O(u, {
261
+ open: !!T,
262
+ onOpenChange: (e) => {
263
+ e || N(null);
264
+ },
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,
271
+ tone: "danger"
189
272
  })
190
273
  ]
191
274
  });
192
275
  }
193
276
  //#endregion
194
- export { E as SyncRunsListPage };
277
+ export { I as SyncRunsListPage };
195
278
 
196
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 { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useSyncRuns, useCancelSyncRun } from '../../hooks/useAdapterCloudApi';\nimport { syncKindLabels, syncStatusLabels } from '../../utils/domain';\nimport { formatDate, formatRelativeTime } from '../../utils/formatters';\nimport type { 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\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 const handleCancel = useCallback(\n (id: string) => {\n setActionError(null);\n cancel.mutate(id, {\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 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 className=\"mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\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={cancel.isPending}\n onClick={() => handleCancel(run.id)}\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 {cancel.isPending ? (\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 </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":";;;;;;;;;;;;;;;AA2CA,IAAM,IAAY,IAMZ,IAAsD;CAC1D,cAAc;CACd,eAAe;CACf,MAAM;CACN,SAAS;CACT,MAAM;CACN,KAAK;CACL,YAAY;CACZ,SAAS;CACT,aAAa;CACb,QAAQ;CACT,EAGK,IAA8C;CAClD,WAAW;CACX,WAAW;CACX,aAAa;CACb,MAAM;CACP;AAED,SAAS,EAAU,EAAE,WAA+B;AAClD,QACE,kBAAC,QAAD;EACE,WAAW,EACT,2EACA,EAAe,GAChB;YAEA,EAAe;EACX,CAAA;;AAKX,SAAS,EAAe,GAA0B,GAA0C;AAC1F,KAAI,CAAC,KAAa,CAAC,EAAY,QAAO;CACtC,IAAM,IAAK,IAAI,KAAK,EAAW,CAAC,SAAS,GAAG,IAAI,KAAK,EAAU,CAAC,SAAS;AACzE,KAAI,CAAC,OAAO,SAAS,EAAG,IAAI,IAAK,EAAG,QAAO;CAC3C,IAAM,IAAe,KAAK,MAAM,IAAK,IAAK,EACpC,IAAQ,KAAK,MAAM,IAAe,KAAK,EACvC,IAAU,KAAK,MAAO,IAAe,OAAQ,GAAG,EAChD,IAAU,IAAe;AAG/B,QAFI,IAAQ,IAAU,GAAG,EAAM,IAAI,EAAQ,IAAI,EAAQ,KACnD,IAAU,IAAU,GAAG,EAAQ,IAAI,EAAQ,KACxC,GAAG,EAAQ;;AAGpB,SAAgB,IAAmB;CACjC,IAAM,EAAE,kBAAe,GAAiB,EAClC,EAAE,GAAG,MAAc,GAAS,EAC5B,IAAI,GACP,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAU,CACZ,EAEK,CAAC,GAAQ,KAAa,EAA6B,GAAG,EACtD,CAAC,GAAQ,KAAa,EAAS,EAAE,EACjC,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAe,KAAoB,EAA4B,KAAK,EAErE,IAAQ,EAAY;EACxB,QAAQ,KAAU,KAAA;EAClB,YAAY;GAAE,OAAO;GAAW;GAAQ;EACzC,CAAC,EACI,IAAS,GAAkB,EAE3B,IAAQ,EAAM,MAAM,SAAS,EAAE,EAC/B,IAAU,EAAM,MAAM,SAAS,WAAW,IAG1C,IAAY,EAAO,YAAY,EAAO,YAAY,KAAA,GAElD,IAAe,GAClB,MAAe;AAEd,EADA,EAAe,KAAK,EACpB,EAAO,OAAO,GAAI;GAChB,iBAAiB,EAAiB,KAAK;GACvC,UAAU,MACR,EAAe,aAAe,QAAQ,EAAI,UAAU,wBAAwB;GAC/E,CAAC;IAEJ,CAAC,EAAO,CACT,EAIK,KAAe,GAAiB,MACpC,EAAI,WAAW,YAAY,EAAI,WAAW,YACxC,kBAAC,QAAD;EAAM,WAAU;YAAqB;EAAQ,CAAA,GAE7C,kBAAC,QAAD;EAAM,WAAU;YAAkC;EAAa,CAAA,EAG7D,IAA+C;EACnD;GACE,KAAK;GACL,QAAQ,EAAE,8BAA8B,OAAO;GAC/C,UAAU;GACV,OAAO,MAAQ,kBAAC,GAAD,EAAW,MAAM,EAAI,MAAQ,CAAA;GAC7C;EACD;GACE,KAAK;GACL,QAAQ,EAAE,oCAAoC,aAAa;GAC3D,UAAU;GACV,OAAO,MAAQ;IACb,IAAM,IAAO,EAAI,YAAY,SAAS;AAEtC,WACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBACd,kBAJO,IAAO,EAAgB,KAAQ,GAItC,EAAM,WAAU,eAAgB,CAAA;MAC3B,CAAA,EACN,EAAI,YAAY,OACf,kBAAC,QAAD;MAAM,WAAU;gBAAsC,EAAI,WAAW;MAAY,CAAA,GAEjF,kBAAC,QAAD;MAAM,WAAU;gBAA6B;MAAQ,CAAA,CAEnD;;;GAGX;EACD;GACE,KAAK;GACL,QAAQ,EAAE,gCAAgC,SAAS;GACnD,UAAU;GACV,OAAO,MACL,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAa,QAAQ,EAAI;KAAQ,OAAO,EAAiB,EAAI;KAAW,CAAA,EACvE,EAAI,SACH,kBAAC,QAAD;KAAM,WAAU;KAAmD,OAAO,EAAI;eAC3E,EAAI;KACA,CAAA,CAEL;;GAET;EACD;GACE,KAAK;GACL,QAAQ,EAAE,oCAAoC,aAAa;GAC3D,UAAU;GACV,iBAAiB;GACjB,eAAe;GACf,OAAO,MAAQ,EAAY,GAAK,EAAI,gBAAgB;GACrD;EACD;GACE,KAAK;GACL,QAAQ,EAAE,oCAAoC,aAAa;GAC3D,UAAU;GACV,iBAAiB;GACjB,eAAe;GACf,OAAO,MAAQ,EAAY,GAAK,EAAI,gBAAgB;GACrD;EACD;GACE,KAAK;GACL,QAAQ,EAAE,+BAA+B,QAAQ;GACjD,UAAU;GACV,iBAAiB;GACjB,eAAe;GACf,OAAO,MAAQ,EAAY,GAAK,EAAI,WAAW;GAChD;EACD;GACE,KAAK;GACL,QAAQ,EAAE,oCAAoC,aAAa;GAC3D,UAAU;GACV,iBAAiB;GACjB,eAAe;GACf,OAAO,MAAQ,EAAY,GAAK,EAAI,eAAe;GACpD;EACD;GACE,KAAK;GACL,QAAQ,EAAE,iCAAiC,UAAU;GACrD,UAAU;GACV,OAAO,MACL,EAAI,YACF,kBAAC,QAAD;IAAM,WAAU;cACb,EAAW,EAAI,UAAU;IACrB,CAAA,GAEP,kBAAC,QAAD;IAAM,WAAU;cAA6B;IAAQ,CAAA;GAE1D;EACD;GACE,KAAK;GACL,QAAQ,EAAE,kCAAkC,WAAW;GACvD,UAAU;GACV,iBAAiB;GACjB,eAAe;GACf,OAAO,MAAQ;IACb,IAAM,IAAW,EAAe,EAAI,WAAW,EAAI,WAAW;AAC9D,WAAO,IACL,kBAAC,QAAD;KAAM,WAAU;eACb;KACI,CAAA,GAEP,kBAAC,QAAD;KAAM,WAAU;eAA6B;KAAQ,CAAA;;GAG1D;EACF;AAED,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,+BAA+B,YAAY;EACpD,aAAa,EACX,kCACA,mFACD;YALH;GAOE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,OAAO;KACP,WAAW,MAAM;AAEf,MADA,EAAU,EAAE,OAAO,MAA4B,EAC/C,EAAU,EAAE;;KAEd,cAAY,EAAE,wCAAwC,mBAAmB;KACzE,WAAU;eAPZ,CASE,kBAAC,UAAD;MAAQ,OAAM;gBAAI,EAAE,qCAAqC,eAAe;MAAU,CAAA,EAChF,OAAO,KAAK,EAAiB,CAAqB,KAAK,MACvD,kBAAC,UAAD;MAAgB,OAAO;gBACpB,EAAiB;MACX,EAFI,EAEJ,CACT,CACK;;IACL,CAAA;GAEL,KACC,kBAAC,KAAD;IACE,MAAK;IACL,aAAU;IACV,WAAU;cAET;IACC,CAAA;GAGN,kBAAC,GAAD;IACE,WAAW,EAAM;IACjB,SAAS,EAAM;IACf,SAAS,EAAM,WAAW;IAC1B,OAAO,EAAM;IACb,eAAe,KAAK,EAAM,SAAS;IACnC,YAAY,EAAE,oCAAoC,mBAAmB;IACrE,kBAAkB,EAChB,0CACA,oEACD;IACD,WAAW,kBAAC,GAAD,EAAU,WAAU,aAAc,CAAA;IAC7C,aACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,eAAe;KACzC,WAAU;eAET,EAAE,yCAAyC,oBAAoB;KACzD,CAAA;cAnBb,CAsBE,kBAAC,GAAD;KACE,MAAM;KACG;KACT,SAAS,MAAQ,EAAI;KACrB,aAAa,MAAQ,EAAW,gBAAgB,EAAI,eAAe;KACnE,aAAa,MACS,EAAI,WAAW,YAAY,EAAI,WAAW,YAG5D,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,MAAc,EAAI;MAC5B,eAAe;AAEb,OADA,EAAe,KAAK,EACpB,EAAiB,EAAI;;MAEvB,WAAU;gBAPZ,CASG,MAAc,EAAI,KACjB,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,kBAAC,GAAD,EAAS,WAAU,eAAgB,CAAA,EAEpC,EAAE,gCAAgC,SAAS,CACrC;UAjBc;KAoB3B,CAAA,GAEA,IAAS,KAAK,MACd,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAW;OACrB,eAAe,GAAW,MAAM,KAAK,IAAI,GAAG,IAAI,EAAU,CAAC;OAC3D,WAAU;iBAJZ,CAME,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA,EAClC,EAAE,4BAA4B,WAAW,CACnC;;MACT,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG,EAAE,+BAA+B,UAAU;QAAC;QAAE,IAAS;QAAE;QAAE,IAAS,EAAM;QACtE;;MACP,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,CAAC;OACX,eAAe,GAAW,MAAM,IAAI,EAAU;OAC9C,WAAU;iBAJZ,CAMG,EAAE,4BAA4B,OAAO,EACtC,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,CAC7B;;MACL;OAEE;;GAEZ,kBAAC,GAAD;IACE,MAAM,EAAQ;IACd,eAAe,MAAS;AACtB,KAAK,KAAM,EAAiB,KAAK;;IAEnC,OAAO,EAAE,qCAAqC,wBAAwB;IACtE,aAAa,EACX,sCACA,wGACD;IACD,cAAc,EAAE,uCAAuC,aAAa;IACpE,aAAa,EAAE,qCAAqC,eAAe;IACnE,iBAAiB,KAAiB,EAAa,EAAc,GAAG;IAChE,WAAW,EAAQ,KAAkB,MAAc,GAAe;IAClE,MAAK;IACL,CAAA;GACS"}