@burdenoff/microfe-adaptercloud 2026.825.4 → 2026.829.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/adaptercloud/AdapterCloudRoot.d.ts.map +1 -1
  2. package/dist/adaptercloud/AdapterCloudRoot.js +52 -55
  3. package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
  4. package/dist/adaptercloud/components/ActiveSwitch.d.ts +17 -0
  5. package/dist/adaptercloud/components/ActiveSwitch.d.ts.map +1 -0
  6. package/dist/adaptercloud/components/ActiveSwitch.js +28 -0
  7. package/dist/adaptercloud/components/ActiveSwitch.js.map +1 -0
  8. package/dist/adaptercloud/components/index.d.ts +1 -0
  9. package/dist/adaptercloud/components/index.d.ts.map +1 -1
  10. package/dist/adaptercloud/hooks/index.d.ts +1 -0
  11. package/dist/adaptercloud/hooks/index.d.ts.map +1 -1
  12. package/dist/adaptercloud/hooks/useAdapterCloudApi.d.ts +28 -2
  13. package/dist/adaptercloud/hooks/useAdapterCloudApi.d.ts.map +1 -1
  14. package/dist/adaptercloud/hooks/useAdapterCloudApi.js +318 -291
  15. package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
  16. package/dist/adaptercloud/hooks/useTr.d.ts +2 -0
  17. package/dist/adaptercloud/hooks/useTr.d.ts.map +1 -0
  18. package/dist/adaptercloud/hooks/useTr.js +14 -0
  19. package/dist/adaptercloud/hooks/useTr.js.map +1 -0
  20. package/dist/adaptercloud/pages/OverviewPage.d.ts.map +1 -1
  21. package/dist/adaptercloud/pages/OverviewPage.js +117 -120
  22. package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
  23. package/dist/adaptercloud/pages/adapters/AdaptersListPage.d.ts.map +1 -1
  24. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +6 -5
  25. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
  26. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.d.ts.map +1 -1
  27. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +217 -104
  28. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
  29. package/dist/adaptercloud/pages/connections/ConnectionsListPage.d.ts.map +1 -1
  30. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +6 -5
  31. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
  32. package/dist/adaptercloud/pages/costs/CostsPage.d.ts.map +1 -1
  33. package/dist/adaptercloud/pages/costs/CostsPage.js +183 -156
  34. package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
  35. package/dist/adaptercloud/pages/drift/DriftListPage.d.ts.map +1 -1
  36. package/dist/adaptercloud/pages/drift/DriftListPage.js +108 -107
  37. package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
  38. package/dist/adaptercloud/pages/policies/PoliciesListPage.d.ts.map +1 -1
  39. package/dist/adaptercloud/pages/policies/PoliciesListPage.js +141 -131
  40. package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
  41. package/dist/adaptercloud/pages/policies/PolicyDetailPage.d.ts.map +1 -1
  42. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +171 -117
  43. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -1
  44. package/dist/adaptercloud/pages/resources/ResourcesListPage.d.ts.map +1 -1
  45. package/dist/adaptercloud/pages/resources/ResourcesListPage.js +85 -66
  46. package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
  47. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.d.ts.map +1 -1
  48. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +148 -121
  49. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
  50. package/dist/adaptercloud/pages/violations/ViolationsListPage.d.ts.map +1 -1
  51. package/dist/adaptercloud/pages/violations/ViolationsListPage.js +145 -144
  52. package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
  53. package/dist/utils/pageContext.d.ts +0 -2
  54. package/dist/utils/pageContext.d.ts.map +1 -1
  55. package/dist/utils/pageContext.js +1 -3
  56. package/dist/utils/pageContext.js.map +1 -1
  57. package/package.json +1 -1
@@ -9,304 +9,305 @@ import { useResolveViolation as o, useViolations as s } from "../../hooks/useAda
9
9
  import { formatDate as c } from "../../utils/formatters.js";
10
10
  import { useCallback as l, useState as u } from "react";
11
11
  import { useI18n as ne } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
- import { Check as re, CheckCheck as d, ChevronDown as f, ChevronLeft as p, ChevronRight as m, FileText as h, Loader2 as g, ShieldAlert as _ } from "lucide-react";
13
- import { useSearchParams as v } from "react-router";
14
- import { jsx as y, jsxs as b } from "react/jsx-runtime";
15
- import { GlassCard as x, ResponsiveDialog as S, ResponsiveDialogContent as C, ResponsiveDialogFooter as w, ResponsiveDialogHeader as T, ResponsiveDialogTitle as E } from "@burdenoff/fe-libs/ui";
12
+ import { Check as re, CheckCheck as ie, ChevronDown as d, ChevronLeft as f, ChevronRight as p, FileText as m, Loader2 as h, ShieldAlert as g } from "lucide-react";
13
+ import { useSearchParams as _ } from "react-router";
14
+ import { jsx as v, jsxs as y } from "react/jsx-runtime";
15
+ import { GlassCard as b, ResponsiveDialog as x, ResponsiveDialogContent as S, ResponsiveDialogFooter as C, ResponsiveDialogHeader as w, ResponsiveDialogTitle as T } from "@burdenoff/fe-libs/ui";
16
16
  //#region src/adaptercloud/pages/violations/ViolationsListPage.tsx
