@burdenoff/microfe-adaptercloud 2026.825.3 → 2026.827.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adaptercloud/components/ActiveSwitch.d.ts +17 -0
- package/dist/adaptercloud/components/ActiveSwitch.d.ts.map +1 -0
- package/dist/adaptercloud/components/ActiveSwitch.js +28 -0
- package/dist/adaptercloud/components/ActiveSwitch.js.map +1 -0
- package/dist/adaptercloud/components/index.d.ts +1 -0
- package/dist/adaptercloud/components/index.d.ts.map +1 -1
- package/dist/adaptercloud/hooks/useAdapterCloudApi.d.ts +28 -2
- package/dist/adaptercloud/hooks/useAdapterCloudApi.d.ts.map +1 -1
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js +43 -18
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
- package/dist/adaptercloud/pages/OverviewPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/OverviewPage.js +4 -4
- package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +6 -5
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +217 -104
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +6 -5
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
- package/dist/adaptercloud/pages/costs/CostsPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/costs/CostsPage.js +183 -156
- package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
- package/dist/adaptercloud/pages/drift/DriftListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/drift/DriftListPage.js +108 -107
- package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
- package/dist/adaptercloud/pages/policies/PoliciesListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js +141 -131
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +171 -117
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourcesListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js +85 -66
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +148 -121
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
- package/dist/adaptercloud/pages/violations/ViolationsListPage.d.ts.map +1 -1
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js +145 -144
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
- package/dist/utils/pageContext.d.ts +0 -2
- package/dist/utils/pageContext.d.ts.map +1 -1
- package/dist/utils/pageContext.js +25 -10
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +8 -5
|
@@ -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
|
|
13
|
-
import { useSearchParams as
|
|
14
|
-
import { jsx as
|
|
15
|
-
import { GlassCard as
|
|
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
|
|
17
|
+
var ae = [
|
|
18
18
|
"",
|
|
19
19
|
"OPEN",
|
|
20
20
|
"ACKNOWLEDGED",
|
|
21
21
|
"RESOLVED",
|
|
22
22
|
"WAIVED"
|
|
23
|
-
],
|
|
24
|
-
function
|
|
25
|
-
let { navigateTo:
|
|
26
|
-
let n =
|
|
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
|
-
}, [
|
|
29
|
-
policyId:
|
|
30
|
-
resourceId:
|
|
31
|
-
status:
|
|
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:
|
|
34
|
-
offset:
|
|
33
|
+
limit: E,
|
|
34
|
+
offset: F
|
|
35
35
|
}
|
|
36
|
-
}),
|
|
37
|
-
policyId:
|
|
38
|
-
resourceId:
|
|
36
|
+
}), K = o(), q = {
|
|
37
|
+
policyId: k,
|
|
38
|
+
resourceId: A,
|
|
39
39
|
pagination: {
|
|
40
40
|
limit: 1,
|
|
41
41
|
offset: 0
|
|
42
42
|
}
|
|
43
|
-
},
|
|
44
|
-
...
|
|
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
|
-
...
|
|
51
|
-
status: "
|
|
47
|
+
...q,
|
|
48
|
+
status: "ACKNOWLEDGED"
|
|
52
49
|
}), le = s({
|
|
53
|
-
...
|
|
50
|
+
...q,
|
|
51
|
+
status: "RESOLVED"
|
|
52
|
+
}), ue = s({
|
|
53
|
+
...q,
|
|
54
54
|
status: "WAIVED"
|
|
55
|
-
}),
|
|
56
|
-
"":
|
|
57
|
-
OPEN:
|
|
58
|
-
ACKNOWLEDGED:
|
|
59
|
-
RESOLVED:
|
|
60
|
-
WAIVED:
|
|
61
|
-
},
|
|
62
|
-
|
|
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
|
-
}, []),
|
|
67
|
-
|
|
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
|
-
}, [
|
|
72
|
-
if (
|
|
73
|
-
if (!
|
|
74
|
-
|
|
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
|
-
|
|
78
|
-
id:
|
|
77
|
+
H(null), K.mutate({
|
|
78
|
+
id: L.id,
|
|
79
79
|
status: "WAIVED",
|
|
80
|
-
waiverReason:
|
|
80
|
+
waiverReason: z.trim()
|
|
81
81
|
}, {
|
|
82
82
|
onSuccess: () => {
|
|
83
|
-
|
|
83
|
+
R(null), B("");
|
|
84
84
|
},
|
|
85
85
|
onError: Q
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
}, [
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
L,
|
|
90
|
+
z,
|
|
91
|
+
K,
|
|
92
92
|
Q,
|
|
93
|
-
|
|
93
|
+
M
|
|
94
94
|
]);
|
|
95
|
-
return /* @__PURE__ */
|
|
96
|
-
title:
|
|
97
|
-
description:
|
|
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__ */
|
|
99
|
+
/* @__PURE__ */ y("div", {
|
|
100
100
|
className: "mb-6 flex flex-wrap items-center gap-2",
|
|
101
101
|
role: "tablist",
|
|
102
|
-
"aria-label":
|
|
103
|
-
children: [
|
|
104
|
-
let n =
|
|
105
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
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__ */
|
|
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
|
-
}), (
|
|
118
|
+
}), (k || A) && /* @__PURE__ */ v("button", {
|
|
119
119
|
type: "button",
|
|
120
|
-
onClick: () =>
|
|
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:
|
|
122
|
+
children: M("adaptercloud.violations.clearScope", "Clear scope ×")
|
|
123
123
|
})]
|
|
124
124
|
}),
|
|
125
|
-
|
|
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:
|
|
129
|
+
children: V
|
|
130
130
|
}),
|
|
131
|
-
/* @__PURE__ */
|
|
132
|
-
isLoading:
|
|
133
|
-
isError:
|
|
134
|
-
isEmpty:
|
|
135
|
-
error:
|
|
136
|
-
onRetry: () => void
|
|
137
|
-
emptyTitle:
|
|
138
|
-
emptyDescription:
|
|
139
|
-
emptyIcon: /* @__PURE__ */
|
|
140
|
-
children: [/* @__PURE__ */
|
|
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:
|
|
142
|
+
children: Y.map((e) => {
|
|
143
143
|
let t = e.status === "OPEN" || e.status === "ACKNOWLEDGED";
|
|
144
|
-
return /* @__PURE__ */
|
|
144
|
+
return /* @__PURE__ */ v(b, {
|
|
145
145
|
className: "p-5",
|
|
146
|
-
children: /* @__PURE__ */
|
|
146
|
+
children: /* @__PURE__ */ y("div", {
|
|
147
147
|
className: "flex flex-wrap items-start justify-between gap-3",
|
|
148
|
-
children: [/* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ y("div", {
|
|
149
149
|
className: "min-w-0 flex-1",
|
|
150
150
|
children: [
|
|
151
|
-
/* @__PURE__ */
|
|
151
|
+
/* @__PURE__ */ y("div", {
|
|
152
152
|
className: "mb-1.5 flex flex-wrap items-center gap-2",
|
|
153
153
|
children: [
|
|
154
|
-
/* @__PURE__ */
|
|
155
|
-
/* @__PURE__ */
|
|
156
|
-
e.policy && /* @__PURE__ */
|
|
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: () =>
|
|
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__ */
|
|
164
|
+
/* @__PURE__ */ v("p", {
|
|
165
165
|
className: "text-sm font-medium text-text-primary",
|
|
166
166
|
children: e.message
|
|
167
167
|
}),
|
|
168
|
-
/* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ y("p", {
|
|
169
169
|
className: "mt-1 text-xs text-text-secondary",
|
|
170
170
|
children: [
|
|
171
|
-
|
|
171
|
+
M("adaptercloud.violations.detected", "Detected"),
|
|
172
172
|
" ",
|
|
173
173
|
c(e.detectedAt),
|
|
174
174
|
" · ",
|
|
175
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ v("button", {
|
|
176
176
|
type: "button",
|
|
177
|
-
onClick: () =>
|
|
177
|
+
onClick: () => D(`/resources/${e.resourceId}`),
|
|
178
178
|
className: "hover:underline",
|
|
179
|
-
children:
|
|
179
|
+
children: M("adaptercloud.violations.viewResource", "View resource")
|
|
180
180
|
})
|
|
181
181
|
]
|
|
182
182
|
}),
|
|
183
|
-
e.waiverReason && /* @__PURE__ */
|
|
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
|
-
|
|
186
|
+
M("adaptercloud.violations.waiver", "Waiver"),
|
|
187
187
|
": ",
|
|
188
188
|
e.waiverReason
|
|
189
189
|
]
|
|
190
190
|
}),
|
|
191
|
-
e.evidence != null && /* @__PURE__ */
|
|
191
|
+
e.evidence != null && /* @__PURE__ */ y("div", {
|
|
192
192
|
className: "mt-2",
|
|
193
|
-
children: [/* @__PURE__ */
|
|
193
|
+
children: [/* @__PURE__ */ y("button", {
|
|
194
194
|
type: "button",
|
|
195
|
-
onClick: () =>
|
|
196
|
-
"aria-expanded":
|
|
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: [
|
|
199
|
-
}),
|
|
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__ */
|
|
205
|
+
}), t && /* @__PURE__ */ y("div", {
|
|
206
206
|
className: "flex shrink-0 flex-wrap gap-2",
|
|
207
207
|
children: [
|
|
208
|
-
e.status === "OPEN" && /* @__PURE__ */
|
|
208
|
+
e.status === "OPEN" && /* @__PURE__ */ y("button", {
|
|
209
209
|
type: "button",
|
|
210
|
-
disabled:
|
|
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__ */
|
|
213
|
+
children: [/* @__PURE__ */ v(re, { className: "h-3.5 w-3.5" }), M("adaptercloud.violations.acknowledge", "Acknowledge")]
|
|
214
214
|
}),
|
|
215
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ y("button", {
|
|
216
216
|
type: "button",
|
|
217
|
-
disabled:
|
|
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__ */
|
|
220
|
+
children: [/* @__PURE__ */ v(ie, { className: "h-3.5 w-3.5" }), M("adaptercloud.violations.resolve", "Resolve")]
|
|
221
221
|
}),
|
|
222
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ y("button", {
|
|
223
223
|
type: "button",
|
|
224
|
-
disabled:
|
|
224
|
+
disabled: K.isPending,
|
|
225
225
|
onClick: () => {
|
|
226
|
-
|
|
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__ */
|
|
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
|
-
}), (
|
|
236
|
+
}), (F > 0 || X) && /* @__PURE__ */ y("div", {
|
|
237
237
|
className: "mt-6 flex items-center justify-between",
|
|
238
238
|
children: [
|
|
239
|
-
/* @__PURE__ */
|
|
239
|
+
/* @__PURE__ */ y("button", {
|
|
240
240
|
type: "button",
|
|
241
|
-
disabled:
|
|
242
|
-
onClick: () =>
|
|
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__ */
|
|
244
|
+
children: [/* @__PURE__ */ v(f, { className: "h-4 w-4" }), M("adaptercloud.common.prev", "Previous")]
|
|
245
245
|
}),
|
|
246
|
-
/* @__PURE__ */
|
|
247
|
-
className: "text-sm text-text-secondary",
|
|
246
|
+
/* @__PURE__ */ y("span", {
|
|
247
|
+
className: "text-sm tabular-nums text-text-secondary",
|
|
248
248
|
children: [
|
|
249
|
-
|
|
249
|
+
M("adaptercloud.common.showing", "Showing"),
|
|
250
250
|
" ",
|
|
251
|
-
|
|
251
|
+
F + 1,
|
|
252
252
|
"–",
|
|
253
|
-
|
|
253
|
+
F + Y.length,
|
|
254
|
+
Z != null && ` ${M("adaptercloud.common.of", "of")} ${Z}`
|
|
254
255
|
]
|
|
255
256
|
}),
|
|
256
|
-
/* @__PURE__ */
|
|
257
|
+
/* @__PURE__ */ y("button", {
|
|
257
258
|
type: "button",
|
|
258
|
-
disabled: !
|
|
259
|
-
onClick: () =>
|
|
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: [
|
|
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__ */
|
|
267
|
-
open: !!
|
|
267
|
+
/* @__PURE__ */ v(x, {
|
|
268
|
+
open: !!L,
|
|
268
269
|
onOpenChange: (e) => {
|
|
269
|
-
e ||
|
|
270
|
+
e || R(null);
|
|
270
271
|
},
|
|
271
|
-
children: /* @__PURE__ */
|
|
272
|
-
/* @__PURE__ */
|
|
273
|
-
/* @__PURE__ */
|
|
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__ */
|
|
277
|
+
/* @__PURE__ */ v("label", {
|
|
277
278
|
htmlFor: "ada-waiver-reason",
|
|
278
279
|
className: "mb-1.5 block text-sm text-text-secondary",
|
|
279
|
-
children:
|
|
280
|
+
children: M("adaptercloud.violations.waivePrompt", "A waiver requires a documented reason and is recorded on the violation.")
|
|
280
281
|
}),
|
|
281
|
-
/* @__PURE__ */
|
|
282
|
+
/* @__PURE__ */ v("textarea", {
|
|
282
283
|
id: "ada-waiver-reason",
|
|
283
|
-
value:
|
|
284
|
-
onChange: (e) =>
|
|
284
|
+
value: z,
|
|
285
|
+
onChange: (e) => B(e.target.value),
|
|
285
286
|
rows: 4,
|
|
286
287
|
autoFocus: !0,
|
|
287
|
-
"aria-label":
|
|
288
|
-
placeholder:
|
|
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
|
-
|
|
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:
|
|
296
|
+
children: V
|
|
296
297
|
})
|
|
297
298
|
]
|
|
298
299
|
}),
|
|
299
|
-
/* @__PURE__ */
|
|
300
|
+
/* @__PURE__ */ y(C, { children: [/* @__PURE__ */ v("button", {
|
|
300
301
|
type: "button",
|
|
301
|
-
onClick: () =>
|
|
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:
|
|
304
|
-
}), /* @__PURE__ */
|
|
304
|
+
children: M("adaptercloud.common.cancel", "Cancel")
|
|
305
|
+
}), /* @__PURE__ */ y("button", {
|
|
305
306
|
type: "button",
|
|
306
|
-
disabled:
|
|
307
|
-
onClick:
|
|
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: [
|
|
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 {
|
|
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":";;;;;;;;;;;;;;;;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,IAE1C,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,GAAD,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;OACvE;;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"}
|
|
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":"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"}
|
|
@@ -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;
|
|
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"}
|