@burdenoff/microfe-adaptercloud 2026.706.2 → 2026.706.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adaptercloud/AdapterCloudRoot.js +52 -44
- package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
- package/dist/adaptercloud/AdapterCloudRoutes.js +18 -14
- package/dist/adaptercloud/AdapterCloudRoutes.js.map +1 -1
- package/dist/adaptercloud/components/ConfirmDialog.js.map +1 -1
- package/dist/adaptercloud/components/viz/BarChart.js +32 -0
- package/dist/adaptercloud/components/viz/BarChart.js.map +1 -0
- package/dist/adaptercloud/components/viz/Sparkline.js +50 -0
- package/dist/adaptercloud/components/viz/Sparkline.js.map +1 -0
- package/dist/adaptercloud/components/viz/StackedBar.js +1 -0
- package/dist/adaptercloud/components/viz/TopologyGraph.js +96 -0
- package/dist/adaptercloud/components/viz/TopologyGraph.js.map +1 -0
- package/dist/adaptercloud/components/viz/index.js +4 -0
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js +22 -4
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
- package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js +176 -123
- package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdapterFormPage.js +261 -179
- package/dist/adaptercloud/pages/adapters/AdapterFormPage.js.map +1 -1
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +158 -94
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
- package/dist/adaptercloud/pages/costs/CostsPage.js +160 -111
- package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
- package/dist/adaptercloud/pages/drift/DriftListPage.js +163 -84
- package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js +177 -96
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +145 -131
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -1
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js +10 -1
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +226 -165
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
- package/dist/adaptercloud/pages/topology/TopologyPage.js +397 -0
- package/dist/adaptercloud/pages/topology/TopologyPage.js.map +1 -0
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js +184 -131
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,259 +1,312 @@
|
|
|
1
1
|
import { useAdapterCloud as e } from "../../context/AdapterCloudContext.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
2
|
+
import { cn as t } from "../../utils/cn.js";
|
|
3
|
+
import { PageLayout as n } from "../../components/PageLayout.js";
|
|
4
|
+
import { severityRank as r, violationStatusLabels as i } from "../../utils/domain.js";
|
|
5
|
+
import { StatusBadge as a } from "../../components/StatusBadge.js";
|
|
6
|
+
import { SeverityBadge as ee } from "../../components/SeverityBadge.js";
|
|
7
|
+
import { DataState as te } from "../../components/DataState.js";
|
|
8
|
+
import { useResolveViolation as o, useViolations as s } from "../../hooks/useAdapterCloudApi.js";
|
|
9
|
+
import { formatDate as c } from "../../utils/formatters.js";
|
|
10
|
+
import { useCallback as l, useState as u } from "react";
|
|
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-dom";
|
|
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";
|
|
15
16
|
//#region src/adaptercloud/pages/violations/ViolationsListPage.tsx
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
var ie = [
|
|
18
|
+
"",
|
|
19
|
+
"OPEN",
|
|
20
|
+
"ACKNOWLEDGED",
|
|
21
|
+
"RESOLVED",
|
|
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);
|
|
20
27
|
return n === e ? t : n;
|
|
21
|
-
}, [
|
|
22
|
-
policyId:
|
|
23
|
-
resourceId:
|
|
24
|
-
status:
|
|
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,
|
|
25
32
|
pagination: {
|
|
26
|
-
limit:
|
|
27
|
-
offset:
|
|
33
|
+
limit: D,
|
|
34
|
+
offset: I
|
|
28
35
|
}
|
|
29
|
-
}), q =
|
|
30
|
-
|
|
36
|
+
}), q = o(), J = {
|
|
37
|
+
policyId: A,
|
|
38
|
+
resourceId: j,
|
|
39
|
+
pagination: {
|
|
40
|
+
limit: 1,
|
|
41
|
+
offset: 0
|
|
42
|
+
}
|
|
43
|
+
}, ae = s(J), oe = s({
|
|
44
|
+
...J,
|
|
45
|
+
status: "OPEN"
|
|
46
|
+
}), se = s({
|
|
47
|
+
...J,
|
|
48
|
+
status: "ACKNOWLEDGED"
|
|
49
|
+
}), ce = s({
|
|
50
|
+
...J,
|
|
51
|
+
status: "RESOLVED"
|
|
52
|
+
}), le = s({
|
|
53
|
+
...J,
|
|
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) => {
|
|
63
|
+
let n = new Set(t);
|
|
64
|
+
return n.has(e) ? n.delete(e) : n.add(e), n;
|
|
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({
|
|
31
68
|
id: e,
|
|
32
69
|
status: t
|
|
33
|
-
}, { onError:
|
|
34
|
-
}, [q,
|
|
35
|
-
if (
|
|
36
|
-
if (!
|
|
37
|
-
|
|
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."));
|
|
38
75
|
return;
|
|
39
76
|
}
|
|
40
|
-
|
|
41
|
-
id:
|
|
77
|
+
U(null), q.mutate({
|
|
78
|
+
id: R.id,
|
|
42
79
|
status: "WAIVED",
|
|
43
|
-
waiverReason:
|
|
80
|
+
waiverReason: B.trim()
|
|
44
81
|
}, {
|
|
45
82
|
onSuccess: () => {
|
|
46
|
-
|
|
83
|
+
z(null), V("");
|
|
47
84
|
},
|
|
48
|
-
onError:
|
|
85
|
+
onError: Q
|
|
49
86
|
});
|
|
50
87
|
}
|
|
51
88
|
}, [
|
|
89
|
+
R,
|
|
52
90
|
B,
|
|
53
|
-
H,
|
|
54
91
|
q,
|
|
55
|
-
|
|
56
|
-
|
|
92
|
+
Q,
|
|
93
|
+
N
|
|
57
94
|
]);
|
|
58
|
-
return /* @__PURE__ */
|
|
59
|
-
title:
|
|
60
|
-
description:
|
|
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."),
|
|
61
98
|
children: [
|
|
62
|
-
/* @__PURE__ */
|
|
63
|
-
className: "mb-6 flex flex-wrap items-center gap-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
children: r
|
|
77
|
-
|
|
78
|
-
|
|
99
|
+
/* @__PURE__ */ b("div", {
|
|
100
|
+
className: "mb-6 flex flex-wrap items-center gap-2",
|
|
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", {
|
|
106
|
+
type: "button",
|
|
107
|
+
role: "tab",
|
|
108
|
+
"aria-selected": n,
|
|
109
|
+
onClick: () => {
|
|
110
|
+
F(e), L(0);
|
|
111
|
+
},
|
|
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", {
|
|
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
|
+
children: r
|
|
116
|
+
})]
|
|
117
|
+
}, e || "all");
|
|
118
|
+
}), (A || j) && /* @__PURE__ */ y("button", {
|
|
79
119
|
type: "button",
|
|
80
|
-
onClick: () =>
|
|
120
|
+
onClick: () => O("/violations"),
|
|
81
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",
|
|
82
|
-
children:
|
|
122
|
+
children: N("adaptercloud.violations.clearScope", "Clear scope ×")
|
|
83
123
|
})]
|
|
84
124
|
}),
|
|
85
|
-
|
|
125
|
+
H && /* @__PURE__ */ y("p", {
|
|
86
126
|
role: "alert",
|
|
87
127
|
"aria-live": "polite",
|
|
88
128
|
className: "mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
89
|
-
children:
|
|
129
|
+
children: H
|
|
90
130
|
}),
|
|
91
|
-
/* @__PURE__ */
|
|
131
|
+
/* @__PURE__ */ b(te, {
|
|
92
132
|
isLoading: K.isLoading,
|
|
93
133
|
isError: K.isError,
|
|
94
|
-
isEmpty:
|
|
134
|
+
isEmpty: X.length === 0,
|
|
95
135
|
error: K.error,
|
|
96
136
|
onRetry: () => void K.refetch(),
|
|
97
|
-
emptyTitle:
|
|
98
|
-
emptyDescription:
|
|
99
|
-
emptyIcon: /* @__PURE__ */
|
|
100
|
-
children: [/* @__PURE__ */
|
|
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", {
|
|
101
141
|
className: "space-y-3",
|
|
102
|
-
children:
|
|
142
|
+
children: X.map((e) => {
|
|
103
143
|
let t = e.status === "OPEN" || e.status === "ACKNOWLEDGED";
|
|
104
|
-
return /* @__PURE__ */ x
|
|
144
|
+
return /* @__PURE__ */ y(x, {
|
|
105
145
|
className: "p-5",
|
|
106
|
-
children: /* @__PURE__ */
|
|
146
|
+
children: /* @__PURE__ */ b("div", {
|
|
107
147
|
className: "flex flex-wrap items-start justify-between gap-3",
|
|
108
|
-
children: [/* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ b("div", {
|
|
109
149
|
className: "min-w-0 flex-1",
|
|
110
150
|
children: [
|
|
111
|
-
/* @__PURE__ */
|
|
151
|
+
/* @__PURE__ */ b("div", {
|
|
112
152
|
className: "mb-1.5 flex flex-wrap items-center gap-2",
|
|
113
153
|
children: [
|
|
114
|
-
/* @__PURE__ */
|
|
115
|
-
/* @__PURE__ */
|
|
116
|
-
e.policy && /* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ y(ee, { severity: e.severity }),
|
|
155
|
+
/* @__PURE__ */ y(a, { status: e.status }),
|
|
156
|
+
e.policy && /* @__PURE__ */ y("button", {
|
|
117
157
|
type: "button",
|
|
118
|
-
onClick: () =>
|
|
158
|
+
onClick: () => O(`/policies/${e.policyId}`),
|
|
119
159
|
className: "text-xs font-medium text-accent-blue hover:underline",
|
|
120
160
|
children: e.policy.name
|
|
121
161
|
})
|
|
122
162
|
]
|
|
123
163
|
}),
|
|
124
|
-
/* @__PURE__ */
|
|
164
|
+
/* @__PURE__ */ y("p", {
|
|
125
165
|
className: "text-sm font-medium text-text-primary",
|
|
126
166
|
children: e.message
|
|
127
167
|
}),
|
|
128
|
-
/* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ b("p", {
|
|
129
169
|
className: "mt-1 text-xs text-text-secondary",
|
|
130
170
|
children: [
|
|
131
|
-
|
|
171
|
+
N("adaptercloud.violations.detected", "Detected"),
|
|
132
172
|
" ",
|
|
133
|
-
|
|
173
|
+
c(e.detectedAt),
|
|
134
174
|
" · ",
|
|
135
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ y("button", {
|
|
136
176
|
type: "button",
|
|
137
|
-
onClick: () =>
|
|
177
|
+
onClick: () => O(`/resources/${e.resourceId}`),
|
|
138
178
|
className: "hover:underline",
|
|
139
|
-
children:
|
|
179
|
+
children: N("adaptercloud.violations.viewResource", "View resource")
|
|
140
180
|
})
|
|
141
181
|
]
|
|
142
182
|
}),
|
|
143
|
-
e.waiverReason && /* @__PURE__ */
|
|
183
|
+
e.waiverReason && /* @__PURE__ */ b("p", {
|
|
144
184
|
className: "mt-2 rounded-lg bg-bg-sunken px-3 py-1.5 text-xs text-text-secondary",
|
|
145
185
|
children: [
|
|
146
|
-
|
|
186
|
+
N("adaptercloud.violations.waiver", "Waiver"),
|
|
147
187
|
": ",
|
|
148
188
|
e.waiverReason
|
|
149
189
|
]
|
|
190
|
+
}),
|
|
191
|
+
e.evidence != null && /* @__PURE__ */ b("div", {
|
|
192
|
+
className: "mt-2",
|
|
193
|
+
children: [/* @__PURE__ */ b("button", {
|
|
194
|
+
type: "button",
|
|
195
|
+
onClick: () => Y(e.id),
|
|
196
|
+
"aria-expanded": W.has(e.id),
|
|
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", {
|
|
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
|
+
children: JSON.stringify(e.evidence, null, 2)
|
|
202
|
+
})]
|
|
150
203
|
})
|
|
151
204
|
]
|
|
152
|
-
}), t && /* @__PURE__ */
|
|
205
|
+
}), t && /* @__PURE__ */ b("div", {
|
|
153
206
|
className: "flex shrink-0 flex-wrap gap-2",
|
|
154
207
|
children: [
|
|
155
|
-
e.status === "OPEN" && /* @__PURE__ */
|
|
208
|
+
e.status === "OPEN" && /* @__PURE__ */ b("button", {
|
|
156
209
|
type: "button",
|
|
157
210
|
disabled: q.isPending,
|
|
158
|
-
onClick: () =>
|
|
211
|
+
onClick: () => $(e.id, "ACKNOWLEDGED"),
|
|
159
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",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
213
|
+
children: [/* @__PURE__ */ y(re, { className: "h-3.5 w-3.5" }), N("adaptercloud.violations.acknowledge", "Acknowledge")]
|
|
161
214
|
}),
|
|
162
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ b("button", {
|
|
163
216
|
type: "button",
|
|
164
217
|
disabled: q.isPending,
|
|
165
|
-
onClick: () =>
|
|
218
|
+
onClick: () => $(e.id, "RESOLVED"),
|
|
166
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",
|
|
167
|
-
children: [/* @__PURE__ */
|
|
220
|
+
children: [/* @__PURE__ */ y(d, { className: "h-3.5 w-3.5" }), N("adaptercloud.violations.resolve", "Resolve")]
|
|
168
221
|
}),
|
|
169
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ b("button", {
|
|
170
223
|
type: "button",
|
|
171
224
|
disabled: q.isPending,
|
|
172
225
|
onClick: () => {
|
|
173
|
-
|
|
226
|
+
U(null), V(""), z(e);
|
|
174
227
|
},
|
|
175
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",
|
|
176
|
-
children: [/* @__PURE__ */
|
|
229
|
+
children: [/* @__PURE__ */ y(h, { className: "h-3.5 w-3.5" }), N("adaptercloud.violations.waive", "Waive")]
|
|
177
230
|
})
|
|
178
231
|
]
|
|
179
232
|
})]
|
|
180
233
|
})
|
|
181
234
|
}, e.id);
|
|
182
235
|
})
|
|
183
|
-
}), (
|
|
236
|
+
}), (I > 0 || Z) && /* @__PURE__ */ b("div", {
|
|
184
237
|
className: "mt-6 flex items-center justify-between",
|
|
185
238
|
children: [
|
|
186
|
-
/* @__PURE__ */
|
|
239
|
+
/* @__PURE__ */ b("button", {
|
|
187
240
|
type: "button",
|
|
188
|
-
disabled:
|
|
189
|
-
onClick: () =>
|
|
241
|
+
disabled: I === 0,
|
|
242
|
+
onClick: () => L((e) => Math.max(0, e - D)),
|
|
190
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",
|
|
191
|
-
children: [/* @__PURE__ */
|
|
244
|
+
children: [/* @__PURE__ */ y(p, { className: "h-4 w-4" }), N("adaptercloud.common.prev", "Previous")]
|
|
192
245
|
}),
|
|
193
|
-
/* @__PURE__ */
|
|
246
|
+
/* @__PURE__ */ b("span", {
|
|
194
247
|
className: "text-sm text-text-secondary",
|
|
195
248
|
children: [
|
|
196
|
-
|
|
249
|
+
N("adaptercloud.common.showing", "Showing"),
|
|
197
250
|
" ",
|
|
198
|
-
|
|
251
|
+
I + 1,
|
|
199
252
|
"–",
|
|
200
|
-
|
|
253
|
+
I + X.length
|
|
201
254
|
]
|
|
202
255
|
}),
|
|
203
|
-
/* @__PURE__ */
|
|
256
|
+
/* @__PURE__ */ b("button", {
|
|
204
257
|
type: "button",
|
|
205
|
-
disabled: !
|
|
206
|
-
onClick: () =>
|
|
258
|
+
disabled: !Z,
|
|
259
|
+
onClick: () => L((e) => e + D),
|
|
207
260
|
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",
|
|
208
|
-
children: [
|
|
261
|
+
children: [N("adaptercloud.common.next", "Next"), /* @__PURE__ */ y(m, { className: "h-4 w-4" })]
|
|
209
262
|
})
|
|
210
263
|
]
|
|
211
264
|
})]
|
|
212
265
|
}),
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
-
open: !!
|
|
266
|
+
/* @__PURE__ */ y(S, {
|
|
267
|
+
open: !!R,
|
|
215
268
|
onOpenChange: (e) => {
|
|
216
|
-
e ||
|
|
269
|
+
e || z(null);
|
|
217
270
|
},
|
|
218
|
-
children: /* @__PURE__ */
|
|
219
|
-
/* @__PURE__ */
|
|
220
|
-
/* @__PURE__ */
|
|
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", {
|
|
221
274
|
className: "px-1 py-2",
|
|
222
275
|
children: [
|
|
223
|
-
/* @__PURE__ */
|
|
276
|
+
/* @__PURE__ */ y("label", {
|
|
224
277
|
htmlFor: "ada-waiver-reason",
|
|
225
278
|
className: "mb-1.5 block text-sm text-text-secondary",
|
|
226
|
-
children:
|
|
279
|
+
children: N("adaptercloud.violations.waivePrompt", "A waiver requires a documented reason and is recorded on the violation.")
|
|
227
280
|
}),
|
|
228
|
-
/* @__PURE__ */
|
|
281
|
+
/* @__PURE__ */ y("textarea", {
|
|
229
282
|
id: "ada-waiver-reason",
|
|
230
|
-
value:
|
|
231
|
-
onChange: (e) =>
|
|
283
|
+
value: B,
|
|
284
|
+
onChange: (e) => V(e.target.value),
|
|
232
285
|
rows: 4,
|
|
233
286
|
autoFocus: !0,
|
|
234
|
-
"aria-label":
|
|
235
|
-
placeholder:
|
|
287
|
+
"aria-label": N("adaptercloud.violations.waiverReason", "Waiver reason"),
|
|
288
|
+
placeholder: N("adaptercloud.violations.waiveReasonPlaceholder", "Why is this acceptable?"),
|
|
236
289
|
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)]"
|
|
237
290
|
}),
|
|
238
|
-
|
|
291
|
+
H && /* @__PURE__ */ y("p", {
|
|
239
292
|
role: "alert",
|
|
240
293
|
"aria-live": "polite",
|
|
241
294
|
className: "mt-2 text-sm text-status-error-text",
|
|
242
|
-
children:
|
|
295
|
+
children: H
|
|
243
296
|
})
|
|
244
297
|
]
|
|
245
298
|
}),
|
|
246
|
-
/* @__PURE__ */
|
|
299
|
+
/* @__PURE__ */ b(w, { children: [/* @__PURE__ */ y("button", {
|
|
247
300
|
type: "button",
|
|
248
|
-
onClick: () =>
|
|
301
|
+
onClick: () => z(null),
|
|
249
302
|
className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent",
|
|
250
|
-
children:
|
|
251
|
-
}), /* @__PURE__ */
|
|
303
|
+
children: N("adaptercloud.common.cancel", "Cancel")
|
|
304
|
+
}), /* @__PURE__ */ b("button", {
|
|
252
305
|
type: "button",
|
|
253
306
|
disabled: q.isPending,
|
|
254
|
-
onClick:
|
|
307
|
+
onClick: de,
|
|
255
308
|
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",
|
|
256
|
-
children: [q.isPending && /* @__PURE__ */
|
|
309
|
+
children: [q.isPending && /* @__PURE__ */ y(g, { className: "h-4 w-4 animate-spin" }), N("adaptercloud.violations.confirmWaive", "Waive")]
|
|
257
310
|
})] })
|
|
258
311
|
] })
|
|
259
312
|
})
|
|
@@ -261,6 +314,6 @@ function A() {
|
|
|
261
314
|
});
|
|
262
315
|
}
|
|
263
316
|
//#endregion
|
|
264
|
-
export {
|
|
317
|
+
export { O as ViolationsListPage };
|
|
265
318
|
|
|
266
319
|
//# 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-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n ShieldAlert,\n ChevronLeft,\n ChevronRight,\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 type { AdaPolicyViolation, AdaViolationStatus } from '../../types';\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\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 // 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 className=\"mb-6 flex flex-wrap items-center gap-3\">\n <select\n value={status}\n onChange={(e) => {\n setStatus(e.target.value as AdaViolationStatus | '');\n setOffset(0);\n }}\n aria-label={t('adaptercloud.violations.filterByStatus', 'Filter by status')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.violations.allStatuses', 'All statuses')}</option>\n {(Object.keys(violationStatusLabels) as AdaViolationStatus[]).map((s) => (\n <option key={s} value={s}>\n {violationStatusLabels[s]}\n </option>\n ))}\n </select>\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 </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-dom';\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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"}
|