17
- var ie = [
17
+ var ae = [
18
18
  "",
19
19
  "OPEN",
20
20
  "ACKNOWLEDGED",
21
21
  "RESOLVED",
22
22
  "WAIVED"
23
- ], D = 20;
24
- function O() {
25
- let { navigateTo: O } = e(), [k] = v(), A = k.get("policyId") ?? void 0, j = k.get("resourceId") ?? void 0, { t: M } = ne(), N = l((e, t) => {
26
- let n = M(e);
23
+ ], E = 20;
24
+ function D() {
25
+ let { navigateTo: D } = e(), [O] = _(), k = O.get("policyId") ?? void 0, A = O.get("resourceId") ?? void 0, { t: j } = ne(), M = l((e, t) => {
26
+ let n = j(e);
27
27
  return n === e ? t : n;
28
- }, [M]), [P, F] = u("OPEN"), [I, L] = u(0), [R, z] = u(null), [B, V] = u(""), [H, U] = u(null), [W, G] = u(/* @__PURE__ */ new Set()), K = s({
29
- policyId: A,
30
- resourceId: j,
31
- status: P || void 0,
28
+ }, [j]), [N, P] = u("OPEN"), [F, I] = u(0), [L, R] = u(null), [z, B] = u(""), [V, H] = u(null), [U, W] = u(/* @__PURE__ */ new Set()), G = s({
29
+ policyId: k,
30
+ resourceId: A,
31
+ status: N || void 0,
32
32
  pagination: {
33
- limit: D,
34
- offset: I
33
+ limit: E,
34
+ offset: F
35
35
  }
36
- }), q = o(), J = {
37
- policyId: A,
38
- resourceId: j,
36
+ }), K = o(), q = {
37
+ policyId: k,
38
+ resourceId: A,
39
39
  pagination: {
40
40
  limit: 1,
41
41
  offset: 0
42
42
  }
43
- }, ae = s(J), oe = s({
44
- ...J,
43
+ }, oe = s(q), se = s({
44
+ ...q,
45
45
  status: "OPEN"
46
- }), se = s({
47
- ...J,
48
- status: "ACKNOWLEDGED"
49
46
  }), ce = s({
50
- ...J,
51
- status: "RESOLVED"
47
+ ...q,
48
+ status: "ACKNOWLEDGED"
52
49
  }), le = s({
53
- ...J,
50
+ ...q,
51
+ status: "RESOLVED"
52
+ }), ue = s({
53
+ ...q,
54
54
  status: "WAIVED"
55
- }), ue = {
56
- "": ae.data?.pageInfo.totalCount ?? void 0,
57
- OPEN: oe.data?.pageInfo.totalCount ?? void 0,
58
- ACKNOWLEDGED: se.data?.pageInfo.totalCount ?? void 0,
59
- RESOLVED: ce.data?.pageInfo.totalCount ?? void 0,
60
- WAIVED: le.data?.pageInfo.totalCount ?? void 0
61
- }, Y = l((e) => {
62
- G((t) => {
55
+ }), de = {
56
+ "": oe.data?.pageInfo.totalCount ?? void 0,
57
+ OPEN: se.data?.pageInfo.totalCount ?? void 0,
58
+ ACKNOWLEDGED: ce.data?.pageInfo.totalCount ?? void 0,
59
+ RESOLVED: le.data?.pageInfo.totalCount ?? void 0,
60
+ WAIVED: ue.data?.pageInfo.totalCount ?? void 0
61
+ }, J = l((e) => {
62
+ W((t) => {
63
63
  let n = new Set(t);
64
64
  return n.has(e) ? n.delete(e) : n.add(e), n;
65
65
  });
66
- }, []), X = [...K.data?.items ?? []].sort((e, t) => r[t.severity] - r[e.severity]), Z = K.data?.pageInfo.hasMore ?? !1, Q = l((e) => U(e instanceof Error ? e.message : "Action failed. Please try again."), []), $ = l((e, t) => {
67
- U(null), q.mutate({
66
+ }, []), Y = [...G.data?.items ?? []].sort((e, t) => r[t.severity] - r[e.severity]), X = G.data?.pageInfo.hasMore ?? !1, Z = G.data?.pageInfo.totalCount, Q = l((e) => H(e instanceof Error ? e.message : "Action failed. Please try again."), []), $ = l((e, t) => {
67
+ H(null), K.mutate({
68
68
  id: e,
69
69
  status: t
70
70
  }, { onError: Q });
71
- }, [q, Q]), de = l(() => {
72
- if (R) {
73
- if (!B.trim()) {
74
- U(N("adaptercloud.violations.reasonRequired", "A waiver requires a reason."));
71
+ }, [K, Q]), fe = l(() => {
72
+ if (L) {
73
+ if (!z.trim()) {
74
+ H(M("adaptercloud.violations.reasonRequired", "A waiver requires a reason."));
75
75
  return;
76
76
  }
77
- U(null), q.mutate({
78
- id: R.id,
77
+ H(null), K.mutate({
78
+ id: L.id,
79
79
  status: "WAIVED",
80
- waiverReason: B.trim()
80
+ waiverReason: z.trim()
81
81
  }, {
82
82
  onSuccess: () => {
83
- z(null), V("");
83
+ R(null), B("");
84
84
  },
85
85
  onError: Q
86
86
  });
87
87
  }
88
88
  }, [
89
- R,
90
- B,
91
- q,
89
+ L,
90
+ z,
91
+ K,
92
92
  Q,
93
- N
93
+ M
94
94
  ]);
95
- return /* @__PURE__ */ b(n, {
96
- title: N("adaptercloud.violations.title", "Violations"),
97
- description: N("adaptercloud.violations.subtitle", "Work the governance queue — acknowledge, resolve, or waive policy breaches."),
95
+ return /* @__PURE__ */ y(n, {
96
+ title: M("adaptercloud.violations.title", "Violations"),
97
+ description: M("adaptercloud.violations.subtitle", "Work the governance queue — acknowledge, resolve, or waive policy breaches."),
98
98
  children: [
99
- /* @__PURE__ */ b("div", {
99
+ /* @__PURE__ */ y("div", {
100
100
  className: "mb-6 flex flex-wrap items-center gap-2",
101
101
  role: "tablist",
102
- "aria-label": N("adaptercloud.violations.filterByStatus", "Filter by status"),
103
- children: [ie.map((e) => {
104
- let n = P === e, r = ue[e], a = e === "" ? N("adaptercloud.violations.allStatuses", "All") : i[e];
105
- return /* @__PURE__ */ b("button", {
102
+ "aria-label": M("adaptercloud.violations.filterByStatus", "Filter by status"),
103
+ children: [ae.map((e) => {
104
+ let n = N === e, r = de[e], a = e === "" ? M("adaptercloud.violations.allStatuses", "All") : i[e];
105
+ return /* @__PURE__ */ y("button", {
106
106
  type: "button",
107
107
  role: "tab",
108
108
  "aria-selected": n,
109
109
  onClick: () => {
110
- F(e), L(0);
110
+ P(e), I(0);
111
111
  },
112
112
  className: t("inline-flex items-center gap-2 rounded-full border px-3.5 py-1.5 text-sm font-medium transition-colors", n ? "border-transparent bg-action-primary-bg text-action-primary-text" : "border-border-subtle text-text-secondary hover:bg-accent"),
113
- children: [a, r !== void 0 && /* @__PURE__ */ y("span", {
113
+ children: [a, r !== void 0 && /* @__PURE__ */ v("span", {
114
114
  className: t("inline-flex min-w-5 items-center justify-center rounded-full px-1.5 text-xs font-semibold tabular-nums", n ? "bg-action-primary-text/20 text-action-primary-text" : "bg-bg-sunken text-text-secondary"),
115
115
  children: r
116
116
  })]
117
117
  }, e || "all");
118
- }), (A || j) && /* @__PURE__ */ y("button", {
118
+ }), (k || A) && /* @__PURE__ */ v("button", {
119
119
  type: "button",
120
- onClick: () => O("/violations"),
120
+ onClick: () => D("/violations"),
121
121
  className: "inline-flex items-center gap-1.5 rounded-full bg-accent-blue-subtle px-2.5 py-1 text-xs text-accent-blue hover:underline",
122
- children: N("adaptercloud.violations.clearScope", "Clear scope ×")
122
+ children: M("adaptercloud.violations.clearScope", "Clear scope ×")
123
123
  })]
124
124
  }),
125
- H && /* @__PURE__ */ y("p", {
125
+ V && /* @__PURE__ */ v("p", {
126
126
  role: "alert",
127
127
  "aria-live": "polite",
128
128
  className: "mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
129
- children: H
129
+ children: V
130
130
  }),
131
- /* @__PURE__ */ b(te, {
132
- isLoading: K.isLoading,
133
- isError: K.isError,
134
- isEmpty: X.length === 0,
135
- error: K.error,
136
- onRetry: () => void K.refetch(),
137
- emptyTitle: N("adaptercloud.violations.emptyTitle", "No violations"),
138
- emptyDescription: N("adaptercloud.violations.emptyDescription", "Nothing matches this filter — your estate is clean here."),
139
- emptyIcon: /* @__PURE__ */ y(_, { className: "h-10 w-10" }),
140
- children: [/* @__PURE__ */ y("div", {
131
+ /* @__PURE__ */ y(te, {
132
+ isLoading: G.isLoading,
133
+ isError: G.isError,
134
+ isEmpty: Y.length === 0,
135
+ error: G.error,
136
+ onRetry: () => void G.refetch(),
137
+ emptyTitle: M("adaptercloud.violations.emptyTitle", "No violations"),
138
+ emptyDescription: M("adaptercloud.violations.emptyDescription", "Nothing matches this filter — your estate is clean here."),
139
+ emptyIcon: /* @__PURE__ */ v(g, { className: "h-10 w-10" }),
140
+ children: [/* @__PURE__ */ v("div", {
141
141
  className: "space-y-3",
142
- children: X.map((e) => {
142
+ children: Y.map((e) => {
143
143
  let t = e.status === "OPEN" || e.status === "ACKNOWLEDGED";
144
- return /* @__PURE__ */ y(x, {
144
+ return /* @__PURE__ */ v(b, {
145
145
  className: "p-5",
146
- children: /* @__PURE__ */ b("div", {
146
+ children: /* @__PURE__ */ y("div", {
147
147
  className: "flex flex-wrap items-start justify-between gap-3",
148
- children: [/* @__PURE__ */ b("div", {
148
+ children: [/* @__PURE__ */ y("div", {
149
149
  className: "min-w-0 flex-1",
150
150
  children: [
151
- /* @__PURE__ */ b("div", {
151
+ /* @__PURE__ */ y("div", {
152
152
  className: "mb-1.5 flex flex-wrap items-center gap-2",
153
153
  children: [
154
- /* @__PURE__ */ y(ee, { severity: e.severity }),
155
- /* @__PURE__ */ y(a, { status: e.status }),
156
- e.policy && /* @__PURE__ */ y("button", {
154
+ /* @__PURE__ */ v(ee, { severity: e.severity }),
155
+ /* @__PURE__ */ v(a, { status: e.status }),
156
+ e.policy && /* @__PURE__ */ v("button", {
157
157
  type: "button",
158
- onClick: () => O(`/policies/${e.policyId}`),
158
+ onClick: () => D(`/policies/${e.policyId}`),
159
159
  className: "text-xs font-medium text-accent-blue hover:underline",
160
160
  children: e.policy.name
161
161
  })
162
162
  ]
163
163
  }),
164
- /* @__PURE__ */ y("p", {
164
+ /* @__PURE__ */ v("p", {
165
165
  className: "text-sm font-medium text-text-primary",
166
166
  children: e.message
167
167
  }),
168
- /* @__PURE__ */ b("p", {
168
+ /* @__PURE__ */ y("p", {
169
169
  className: "mt-1 text-xs text-text-secondary",
170
170
  children: [
171
- N("adaptercloud.violations.detected", "Detected"),
171
+ M("adaptercloud.violations.detected", "Detected"),
172
172
  " ",
173
173
  c(e.detectedAt),
174
174
  " · ",
175
- /* @__PURE__ */ y("button", {
175
+ /* @__PURE__ */ v("button", {
176
176
  type: "button",
177
- onClick: () => O(`/resources/${e.resourceId}`),
177
+ onClick: () => D(`/resources/${e.resourceId}`),
178
178
  className: "hover:underline",
179
- children: N("adaptercloud.violations.viewResource", "View resource")
179
+ children: M("adaptercloud.violations.viewResource", "View resource")
180
180
  })
181
181
  ]
182
182
  }),
183
- e.waiverReason && /* @__PURE__ */ b("p", {
183
+ e.waiverReason && /* @__PURE__ */ y("p", {
184
184
  className: "mt-2 rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary",
185
185
  children: [
186
- N("adaptercloud.violations.waiver", "Waiver"),
186
+ M("adaptercloud.violations.waiver", "Waiver"),
187
187
  ": ",
188
188
  e.waiverReason
189
189
  ]
190
190
  }),
191
- e.evidence != null && /* @__PURE__ */ b("div", {
191
+ e.evidence != null && /* @__PURE__ */ y("div", {
192
192
  className: "mt-2",
193
- children: [/* @__PURE__ */ b("button", {
193
+ children: [/* @__PURE__ */ y("button", {
194
194
  type: "button",
195
- onClick: () => Y(e.id),
196
- "aria-expanded": W.has(e.id),
195
+ onClick: () => J(e.id),
196
+ "aria-expanded": U.has(e.id),
197
197
  className: "inline-flex items-center gap-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary",
198
- children: [W.has(e.id) ? /* @__PURE__ */ y(f, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ y(m, { className: "h-3.5 w-3.5" }), N("adaptercloud.violations.evidence", "Evidence")]
199
- }), W.has(e.id) && /* @__PURE__ */ y("pre", {
198
+ children: [U.has(e.id) ? /* @__PURE__ */ v(d, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ v(p, { className: "h-3.5 w-3.5" }), M("adaptercloud.violations.evidence", "Evidence")]
199
+ }), U.has(e.id) && /* @__PURE__ */ v("pre", {
200
200
  className: "mt-2 overflow-x-auto rounded-lg border border-border-subtle bg-bg-sunken p-3 font-mono text-xs text-text-secondary",
201
201
  children: JSON.stringify(e.evidence, null, 2)
202
202
  })]
203
203
  })
204
204
  ]
205
- }), t && /* @__PURE__ */ b("div", {
205
+ }), t && /* @__PURE__ */ y("div", {
206
206
  className: "flex shrink-0 flex-wrap gap-2",
207
207
  children: [
208
- e.status === "OPEN" && /* @__PURE__ */ b("button", {
208
+ e.status === "OPEN" && /* @__PURE__ */ y("button", {
209
209
  type: "button",
210
- disabled: q.isPending,
210
+ disabled: K.isPending,
211
211
  onClick: () => $(e.id, "ACKNOWLEDGED"),
212
212
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-accent disabled:opacity-50",
213
- children: [/* @__PURE__ */ y(re, { className: "h-3.5 w-3.5" }), N("adaptercloud.violations.acknowledge", "Acknowledge")]
213
+ children: [/* @__PURE__ */ v(re, { className: "h-3.5 w-3.5" }), M("adaptercloud.violations.acknowledge", "Acknowledge")]
214
214
  }),
215
- /* @__PURE__ */ b("button", {
215
+ /* @__PURE__ */ y("button", {
216
216
  type: "button",
217
- disabled: q.isPending,
217
+ disabled: K.isPending,
218
218
  onClick: () => $(e.id, "RESOLVED"),
219
219
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-50",
220
- children: [/* @__PURE__ */ y(d, { className: "h-3.5 w-3.5" }), N("adaptercloud.violations.resolve", "Resolve")]
220
+ children: [/* @__PURE__ */ v(ie, { className: "h-3.5 w-3.5" }), M("adaptercloud.violations.resolve", "Resolve")]
221
221
  }),
222
- /* @__PURE__ */ b("button", {
222
+ /* @__PURE__ */ y("button", {
223
223
  type: "button",
224
- disabled: q.isPending,
224
+ disabled: K.isPending,
225
225
  onClick: () => {
226
- U(null), V(""), z(e);
226
+ H(null), B(""), R(e);
227
227
  },
228
228
  className: "inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:bg-accent disabled:opacity-50",
229
- children: [/* @__PURE__ */ y(h, { className: "h-3.5 w-3.5" }), N("adaptercloud.violations.waive", "Waive")]
229
+ children: [/* @__PURE__ */ v(m, { className: "h-3.5 w-3.5" }), M("adaptercloud.violations.waive", "Waive")]
230
230
  })
231
231
  ]
232
232
  })]
233
233
  })
234
234
  }, e.id);
235
235
  })
236
- }), (I > 0 || Z) && /* @__PURE__ */ b("div", {
236
+ }), (F > 0 || X) && /* @__PURE__ */ y("div", {
237
237
  className: "mt-6 flex items-center justify-between",
238
238
  children: [
239
- /* @__PURE__ */ b("button", {
239
+ /* @__PURE__ */ y("button", {
240
240
  type: "button",
241
- disabled: I === 0,
242
- onClick: () => L((e) => Math.max(0, e - D)),
241
+ disabled: F === 0,
242
+ onClick: () => I((e) => Math.max(0, e - E)),
243
243
  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",
244
- children: [/* @__PURE__ */ y(p, { className: "h-4 w-4" }), N("adaptercloud.common.prev", "Previous")]
244
+ children: [/* @__PURE__ */ v(f, { className: "h-4 w-4" }), M("adaptercloud.common.prev", "Previous")]
245
245
  }),
246
- /* @__PURE__ */ b("span", {
247
- className: "text-sm text-text-secondary",
246
+ /* @__PURE__ */ y("span", {
247
+ className: "text-sm tabular-nums text-text-secondary",
248
248
  children: [
249
- N("adaptercloud.common.showing", "Showing"),
249
+ M("adaptercloud.common.showing", "Showing"),
250
250
  " ",
251
- I + 1,
251
+ F + 1,
252
252
  "–",
253
- I + X.length
253
+ F + Y.length,
254
+ Z != null && ` ${M("adaptercloud.common.of", "of")} ${Z}`
254
255
  ]
255
256
  }),
256
- /* @__PURE__ */ b("button", {
257
+ /* @__PURE__ */ y("button", {
257
258
  type: "button",
258
- disabled: !Z,
259
- onClick: () => L((e) => e + D),
259
+ disabled: !X,
260
+ onClick: () => I((e) => e + E),
260
261
  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",
261
- children: [N("adaptercloud.common.next", "Next"), /* @__PURE__ */ y(m, { className: "h-4 w-4" })]
262
+ children: [M("adaptercloud.common.next", "Next"), /* @__PURE__ */ v(p, { className: "h-4 w-4" })]
262
263
  })
263
264
  ]
264
265
  })]
265
266
  }),
266
- /* @__PURE__ */ y(S, {
267
- open: !!R,
267
+ /* @__PURE__ */ v(x, {
268
+ open: !!L,
268
269
  onOpenChange: (e) => {
269
- e || z(null);
270
+ e || R(null);
270
271
  },
271
- children: /* @__PURE__ */ b(C, { children: [
272
- /* @__PURE__ */ y(T, { children: /* @__PURE__ */ y(E, { children: N("adaptercloud.violations.waiveTitle", "Waive violation") }) }),
273
- /* @__PURE__ */ b("div", {
272
+ children: /* @__PURE__ */ y(S, { children: [
273
+ /* @__PURE__ */ v(w, { children: /* @__PURE__ */ v(T, { children: M("adaptercloud.violations.waiveTitle", "Waive violation") }) }),
274
+ /* @__PURE__ */ y("div", {
274
275
  className: "px-1 py-2",
275
276
  children: [
276
- /* @__PURE__ */ y("label", {
277
+ /* @__PURE__ */ v("label", {
277
278
  htmlFor: "ada-waiver-reason",
278
279
  className: "mb-1.5 block text-sm text-text-secondary",
279
- children: N("adaptercloud.violations.waivePrompt", "A waiver requires a documented reason and is recorded on the violation.")
280
+ children: M("adaptercloud.violations.waivePrompt", "A waiver requires a documented reason and is recorded on the violation.")
280
281
  }),
281
- /* @__PURE__ */ y("textarea", {
282
+ /* @__PURE__ */ v("textarea", {
282
283
  id: "ada-waiver-reason",
283
- value: B,
284
- onChange: (e) => V(e.target.value),
284
+ value: z,
285
+ onChange: (e) => B(e.target.value),
285
286
  rows: 4,
286
287
  autoFocus: !0,
287
- "aria-label": N("adaptercloud.violations.waiverReason", "Waiver reason"),
288
- placeholder: N("adaptercloud.violations.waiveReasonPlaceholder", "Why is this acceptable?"),
288
+ "aria-label": M("adaptercloud.violations.waiverReason", "Waiver reason"),
289
+ placeholder: M("adaptercloud.violations.waiveReasonPlaceholder", "Why is this acceptable?"),
289
290
  className: "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
290
291
  }),
291
- H && /* @__PURE__ */ y("p", {
292
+ V && /* @__PURE__ */ v("p", {
292
293
  role: "alert",
293
294
  "aria-live": "polite",
294
295
  className: "mt-2 text-sm text-status-error-text",
295
- children: H
296
+ children: V
296
297
  })
297
298
  ]
298
299
  }),
299
- /* @__PURE__ */ b(w, { children: [/* @__PURE__ */ y("button", {
300
+ /* @__PURE__ */ y(C, { children: [/* @__PURE__ */ v("button", {
300
301
  type: "button",
301
- onClick: () => z(null),
302
+ onClick: () => R(null),
302
303
  className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent",
303
- children: N("adaptercloud.common.cancel", "Cancel")
304
- }), /* @__PURE__ */ b("button", {
304
+ children: M("adaptercloud.common.cancel", "Cancel")
305
+ }), /* @__PURE__ */ y("button", {
305
306
  type: "button",
306
- disabled: q.isPending,
307
- onClick: de,
307
+ disabled: K.isPending,
308
+ onClick: fe,
308
309
  className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50",
309
- children: [q.isPending && /* @__PURE__ */ y(g, { className: "h-4 w-4 animate-spin" }), N("adaptercloud.violations.confirmWaive", "Waive")]
310
+ children: [K.isPending && /* @__PURE__ */ v(h, { className: "h-4 w-4 animate-spin" }), M("adaptercloud.violations.confirmWaive", "Waive")]
310
311
  })] })
311
312
  ] })
312
313
  })
@@ -314,6 +315,6 @@ function O() {
314
315
  });
315
316
  }
316
317
  //#endregion
317
- export { O as ViolationsListPage };
318
+ export { D as ViolationsListPage };
318
319
 
319
320
  //# sourceMappingURL=ViolationsListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ViolationsListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/violations/ViolationsListPage.tsx"],"sourcesContent":["/**\n * Violations Queue Page\n * @module adaptercloud/pages/violations\n *\n * The governance work queue (`adaListViolations`). Filters by status (and by\n * policy/resource via URL params). Each open/acknowledged violation can be\n * transitioned via `adaResolveViolation` — acknowledge, resolve, or waive (a\n * waiver requires a reason, enforced both client-side and at the service).\n */\n\nimport { useState, useCallback } from 'react';\nimport { useSearchParams } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ShieldAlert,\n ChevronLeft,\n ChevronRight,\n ChevronDown,\n Check,\n CheckCheck,\n FileText,\n Loader2,\n} from 'lucide-react';\nimport {\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { SeverityBadge } from '../../components/SeverityBadge';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useViolations, useResolveViolation } from '../../hooks/useAdapterCloudApi';\nimport { violationStatusLabels, severityRank } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { AdaPolicyViolation, AdaViolationStatus } from '../../types';\n\n/** Segmented status tabs — All plus each `AdaViolationStatus`, in queue order. */\nconst STATUS_TABS: (AdaViolationStatus | '')[] = ['', 'OPEN', 'ACKNOWLEDGED', 'RESOLVED', 'WAIVED'];\n\nconst PAGE_SIZE = 20;\n\nexport function ViolationsListPage() {\n const { navigateTo } = useAdapterCloud();\n const [searchParams] = useSearchParams();\n const policyId = searchParams.get('policyId') ?? undefined;\n const resourceId = searchParams.get('resourceId') ?? undefined;\n\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<AdaViolationStatus | ''>('OPEN');\n const [offset, setOffset] = useState(0);\n const [waiveTarget, setWaiveTarget] = useState<AdaPolicyViolation | null>(null);\n const [waiverReason, setWaiverReason] = useState('');\n const [actionError, setActionError] = useState<string | null>(null);\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n\n const query = useViolations({\n policyId,\n resourceId,\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n const resolve = useResolveViolation();\n\n // Live per-status counts for the segmented tabs. Each is a cheap `limit: 1`\n // query read for its `pageInfo.totalCount` (the estate-wide total for that\n // filter); \"All\" is the same query without a status. They respect the current\n // policy/resource URL scope so the badges match the visible queue.\n const countBase = { policyId, resourceId, pagination: { limit: 1, offset: 0 } };\n const allCount = useViolations(countBase);\n const openCount = useViolations({ ...countBase, status: 'OPEN' });\n const ackCount = useViolations({ ...countBase, status: 'ACKNOWLEDGED' });\n const resolvedCount = useViolations({ ...countBase, status: 'RESOLVED' });\n const waivedCount = useViolations({ ...countBase, status: 'WAIVED' });\n\n const tabCounts: Record<AdaViolationStatus | '', number | undefined> = {\n '': allCount.data?.pageInfo.totalCount ?? undefined,\n OPEN: openCount.data?.pageInfo.totalCount ?? undefined,\n ACKNOWLEDGED: ackCount.data?.pageInfo.totalCount ?? undefined,\n RESOLVED: resolvedCount.data?.pageInfo.totalCount ?? undefined,\n WAIVED: waivedCount.data?.pageInfo.totalCount ?? undefined,\n };\n\n const toggleEvidence = useCallback((id: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n }, []);\n\n // Severity ordering is applied ONLY within the current server page. The queue\n // pages by offset, so this does not globally reorder across pages — a page-2\n // CRITICAL still sorts below a page-1 LOW. Kept as a within-page readability\n // aid (the service has no severity sort arg to make it global).\n const items = [...(query.data?.items ?? [])].sort(\n (a, b) => severityRank[b.severity] - severityRank[a.severity]\n );\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n const onError = useCallback(\n (err: unknown) =>\n setActionError(err instanceof Error ? err.message : 'Action failed. Please try again.'),\n []\n );\n\n const transition = useCallback(\n (id: string, newStatus: AdaViolationStatus) => {\n setActionError(null);\n resolve.mutate({ id, status: newStatus }, { onError });\n },\n [resolve, onError]\n );\n\n const confirmWaive = useCallback(() => {\n if (!waiveTarget) return;\n if (!waiverReason.trim()) {\n setActionError(t('adaptercloud.violations.reasonRequired', 'A waiver requires a reason.'));\n return;\n }\n setActionError(null);\n resolve.mutate(\n { id: waiveTarget.id, status: 'WAIVED', waiverReason: waiverReason.trim() },\n {\n onSuccess: () => {\n setWaiveTarget(null);\n setWaiverReason('');\n },\n onError,\n }\n );\n }, [waiveTarget, waiverReason, resolve, onError, t]);\n\n return (\n <PageLayout\n title={t('adaptercloud.violations.title', 'Violations')}\n description={t(\n 'adaptercloud.violations.subtitle',\n 'Work the governance queue — acknowledge, resolve, or waive policy breaches.'\n )}\n >\n <div\n className=\"mb-6 flex flex-wrap items-center gap-2\"\n role=\"tablist\"\n aria-label={t('adaptercloud.violations.filterByStatus', 'Filter by status')}\n >\n {STATUS_TABS.map((tab) => {\n const active = status === tab;\n const count = tabCounts[tab];\n const label =\n tab === ''\n ? t('adaptercloud.violations.allStatuses', 'All')\n : violationStatusLabels[tab];\n return (\n <button\n key={tab || 'all'}\n type=\"button\"\n role=\"tab\"\n aria-selected={active}\n onClick={() => {\n setStatus(tab);\n setOffset(0);\n }}\n className={cn(\n 'inline-flex items-center gap-2 rounded-full border px-3.5 py-1.5 text-sm font-medium transition-colors',\n active\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-subtle text-text-secondary hover:bg-accent'\n )}\n >\n {label}\n {count !== undefined && (\n <span\n className={cn(\n 'inline-flex min-w-5 items-center justify-center rounded-full px-1.5 text-xs font-semibold tabular-nums',\n active\n ? 'bg-action-primary-text/20 text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary'\n )}\n >\n {count}\n </span>\n )}\n </button>\n );\n })}\n {(policyId || resourceId) && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-accent-blue-subtle px-2.5 py-1 text-xs text-accent-blue hover:underline\"\n >\n {t('adaptercloud.violations.clearScope', 'Clear scope ×')}\n </button>\n )}\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.violations.emptyTitle', 'No violations')}\n emptyDescription={t(\n 'adaptercloud.violations.emptyDescription',\n 'Nothing matches this filter — your estate is clean here.'\n )}\n emptyIcon={<ShieldAlert className=\"h-10 w-10\" />}\n >\n <div className=\"space-y-3\">\n {items.map((v) => {\n const isOpen = v.status === 'OPEN' || v.status === 'ACKNOWLEDGED';\n return (\n <GlassCard key={v.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 <SeverityBadge severity={v.severity} />\n <StatusBadge status={v.status} />\n {v.policy && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/policies/${v.policyId}`)}\n className=\"text-xs font-medium text-accent-blue hover:underline\"\n >\n {v.policy.name}\n </button>\n )}\n </div>\n <p className=\"text-sm font-medium text-text-primary\">{v.message}</p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.violations.detected', 'Detected')} {formatDate(v.detectedAt)}\n {' · '}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/resources/${v.resourceId}`)}\n className=\"hover:underline\"\n >\n {t('adaptercloud.violations.viewResource', 'View resource')}\n </button>\n </p>\n {v.waiverReason && (\n <p className=\"mt-2 rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary\">\n {t('adaptercloud.violations.waiver', 'Waiver')}: {v.waiverReason}\n </p>\n )}\n {v.evidence != null && (\n <div className=\"mt-2\">\n <button\n type=\"button\"\n onClick={() => toggleEvidence(v.id)}\n aria-expanded={expanded.has(v.id)}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n {expanded.has(v.id) ? (\n <ChevronDown className=\"h-3.5 w-3.5\" />\n ) : (\n <ChevronRight className=\"h-3.5 w-3.5\" />\n )}\n {t('adaptercloud.violations.evidence', 'Evidence')}\n </button>\n {expanded.has(v.id) && (\n <pre className=\"mt-2 overflow-x-auto rounded-lg border border-border-subtle bg-bg-sunken p-3 font-mono text-xs text-text-secondary\">\n {JSON.stringify(v.evidence, null, 2)}\n </pre>\n )}\n </div>\n )}\n </div>\n\n {isOpen && (\n <div className=\"flex shrink-0 flex-wrap gap-2\">\n {v.status === 'OPEN' && (\n <button\n type=\"button\"\n disabled={resolve.isPending}\n onClick={() => transition(v.id, 'ACKNOWLEDGED')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-accent disabled:opacity-50\"\n >\n <Check className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.violations.acknowledge', 'Acknowledge')}\n </button>\n )}\n <button\n type=\"button\"\n disabled={resolve.isPending}\n onClick={() => transition(v.id, 'RESOLVED')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-50\"\n >\n <CheckCheck className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.violations.resolve', 'Resolve')}\n </button>\n <button\n type=\"button\"\n disabled={resolve.isPending}\n onClick={() => {\n setActionError(null);\n setWaiverReason('');\n setWaiveTarget(v);\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:bg-accent disabled:opacity-50\"\n >\n <FileText className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.violations.waive', 'Waive')}\n </button>\n </div>\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 {/* Waive dialog */}\n <ResponsiveDialog\n open={Boolean(waiveTarget)}\n onOpenChange={(open) => {\n if (!open) setWaiveTarget(null);\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {t('adaptercloud.violations.waiveTitle', 'Waive violation')}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <div className=\"px-1 py-2\">\n <label htmlFor=\"ada-waiver-reason\" className=\"mb-1.5 block text-sm text-text-secondary\">\n {t(\n 'adaptercloud.violations.waivePrompt',\n 'A waiver requires a documented reason and is recorded on the violation.'\n )}\n </label>\n <textarea\n id=\"ada-waiver-reason\"\n value={waiverReason}\n onChange={(e) => setWaiverReason(e.target.value)}\n rows={4}\n autoFocus\n aria-label={t('adaptercloud.violations.waiverReason', 'Waiver reason')}\n placeholder={t(\n 'adaptercloud.violations.waiveReasonPlaceholder',\n 'Why is this acceptable?'\n )}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {actionError && (\n <p role=\"alert\" aria-live=\"polite\" className=\"mt-2 text-sm text-status-error-text\">\n {actionError}\n </p>\n )}\n </div>\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setWaiveTarget(null)}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n {t('adaptercloud.common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n disabled={resolve.isPending}\n onClick={confirmWaive}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n {resolve.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {t('adaptercloud.violations.confirmWaive', 'Waive')}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ViolationsListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/violations/ViolationsListPage.tsx"],"sourcesContent":["/**\n * Violations Queue Page\n * @module adaptercloud/pages/violations\n *\n * The governance work queue (`adaListViolations`). Filters by status (and by\n * policy/resource via URL params). Each open/acknowledged violation can be\n * transitioned via `adaResolveViolation` — acknowledge, resolve, or waive (a\n * waiver requires a reason, enforced both client-side and at the service).\n */\n\nimport { useState, useCallback } from 'react';\nimport { useSearchParams } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ShieldAlert,\n ChevronLeft,\n ChevronRight,\n ChevronDown,\n Check,\n CheckCheck,\n FileText,\n Loader2,\n} from 'lucide-react';\nimport {\n GlassCard,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { SeverityBadge } from '../../components/SeverityBadge';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useViolations, useResolveViolation } from '../../hooks/useAdapterCloudApi';\nimport { violationStatusLabels, severityRank } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { AdaPolicyViolation, AdaViolationStatus } from '../../types';\n\n/** Segmented status tabs — All plus each `AdaViolationStatus`, in queue order. */\nconst STATUS_TABS: (AdaViolationStatus | '')[] = ['', 'OPEN', 'ACKNOWLEDGED', 'RESOLVED', 'WAIVED'];\n\nconst PAGE_SIZE = 20;\n\nexport function ViolationsListPage() {\n const { navigateTo } = useAdapterCloud();\n const [searchParams] = useSearchParams();\n const policyId = searchParams.get('policyId') ?? undefined;\n const resourceId = searchParams.get('resourceId') ?? undefined;\n\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<AdaViolationStatus | ''>('OPEN');\n const [offset, setOffset] = useState(0);\n const [waiveTarget, setWaiveTarget] = useState<AdaPolicyViolation | null>(null);\n const [waiverReason, setWaiverReason] = useState('');\n const [actionError, setActionError] = useState<string | null>(null);\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n\n const query = useViolations({\n policyId,\n resourceId,\n status: status || undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n const resolve = useResolveViolation();\n\n // Live per-status counts for the segmented tabs. Each is a cheap `limit: 1`\n // query read for its `pageInfo.totalCount` (the estate-wide total for that\n // filter); \"All\" is the same query without a status. They respect the current\n // policy/resource URL scope so the badges match the visible queue.\n const countBase = { policyId, resourceId, pagination: { limit: 1, offset: 0 } };\n const allCount = useViolations(countBase);\n const openCount = useViolations({ ...countBase, status: 'OPEN' });\n const ackCount = useViolations({ ...countBase, status: 'ACKNOWLEDGED' });\n const resolvedCount = useViolations({ ...countBase, status: 'RESOLVED' });\n const waivedCount = useViolations({ ...countBase, status: 'WAIVED' });\n\n const tabCounts: Record<AdaViolationStatus | '', number | undefined> = {\n '': allCount.data?.pageInfo.totalCount ?? undefined,\n OPEN: openCount.data?.pageInfo.totalCount ?? undefined,\n ACKNOWLEDGED: ackCount.data?.pageInfo.totalCount ?? undefined,\n RESOLVED: resolvedCount.data?.pageInfo.totalCount ?? undefined,\n WAIVED: waivedCount.data?.pageInfo.totalCount ?? undefined,\n };\n\n const toggleEvidence = useCallback((id: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n }, []);\n\n // Severity ordering is applied ONLY within the current server page. The queue\n // pages by offset, so this does not globally reorder across pages — a page-2\n // CRITICAL still sorts below a page-1 LOW. Kept as a within-page readability\n // aid (the service has no severity sort arg to make it global).\n const items = [...(query.data?.items ?? [])].sort(\n (a, b) => severityRank[b.severity] - severityRank[a.severity]\n );\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n // Estate-wide total for the current filter. Nullable on older deployed\n // schemas — the footer then falls back to the bare offset range.\n const totalCount = query.data?.pageInfo.totalCount;\n\n const onError = useCallback(\n (err: unknown) =>\n setActionError(err instanceof Error ? err.message : 'Action failed. Please try again.'),\n []\n );\n\n const transition = useCallback(\n (id: string, newStatus: AdaViolationStatus) => {\n setActionError(null);\n resolve.mutate({ id, status: newStatus }, { onError });\n },\n [resolve, onError]\n );\n\n const confirmWaive = useCallback(() => {\n if (!waiveTarget) return;\n if (!waiverReason.trim()) {\n setActionError(t('adaptercloud.violations.reasonRequired', 'A waiver requires a reason.'));\n return;\n }\n setActionError(null);\n resolve.mutate(\n { id: waiveTarget.id, status: 'WAIVED', waiverReason: waiverReason.trim() },\n {\n onSuccess: () => {\n setWaiveTarget(null);\n setWaiverReason('');\n },\n onError,\n }\n );\n }, [waiveTarget, waiverReason, resolve, onError, t]);\n\n return (\n <PageLayout\n title={t('adaptercloud.violations.title', 'Violations')}\n description={t(\n 'adaptercloud.violations.subtitle',\n 'Work the governance queue — acknowledge, resolve, or waive policy breaches.'\n )}\n >\n <div\n className=\"mb-6 flex flex-wrap items-center gap-2\"\n role=\"tablist\"\n aria-label={t('adaptercloud.violations.filterByStatus', 'Filter by status')}\n >\n {STATUS_TABS.map((tab) => {\n const active = status === tab;\n const count = tabCounts[tab];\n const label =\n tab === ''\n ? t('adaptercloud.violations.allStatuses', 'All')\n : violationStatusLabels[tab];\n return (\n <button\n key={tab || 'all'}\n type=\"button\"\n role=\"tab\"\n aria-selected={active}\n onClick={() => {\n setStatus(tab);\n setOffset(0);\n }}\n className={cn(\n 'inline-flex items-center gap-2 rounded-full border px-3.5 py-1.5 text-sm font-medium transition-colors',\n active\n ? 'border-transparent bg-action-primary-bg text-action-primary-text'\n : 'border-border-subtle text-text-secondary hover:bg-accent'\n )}\n >\n {label}\n {count !== undefined && (\n <span\n className={cn(\n 'inline-flex min-w-5 items-center justify-center rounded-full px-1.5 text-xs font-semibold tabular-nums',\n active\n ? 'bg-action-primary-text/20 text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary'\n )}\n >\n {count}\n </span>\n )}\n </button>\n );\n })}\n {(policyId || resourceId) && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/violations')}\n className=\"inline-flex items-center gap-1.5 rounded-full bg-accent-blue-subtle px-2.5 py-1 text-xs text-accent-blue hover:underline\"\n >\n {t('adaptercloud.violations.clearScope', 'Clear scope ×')}\n </button>\n )}\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.violations.emptyTitle', 'No violations')}\n emptyDescription={t(\n 'adaptercloud.violations.emptyDescription',\n 'Nothing matches this filter — your estate is clean here.'\n )}\n emptyIcon={<ShieldAlert className=\"h-10 w-10\" />}\n >\n <div className=\"space-y-3\">\n {items.map((v) => {\n const isOpen = v.status === 'OPEN' || v.status === 'ACKNOWLEDGED';\n return (\n <GlassCard key={v.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 <SeverityBadge severity={v.severity} />\n <StatusBadge status={v.status} />\n {v.policy && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/policies/${v.policyId}`)}\n className=\"text-xs font-medium text-accent-blue hover:underline\"\n >\n {v.policy.name}\n </button>\n )}\n </div>\n <p className=\"text-sm font-medium text-text-primary\">{v.message}</p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.violations.detected', 'Detected')} {formatDate(v.detectedAt)}\n {' · '}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/resources/${v.resourceId}`)}\n className=\"hover:underline\"\n >\n {t('adaptercloud.violations.viewResource', 'View resource')}\n </button>\n </p>\n {v.waiverReason && (\n <p className=\"mt-2 rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary\">\n {t('adaptercloud.violations.waiver', 'Waiver')}: {v.waiverReason}\n </p>\n )}\n {v.evidence != null && (\n <div className=\"mt-2\">\n <button\n type=\"button\"\n onClick={() => toggleEvidence(v.id)}\n aria-expanded={expanded.has(v.id)}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n {expanded.has(v.id) ? (\n <ChevronDown className=\"h-3.5 w-3.5\" />\n ) : (\n <ChevronRight className=\"h-3.5 w-3.5\" />\n )}\n {t('adaptercloud.violations.evidence', 'Evidence')}\n </button>\n {expanded.has(v.id) && (\n <pre className=\"mt-2 overflow-x-auto rounded-lg border border-border-subtle bg-bg-sunken p-3 font-mono text-xs text-text-secondary\">\n {JSON.stringify(v.evidence, null, 2)}\n </pre>\n )}\n </div>\n )}\n </div>\n\n {isOpen && (\n <div className=\"flex shrink-0 flex-wrap gap-2\">\n {v.status === 'OPEN' && (\n <button\n type=\"button\"\n disabled={resolve.isPending}\n onClick={() => transition(v.id, 'ACKNOWLEDGED')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-accent disabled:opacity-50\"\n >\n <Check className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.violations.acknowledge', 'Acknowledge')}\n </button>\n )}\n <button\n type=\"button\"\n disabled={resolve.isPending}\n onClick={() => transition(v.id, 'RESOLVED')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-50\"\n >\n <CheckCheck className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.violations.resolve', 'Resolve')}\n </button>\n <button\n type=\"button\"\n disabled={resolve.isPending}\n onClick={() => {\n setActionError(null);\n setWaiverReason('');\n setWaiveTarget(v);\n }}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:bg-accent disabled:opacity-50\"\n >\n <FileText className=\"h-3.5 w-3.5\" />\n {t('adaptercloud.violations.waive', 'Waive')}\n </button>\n </div>\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 tabular-nums text-text-secondary\">\n {t('adaptercloud.common.showing', 'Showing')} {offset + 1}–{offset + items.length}\n {totalCount != null && ` ${t('adaptercloud.common.of', 'of')} ${totalCount}`}\n </span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {t('adaptercloud.common.next', 'Next')}\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </DataState>\n\n {/* Waive dialog */}\n <ResponsiveDialog\n open={Boolean(waiveTarget)}\n onOpenChange={(open) => {\n if (!open) setWaiveTarget(null);\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {t('adaptercloud.violations.waiveTitle', 'Waive violation')}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n <div className=\"px-1 py-2\">\n <label htmlFor=\"ada-waiver-reason\" className=\"mb-1.5 block text-sm text-text-secondary\">\n {t(\n 'adaptercloud.violations.waivePrompt',\n 'A waiver requires a documented reason and is recorded on the violation.'\n )}\n </label>\n <textarea\n id=\"ada-waiver-reason\"\n value={waiverReason}\n onChange={(e) => setWaiverReason(e.target.value)}\n rows={4}\n autoFocus\n aria-label={t('adaptercloud.violations.waiverReason', 'Waiver reason')}\n placeholder={t(\n 'adaptercloud.violations.waiveReasonPlaceholder',\n 'Why is this acceptable?'\n )}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {actionError && (\n <p role=\"alert\" aria-live=\"polite\" className=\"mt-2 text-sm text-status-error-text\">\n {actionError}\n </p>\n )}\n </div>\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={() => setWaiveTarget(null)}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n {t('adaptercloud.common.cancel', 'Cancel')}\n </button>\n <button\n type=\"button\"\n disabled={resolve.isPending}\n onClick={confirmWaive}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n {resolve.isPending && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {t('adaptercloud.violations.confirmWaive', 'Waive')}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA2CA,IAAM,KAA2C;CAAC;CAAI;CAAQ;CAAgB;CAAY;AAAQ,GAE5F,IAAY;AAElB,SAAgB,IAAqB;CACnC,IAAM,EAAE,kBAAe,EAAgB,GACjC,CAAC,KAAgB,EAAgB,GACjC,IAAW,EAAa,IAAI,UAAU,KAAK,KAAA,GAC3C,IAAa,EAAa,IAAI,YAAY,KAAK,KAAA,GAE/C,EAAE,GAAG,MAAc,GAAQ,GAC3B,IAAI,GACP,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAU,CAAG;EAC3B,OAAO,MAAU,IAAM,IAAW;CACpC,GACA,CAAC,CAAS,CACZ,GAEM,CAAC,GAAQ,KAAa,EAAkC,MAAM,GAC9D,CAAC,GAAQ,KAAa,EAAS,CAAC,GAChC,CAAC,GAAa,KAAkB,EAAoC,IAAI,GACxE,CAAC,GAAc,KAAmB,EAAS,EAAE,GAC7C,CAAC,GAAa,KAAkB,EAAwB,IAAI,GAC5D,CAAC,GAAU,KAAe,kBAAsB,IAAI,IAAI,CAAC,GAEzD,IAAQ,EAAc;EAC1B;EACA;EACA,QAAQ,KAAU,KAAA;EAClB,YAAY;GAAE,OAAO;GAAW;EAAO;CACzC,CAAC,GACK,IAAU,EAAoB,GAM9B,IAAY;EAAE;EAAU;EAAY,YAAY;GAAE,OAAO;GAAG,QAAQ;EAAE;CAAE,GACxE,KAAW,EAAc,CAAS,GAClC,KAAY,EAAc;EAAE,GAAG;EAAW,QAAQ;CAAO,CAAC,GAC1D,KAAW,EAAc;EAAE,GAAG;EAAW,QAAQ;CAAe,CAAC,GACjE,KAAgB,EAAc;EAAE,GAAG;EAAW,QAAQ;CAAW,CAAC,GAClE,KAAc,EAAc;EAAE,GAAG;EAAW,QAAQ;CAAS,CAAC,GAE9D,KAAiE;EACrE,IAAI,GAAS,MAAM,SAAS,cAAc,KAAA;EAC1C,MAAM,GAAU,MAAM,SAAS,cAAc,KAAA;EAC7C,cAAc,GAAS,MAAM,SAAS,cAAc,KAAA;EACpD,UAAU,GAAc,MAAM,SAAS,cAAc,KAAA;EACrD,QAAQ,GAAY,MAAM,SAAS,cAAc,KAAA;CACnD,GAEM,IAAiB,GAAa,MAAe;EACjD,GAAa,MAAS;GACpB,IAAM,IAAO,IAAI,IAAI,CAAI;GAGzB,OAFI,EAAK,IAAI,CAAE,IAAG,EAAK,OAAO,CAAE,IAC3B,EAAK,IAAI,CAAE,GACT;EACT,CAAC;CACH,GAAG,CAAC,CAAC,GAMC,IAAQ,CAAC,GAAI,EAAM,MAAM,SAAS,CAAC,CAAE,EAAE,MAC1C,GAAG,MAAM,EAAa,EAAE,YAAY,EAAa,EAAE,SACtD,GACM,IAAU,EAAM,MAAM,SAAS,WAAW,IAG1C,IAAa,EAAM,MAAM,SAAS,YAElC,IAAU,GACb,MACC,EAAe,aAAe,QAAQ,EAAI,UAAU,kCAAkC,GACxF,CAAC,CACH,GAEM,IAAa,GAChB,GAAY,MAAkC;EAE7C,AADA,EAAe,IAAI,GACnB,EAAQ,OAAO;GAAE;GAAI,QAAQ;EAAU,GAAG,EAAE,WAAQ,CAAC;CACvD,GACA,CAAC,GAAS,CAAO,CACnB,GAEM,KAAe,QAAkB;EAChC,OACL;OAAI,CAAC,EAAa,KAAK,GAAG;IACxB,EAAe,EAAE,0CAA0C,6BAA6B,CAAC;IACzF;GACF;GAEA,AADA,EAAe,IAAI,GACnB,EAAQ,OACN;IAAE,IAAI,EAAY;IAAI,QAAQ;IAAU,cAAc,EAAa,KAAK;GAAE,GAC1E;IACE,iBAAiB;KAEf,AADA,EAAe,IAAI,GACnB,EAAgB,EAAE;IACpB;IACA;GACF,CACF;EAXA;CAYF,GAAG;EAAC;EAAa;EAAc;EAAS;EAAS;CAAC,CAAC;CAEnD,OACE,kBAAC,GAAD;EACE,OAAO,EAAE,iCAAiC,YAAY;EACtD,aAAa,EACX,oCACA,6EACF;YALF;GAOE,kBAAC,OAAD;IACE,WAAU;IACV,MAAK;IACL,cAAY,EAAE,0CAA0C,kBAAkB;cAH5E,CAKG,GAAY,KAAK,MAAQ;KACxB,IAAM,IAAS,MAAW,GACpB,IAAQ,GAAU,IAClB,IACJ,MAAQ,KACJ,EAAE,uCAAuC,KAAK,IAC9C,EAAsB;KAC5B,OACE,kBAAC,UAAD;MAEE,MAAK;MACL,MAAK;MACL,iBAAe;MACf,eAAe;OAEb,AADA,EAAU,CAAG,GACb,EAAU,CAAC;MACb;MACA,WAAW,EACT,0GACA,IACI,qEACA,0DACN;gBAdF,CAgBG,GACA,MAAU,KAAA,KACT,kBAAC,QAAD;OACE,WAAW,EACT,0GACA,IACI,uDACA,kCACN;iBAEC;MACG,CAAA,CAEF;QA5BD,KAAO,KA4BN;IAEZ,CAAC,IACC,KAAY,MACZ,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,aAAa;KACvC,WAAU;eAET,EAAE,sCAAsC,eAAe;IAClD,CAAA,CAEP;;GAEJ,KACC,kBAAC,KAAD;IACE,MAAK;IACL,aAAU;IACV,WAAU;cAET;GACA,CAAA;GAGL,kBAAC,IAAD;IACE,WAAW,EAAM;IACjB,SAAS,EAAM;IACf,SAAS,EAAM,WAAW;IAC1B,OAAO,EAAM;IACb,eAAe,KAAK,EAAM,QAAQ;IAClC,YAAY,EAAE,sCAAsC,eAAe;IACnE,kBAAkB,EAChB,4CACA,0DACF;IACA,WAAW,kBAAC,GAAD,EAAa,WAAU,YAAa,CAAA;cAXjD,CAaE,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,KAAK,MAAM;MAChB,IAAM,IAAS,EAAE,WAAW,UAAU,EAAE,WAAW;MACnD,OACE,kBAAC,GAAD;OAAsB,WAAU;iBAC9B,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACE,kBAAC,IAAD,EAAe,UAAU,EAAE,SAAW,CAAA;YACtC,kBAAC,GAAD,EAAa,QAAQ,EAAE,OAAS,CAAA;YAC/B,EAAE,UACD,kBAAC,UAAD;aACE,MAAK;aACL,eAAe,EAAW,aAAa,EAAE,UAAU;aACnD,WAAU;uBAET,EAAE,OAAO;YACJ,CAAA;WAEP;;UACL,kBAAC,KAAD;WAAG,WAAU;qBAAyC,EAAE;UAAW,CAAA;UACnE,kBAAC,KAAD;WAAG,WAAU;qBAAb;YACG,EAAE,oCAAoC,UAAU;YAAE;YAAE,EAAW,EAAE,UAAU;YAC3E;YACD,kBAAC,UAAD;aACE,MAAK;aACL,eAAe,EAAW,cAAc,EAAE,YAAY;aACtD,WAAU;uBAET,EAAE,wCAAwC,eAAe;YACpD,CAAA;WACP;;UACF,EAAE,gBACD,kBAAC,KAAD;WAAG,WAAU;qBAAb;YACG,EAAE,kCAAkC,QAAQ;YAAE;YAAG,EAAE;WACnD;;UAEJ,EAAE,YAAY,QACb,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,UAAD;YACE,MAAK;YACL,eAAe,EAAe,EAAE,EAAE;YAClC,iBAAe,EAAS,IAAI,EAAE,EAAE;YAChC,WAAU;sBAJZ,CAMG,EAAS,IAAI,EAAE,EAAE,IAChB,kBAAC,GAAD,EAAa,WAAU,cAAe,CAAA,IAEtC,kBAAC,GAAD,EAAc,WAAU,cAAe,CAAA,GAExC,EAAE,oCAAoC,UAAU,CAC3C;eACP,EAAS,IAAI,EAAE,EAAE,KAChB,kBAAC,OAAD;YAAK,WAAU;sBACZ,KAAK,UAAU,EAAE,UAAU,MAAM,CAAC;WAChC,CAAA,CAEJ;;SAEJ;YAEJ,KACC,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACG,EAAE,WAAW,UACZ,kBAAC,UAAD;WACE,MAAK;WACL,UAAU,EAAQ;WAClB,eAAe,EAAW,EAAE,IAAI,cAAc;WAC9C,WAAU;qBAJZ,CAME,kBAAC,IAAD,EAAO,WAAU,cAAe,CAAA,GAC/B,EAAE,uCAAuC,aAAa,CACjD;;UAEV,kBAAC,UAAD;WACE,MAAK;WACL,UAAU,EAAQ;WAClB,eAAe,EAAW,EAAE,IAAI,UAAU;WAC1C,WAAU;qBAJZ,CAME,kBAAC,IAAD,EAAY,WAAU,cAAe,CAAA,GACpC,EAAE,mCAAmC,SAAS,CACzC;;UACR,kBAAC,UAAD;WACE,MAAK;WACL,UAAU,EAAQ;WAClB,eAAe;YAGb,AAFA,EAAe,IAAI,GACnB,EAAgB,EAAE,GAClB,EAAe,CAAC;WAClB;WACA,WAAU;qBARZ,CAUE,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA,GAClC,EAAE,iCAAiC,OAAO,CACrC;;SACL;UAEJ;;MACI,GA/FK,EAAE,EA+FP;KAEf,CAAC;IACE,CAAA,IAEH,IAAS,KAAK,MACd,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAW;OACrB,eAAe,GAAW,MAAM,KAAK,IAAI,GAAG,IAAI,CAAS,CAAC;OAC1D,WAAU;iBAJZ,CAME,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,GACjC,EAAE,4BAA4B,UAAU,CACnC;;MACR,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG,EAAE,+BAA+B,SAAS;QAAE;QAAE,IAAS;QAAE;QAAE,IAAS,EAAM;QAC1E,KAAc,QAAQ,IAAI,EAAE,0BAA0B,IAAI,EAAE,GAAG;OAC5D;;MACN,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,CAAC;OACX,eAAe,GAAW,MAAM,IAAI,CAAS;OAC7C,WAAU;iBAJZ,CAMG,EAAE,4BAA4B,MAAM,GACrC,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,CAC7B;;KACL;MAEE;;GAGX,kBAAC,GAAD;IACE,MAAM,EAAQ;IACd,eAAe,MAAS;KACtB,AAAK,KAAM,EAAe,IAAI;IAChC;cAEA,kBAAC,GAAD,EAAA,UAAA;KACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UACG,EAAE,sCAAsC,iBAAiB,EACrC,CAAA,EACD,CAAA;KACxB,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,SAAD;QAAO,SAAQ;QAAoB,WAAU;kBAC1C,EACC,uCACA,yEACF;OACK,CAAA;OACP,kBAAC,YAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAgB,EAAE,OAAO,KAAK;QAC/C,MAAM;QACN,WAAA;QACA,cAAY,EAAE,wCAAwC,eAAe;QACrE,aAAa,EACX,kDACA,yBACF;QACA,WAAU;OACX,CAAA;OACA,KACC,kBAAC,KAAD;QAAG,MAAK;QAAQ,aAAU;QAAS,WAAU;kBAC1C;OACA,CAAA;MAEF;;KACL,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAe,IAAI;MAClC,WAAU;gBAET,EAAE,8BAA8B,QAAQ;KACnC,CAAA,GACR,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,EAAQ;MAClB,SAAS;MACT,WAAU;gBAJZ,CAMG,EAAQ,aAAa,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,GAChE,EAAE,wCAAwC,OAAO,CAC5C;OACc,EAAA,CAAA;IACD,EAAA,CAAA;GACT,CAAA;EACR;;AAEhB"}
@@ -16,8 +16,6 @@ export interface PageContext {
16
16
  platform: string;
17
17
  version: string;
18
18
  viewportSize: string;
19
- userAgent: string;
20
- timestamp: string;
21
19
  }
22
20
  export declare function gatherPageContext(): PageContext;
23
21
  export declare function resolveAdapterCloudConversationContext(pathname: string, search: string): ConversationContextShape;
@@ -1 +1 @@
1
- {"version":3,"file":"pageContext.d.ts","sourceRoot":"","sources":["../../src/utils/pageContext.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,UAAU,wBAAwB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAG1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAoB/C;AAMD,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,wBAAwB,CA+D1B"}
1
+ {"version":3,"file":"pageContext.d.ts","sourceRoot":"","sources":["../../src/utils/pageContext.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,UAAU,wBAAwB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IAG1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAkB/C;AAMD,wBAAgB,sCAAsC,CACpD,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,wBAAwB,CA+D1B"}
@@ -8,9 +8,7 @@ function e() {
8
8
  entityId: r.entityId,
9
9
  platform: "adaptercloud",
10
10
  version: "2026.707.1",
11
- viewportSize: `${window.innerWidth}x${window.innerHeight}`,
12
- userAgent: navigator.userAgent,
13
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
11
+ viewportSize: `${window.innerWidth}x${window.innerHeight}`
14
12
  };
15
13
  }
16
14
  function t(e, t) {