@burdenoff/microfe-assethandler 2026.826.9 → 2026.826.10
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/assethandler/pages/SecurityComplianceDetailPage.d.ts.map +1 -1
- package/dist/assethandler/pages/SecurityComplianceDetailPage.js +230 -57
- package/dist/assethandler/pages/SecurityComplianceDetailPage.js.map +1 -1
- package/dist/assethandler/pages/SecurityPolicyDetailPage.d.ts.map +1 -1
- package/dist/assethandler/pages/SecurityPolicyDetailPage.js +160 -138
- package/dist/assethandler/pages/SecurityPolicyDetailPage.js.map +1 -1
- package/dist/assethandler/pages/SecuritySecurityHubPage.d.ts.map +1 -1
- package/dist/assethandler/pages/SecuritySecurityHubPage.js +320 -147
- package/dist/assethandler/pages/SecuritySecurityHubPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityComplianceDetailPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/SecurityComplianceDetailPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SecurityComplianceDetailPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/SecurityComplianceDetailPage.tsx"],"names":[],"mappings":"AA8FA,wBAAgB,4BAA4B,gCAuV3C"}
|
|
@@ -2,19 +2,20 @@ import { useAssetHandlerNavigate as e } from "../hooks/useAssetHandlerNavigate.j
|
|
|
2
2
|
import { notify as t } from "../utils/toast.js";
|
|
3
3
|
import { AttachmentsPanel as n } from "../components/AttachmentsPanel.js";
|
|
4
4
|
import { CommentsPanel as r } from "../components/CommentsPanel.js";
|
|
5
|
-
import { ActivityTimeline as
|
|
6
|
-
import { NotFoundState as
|
|
5
|
+
import { ActivityTimeline as ee } from "../components/ActivityTimeline.js";
|
|
6
|
+
import { NotFoundState as te } from "../components/NotFoundState.js";
|
|
7
7
|
import "../components/index.js";
|
|
8
|
-
import {
|
|
8
|
+
import { useState as i } from "react";
|
|
9
|
+
import { ArrowLeft as a, Edit as o, Trash2 as s } from "lucide-react";
|
|
9
10
|
import { useParams as c } from "react-router";
|
|
10
11
|
import { gql as l } from "@apollo/client";
|
|
11
12
|
import { useMutation as u, useQuery as d } from "@apollo/client/react";
|
|
12
13
|
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
13
|
-
import { Badge as m, Button as h, Card as g, CardContent as _, CardHeader as v, CardTitle as y, EmphasisPanel as
|
|
14
|
+
import { Badge as m, Button as h, Card as g, CardContent as _, CardHeader as v, CardTitle as y, EmphasisPanel as ne, GradientText as re, Input as b, Label as x, PagePurpose as ie, ResponsiveDialog as S, ResponsiveDialogContent as C, ResponsiveDialogDescription as w, ResponsiveDialogFooter as T, ResponsiveDialogHeader as E, ResponsiveDialogTitle as D, Select as O, SelectContent as k, SelectItem as A, SelectTrigger as j, SelectValue as M, Tabs as N, TabsContent as P, TabsList as F, TabsTrigger as I } from "@burdenoff/fe-libs/ui";
|
|
14
15
|
//#region src/assethandler/pages/SecurityComplianceDetailPage.tsx
|
|
15
|
-
var
|
|
16
|
+
var L = l`
|
|
16
17
|
query AssetHandlerComplianceFramework($id: ID!) {
|
|
17
|
-
|
|
18
|
+
securityComplianceFramework(id: $id) {
|
|
18
19
|
id
|
|
19
20
|
name
|
|
20
21
|
version
|
|
@@ -28,42 +29,64 @@ var D = l`
|
|
|
28
29
|
lastAssessedAt
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
|
-
`,
|
|
32
|
-
mutation
|
|
33
|
-
|
|
32
|
+
`, ae = l`
|
|
33
|
+
mutation AssetHandlerUpdateSecurityComplianceFramework(
|
|
34
|
+
$input: UpsertSecurityComplianceFrameworkInput!
|
|
35
|
+
) {
|
|
36
|
+
upsertSecurityComplianceFramework(input: $input) {
|
|
34
37
|
id
|
|
35
|
-
score
|
|
36
|
-
lastAssessedAt
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
`, R = l`
|
|
41
|
+
mutation AssetHandlerDeleteSecurityComplianceFramework($id: ID!) {
|
|
42
|
+
deleteSecurityComplianceFramework(id: $id)
|
|
43
|
+
}
|
|
44
|
+
`, z = (e) => {
|
|
45
|
+
let t = new Date(e);
|
|
46
|
+
return Number.isNaN(t.getTime()) ? "" : t.toISOString().slice(0, 10);
|
|
47
|
+
};
|
|
48
|
+
function B() {
|
|
49
|
+
let l = e(), { frameworkId: B } = c(), { data: V, loading: H, refetch: U } = d(L, {
|
|
50
|
+
variables: { id: B ?? "" },
|
|
51
|
+
skip: !B,
|
|
44
52
|
fetchPolicy: "cache-and-network"
|
|
45
|
-
}), [
|
|
53
|
+
}), [W, G] = i(!1), [K, q] = i(null), [J, { loading: Y }] = u(ae, {
|
|
46
54
|
refetchQueries: ["AssetHandlerBootstrap"],
|
|
47
55
|
onCompleted: () => {
|
|
48
|
-
t.success("
|
|
56
|
+
t.success("Framework updated", "The recorded assessment has been saved."), G(!1), U();
|
|
49
57
|
},
|
|
50
|
-
onError: (e) => t.error("Could not
|
|
58
|
+
onError: (e) => t.error("Could not update framework", e.message)
|
|
59
|
+
}), [oe, { loading: X }] = u(R, {
|
|
60
|
+
refetchQueries: ["AssetHandlerBootstrap"],
|
|
61
|
+
onCompleted: () => {
|
|
62
|
+
t.success("Framework deleted", "The framework is no longer tracked."), l("/security/security-hub");
|
|
63
|
+
},
|
|
64
|
+
onError: (e) => t.error("Could not delete framework", e.message)
|
|
51
65
|
});
|
|
52
|
-
if (
|
|
66
|
+
if (H && !V) return /* @__PURE__ */ f("div", {
|
|
53
67
|
className: "space-y-3 p-6",
|
|
54
68
|
children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ f("div", { className: "h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer" }, t))
|
|
55
69
|
});
|
|
56
|
-
let
|
|
57
|
-
if (!
|
|
70
|
+
let Z = V?.securityComplianceFramework;
|
|
71
|
+
if (!Z) return /* @__PURE__ */ f(te, {
|
|
58
72
|
entityLabel: "Framework",
|
|
59
73
|
description: "This compliance framework may have been removed. Return to the security hub to find it.",
|
|
60
74
|
backLabel: "Back to security",
|
|
61
75
|
onBack: () => l("/security/security-hub")
|
|
62
76
|
});
|
|
63
|
-
let
|
|
64
|
-
...
|
|
65
|
-
status:
|
|
66
|
-
}
|
|
77
|
+
let Q = {
|
|
78
|
+
...Z,
|
|
79
|
+
status: Z.status.toLowerCase()
|
|
80
|
+
}, se = () => {
|
|
81
|
+
q({
|
|
82
|
+
...Z,
|
|
83
|
+
lastAssessment: z(Z.lastAssessment),
|
|
84
|
+
nextAssessment: z(Z.nextAssessment)
|
|
85
|
+
}), G(!0);
|
|
86
|
+
}, $ = (e, t) => q((n) => n && {
|
|
87
|
+
...n,
|
|
88
|
+
[e]: t
|
|
89
|
+
});
|
|
67
90
|
return /* @__PURE__ */ p("div", {
|
|
68
91
|
className: "space-y-6 p-6",
|
|
69
92
|
children: [
|
|
@@ -76,40 +99,190 @@ function k() {
|
|
|
76
99
|
size: "icon",
|
|
77
100
|
"aria-label": "Go back",
|
|
78
101
|
onClick: () => l("/security/security-hub"),
|
|
79
|
-
children: /* @__PURE__ */ f(
|
|
102
|
+
children: /* @__PURE__ */ f(a, { className: "h-4 w-4" })
|
|
80
103
|
}), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ p("div", {
|
|
81
104
|
className: "flex items-center gap-3",
|
|
82
105
|
children: [/* @__PURE__ */ f("h1", {
|
|
83
106
|
className: "text-2xl font-bold",
|
|
84
|
-
children: /* @__PURE__ */ p(
|
|
85
|
-
|
|
107
|
+
children: /* @__PURE__ */ p(re, { children: [
|
|
108
|
+
Q.name,
|
|
86
109
|
" ",
|
|
87
|
-
|
|
110
|
+
Q.version
|
|
88
111
|
] })
|
|
89
112
|
}), /* @__PURE__ */ f(m, {
|
|
90
|
-
variant:
|
|
91
|
-
children:
|
|
113
|
+
variant: Q.status === "compliant" ? "default" : Q.status === "non_compliant" ? "destructive" : "secondary",
|
|
114
|
+
children: Q.status.replace("_", " ")
|
|
92
115
|
})]
|
|
93
116
|
}), /* @__PURE__ */ f("p", {
|
|
94
117
|
className: "text-sm text-text-secondary mt-1",
|
|
95
118
|
children: "Compliance framework assessment details"
|
|
96
119
|
})] })]
|
|
97
|
-
}), /* @__PURE__ */ p(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
}), /* @__PURE__ */ p("div", {
|
|
121
|
+
className: "flex gap-2",
|
|
122
|
+
children: [/* @__PURE__ */ p(h, {
|
|
123
|
+
size: "sm",
|
|
124
|
+
variant: "outline",
|
|
125
|
+
disabled: X,
|
|
126
|
+
onClick: () => {
|
|
127
|
+
window.confirm(`Delete ${Q.name}?`) && oe({ variables: { id: Q.id } });
|
|
128
|
+
},
|
|
129
|
+
children: [/* @__PURE__ */ f(s, { className: "mr-2 h-4 w-4" }), X ? "Deleting…" : "Delete"]
|
|
130
|
+
}), /* @__PURE__ */ p(h, {
|
|
131
|
+
size: "sm",
|
|
132
|
+
onClick: se,
|
|
133
|
+
children: [/* @__PURE__ */ f(o, { className: "mr-2 h-4 w-4" }), "Edit Assessment"]
|
|
134
|
+
})]
|
|
102
135
|
})]
|
|
103
136
|
}),
|
|
104
|
-
/* @__PURE__ */ f(S, {
|
|
137
|
+
/* @__PURE__ */ f(S, {
|
|
138
|
+
open: W,
|
|
139
|
+
onOpenChange: G,
|
|
140
|
+
children: /* @__PURE__ */ p(C, {
|
|
141
|
+
className: "max-h-[85vh] overflow-y-auto sm:max-w-2xl",
|
|
142
|
+
children: [
|
|
143
|
+
/* @__PURE__ */ p(E, { children: [/* @__PURE__ */ f(D, { children: "Edit Recorded Assessment" }), /* @__PURE__ */ f(w, { children: "Update the manually recorded framework score and control totals." })] }),
|
|
144
|
+
K ? /* @__PURE__ */ p("div", {
|
|
145
|
+
className: "space-y-4",
|
|
146
|
+
children: [
|
|
147
|
+
/* @__PURE__ */ p("div", {
|
|
148
|
+
className: "grid gap-4 md:grid-cols-2",
|
|
149
|
+
children: [/* @__PURE__ */ p("div", {
|
|
150
|
+
className: "space-y-2",
|
|
151
|
+
children: [/* @__PURE__ */ f(x, { children: "Name" }), /* @__PURE__ */ f(b, {
|
|
152
|
+
value: K.name,
|
|
153
|
+
onChange: (e) => $("name", e.target.value)
|
|
154
|
+
})]
|
|
155
|
+
}), /* @__PURE__ */ p("div", {
|
|
156
|
+
className: "space-y-2",
|
|
157
|
+
children: [/* @__PURE__ */ f(x, { children: "Version" }), /* @__PURE__ */ f(b, {
|
|
158
|
+
value: K.version,
|
|
159
|
+
onChange: (e) => $("version", e.target.value)
|
|
160
|
+
})]
|
|
161
|
+
})]
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ p("div", {
|
|
164
|
+
className: "grid gap-4 md:grid-cols-2",
|
|
165
|
+
children: [/* @__PURE__ */ p("div", {
|
|
166
|
+
className: "space-y-2",
|
|
167
|
+
children: [/* @__PURE__ */ f(x, { children: "Status" }), /* @__PURE__ */ p(O, {
|
|
168
|
+
value: K.status.toUpperCase(),
|
|
169
|
+
onValueChange: (e) => $("status", e),
|
|
170
|
+
children: [/* @__PURE__ */ f(j, { children: /* @__PURE__ */ f(M, {}) }), /* @__PURE__ */ f(k, { children: [
|
|
171
|
+
"COMPLIANT",
|
|
172
|
+
"NON_COMPLIANT",
|
|
173
|
+
"PARTIAL",
|
|
174
|
+
"UNDER_REVIEW"
|
|
175
|
+
].map((e) => /* @__PURE__ */ f(A, {
|
|
176
|
+
value: e,
|
|
177
|
+
children: e.replace(/_/g, " ")
|
|
178
|
+
}, e)) })]
|
|
179
|
+
})]
|
|
180
|
+
}), /* @__PURE__ */ p("div", {
|
|
181
|
+
className: "space-y-2",
|
|
182
|
+
children: [/* @__PURE__ */ f(x, { children: "Recorded Score" }), /* @__PURE__ */ f(b, {
|
|
183
|
+
type: "number",
|
|
184
|
+
min: "0",
|
|
185
|
+
max: "100",
|
|
186
|
+
value: K.score,
|
|
187
|
+
onChange: (e) => $("score", Number(e.target.value))
|
|
188
|
+
})]
|
|
189
|
+
})]
|
|
190
|
+
}),
|
|
191
|
+
/* @__PURE__ */ p("div", {
|
|
192
|
+
className: "grid gap-4 md:grid-cols-3",
|
|
193
|
+
children: [
|
|
194
|
+
/* @__PURE__ */ p("div", {
|
|
195
|
+
className: "space-y-2",
|
|
196
|
+
children: [/* @__PURE__ */ f(x, { children: "Total" }), /* @__PURE__ */ f(b, {
|
|
197
|
+
type: "number",
|
|
198
|
+
min: "0",
|
|
199
|
+
value: K.totalControls,
|
|
200
|
+
onChange: (e) => $("totalControls", Number(e.target.value))
|
|
201
|
+
})]
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ p("div", {
|
|
204
|
+
className: "space-y-2",
|
|
205
|
+
children: [/* @__PURE__ */ f(x, { children: "Passed" }), /* @__PURE__ */ f(b, {
|
|
206
|
+
type: "number",
|
|
207
|
+
min: "0",
|
|
208
|
+
value: K.passedControls,
|
|
209
|
+
onChange: (e) => $("passedControls", Number(e.target.value))
|
|
210
|
+
})]
|
|
211
|
+
}),
|
|
212
|
+
/* @__PURE__ */ p("div", {
|
|
213
|
+
className: "space-y-2",
|
|
214
|
+
children: [/* @__PURE__ */ f(x, { children: "Failed" }), /* @__PURE__ */ f(b, {
|
|
215
|
+
type: "number",
|
|
216
|
+
min: "0",
|
|
217
|
+
value: K.failedControls,
|
|
218
|
+
onChange: (e) => $("failedControls", Number(e.target.value))
|
|
219
|
+
})]
|
|
220
|
+
})
|
|
221
|
+
]
|
|
222
|
+
}),
|
|
223
|
+
/* @__PURE__ */ p("div", {
|
|
224
|
+
className: "grid gap-4 md:grid-cols-2",
|
|
225
|
+
children: [/* @__PURE__ */ p("div", {
|
|
226
|
+
className: "space-y-2",
|
|
227
|
+
children: [/* @__PURE__ */ f(x, { children: "Assessment Date" }), /* @__PURE__ */ f(b, {
|
|
228
|
+
type: "date",
|
|
229
|
+
value: K.lastAssessment,
|
|
230
|
+
onChange: (e) => $("lastAssessment", e.target.value)
|
|
231
|
+
})]
|
|
232
|
+
}), /* @__PURE__ */ p("div", {
|
|
233
|
+
className: "space-y-2",
|
|
234
|
+
children: [/* @__PURE__ */ f(x, { children: "Next Assessment" }), /* @__PURE__ */ f(b, {
|
|
235
|
+
type: "date",
|
|
236
|
+
value: K.nextAssessment,
|
|
237
|
+
onChange: (e) => $("nextAssessment", e.target.value)
|
|
238
|
+
})]
|
|
239
|
+
})]
|
|
240
|
+
})
|
|
241
|
+
]
|
|
242
|
+
}) : null,
|
|
243
|
+
/* @__PURE__ */ p(T, { children: [/* @__PURE__ */ f(h, {
|
|
244
|
+
variant: "outline",
|
|
245
|
+
onClick: () => G(!1),
|
|
246
|
+
disabled: Y,
|
|
247
|
+
children: "Cancel"
|
|
248
|
+
}), /* @__PURE__ */ f(h, {
|
|
249
|
+
onClick: () => {
|
|
250
|
+
if (K) {
|
|
251
|
+
if (K.score < 0 || K.score > 100 || K.passedControls + K.failedControls > K.totalControls) {
|
|
252
|
+
t.warning("Invalid assessment", "Score must be 0–100 and passed plus failed cannot exceed total.");
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
J({ variables: { input: {
|
|
256
|
+
id: Q.id,
|
|
257
|
+
name: K.name.trim(),
|
|
258
|
+
version: K.version.trim(),
|
|
259
|
+
status: K.status.toUpperCase(),
|
|
260
|
+
score: K.score,
|
|
261
|
+
totalControls: K.totalControls,
|
|
262
|
+
passedControls: K.passedControls,
|
|
263
|
+
failedControls: K.failedControls,
|
|
264
|
+
lastAssessment: new Date(K.lastAssessment).toISOString(),
|
|
265
|
+
lastAssessedAt: new Date(K.lastAssessment).toISOString(),
|
|
266
|
+
nextAssessment: new Date(K.nextAssessment).toISOString(),
|
|
267
|
+
sourceProduct: "assethandler"
|
|
268
|
+
} } });
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
disabled: Y,
|
|
272
|
+
children: Y ? "Saving…" : "Save Assessment"
|
|
273
|
+
})] })
|
|
274
|
+
]
|
|
275
|
+
})
|
|
276
|
+
}),
|
|
277
|
+
/* @__PURE__ */ f(ie, { children: "Review a manually recorded compliance snapshot: score, control totals, and assessment dates. Use it as governance evidence; AssetHandler does not run controls or certify compliance." }),
|
|
105
278
|
/* @__PURE__ */ p("div", {
|
|
106
279
|
className: "grid gap-4 md:grid-cols-4",
|
|
107
280
|
children: [
|
|
108
|
-
/* @__PURE__ */ p(
|
|
281
|
+
/* @__PURE__ */ p(ne, {
|
|
109
282
|
className: "text-center",
|
|
110
283
|
children: [/* @__PURE__ */ p("p", {
|
|
111
284
|
className: "text-4xl font-bold tabular-nums",
|
|
112
|
-
children: [
|
|
285
|
+
children: [Q.score, "%"]
|
|
113
286
|
}), /* @__PURE__ */ f("p", {
|
|
114
287
|
className: "text-xs text-text-secondary",
|
|
115
288
|
children: "Overall Score"
|
|
@@ -119,7 +292,7 @@ function k() {
|
|
|
119
292
|
className: "pt-4 text-center",
|
|
120
293
|
children: [/* @__PURE__ */ f("p", {
|
|
121
294
|
className: "text-4xl font-bold text-status-success-text",
|
|
122
|
-
children:
|
|
295
|
+
children: Q.passedControls
|
|
123
296
|
}), /* @__PURE__ */ f("p", {
|
|
124
297
|
className: "text-xs text-text-secondary",
|
|
125
298
|
children: "Passed"
|
|
@@ -129,7 +302,7 @@ function k() {
|
|
|
129
302
|
className: "pt-4 text-center",
|
|
130
303
|
children: [/* @__PURE__ */ f("p", {
|
|
131
304
|
className: "text-4xl font-bold text-status-error-text",
|
|
132
|
-
children:
|
|
305
|
+
children: Q.failedControls
|
|
133
306
|
}), /* @__PURE__ */ f("p", {
|
|
134
307
|
className: "text-xs text-text-secondary",
|
|
135
308
|
children: "Failed"
|
|
@@ -139,7 +312,7 @@ function k() {
|
|
|
139
312
|
className: "pt-4 text-center",
|
|
140
313
|
children: [/* @__PURE__ */ f("p", {
|
|
141
314
|
className: "text-4xl font-bold",
|
|
142
|
-
children:
|
|
315
|
+
children: Q.totalControls
|
|
143
316
|
}), /* @__PURE__ */ f("p", {
|
|
144
317
|
className: "text-xs text-text-secondary",
|
|
145
318
|
children: "Total Controls"
|
|
@@ -150,44 +323,44 @@ function k() {
|
|
|
150
323
|
/* @__PURE__ */ p(g, { children: [/* @__PURE__ */ f(v, { children: /* @__PURE__ */ f(y, { children: "Compliance Progress" }) }), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ f("div", {
|
|
151
324
|
className: "h-4 rounded-full bg-bg-sunken overflow-hidden",
|
|
152
325
|
children: /* @__PURE__ */ f("div", {
|
|
153
|
-
className: `h-full rounded-full ${
|
|
154
|
-
style: { width: `${
|
|
326
|
+
className: `h-full rounded-full ${Q.score >= 90 ? "bg-status-success-bg" : Q.score >= 70 ? "bg-status-warning-bg" : "bg-status-error-bg"}`,
|
|
327
|
+
style: { width: `${Q.score}%` }
|
|
155
328
|
})
|
|
156
329
|
}), /* @__PURE__ */ p("div", {
|
|
157
330
|
className: "flex flex-wrap justify-between gap-2 mt-2 text-xs text-text-secondary",
|
|
158
331
|
children: [
|
|
159
|
-
/* @__PURE__ */ p("span", { children: ["Last Assessment: ",
|
|
160
|
-
|
|
161
|
-
/* @__PURE__ */ p("span", { children: ["Next Assessment: ",
|
|
332
|
+
/* @__PURE__ */ p("span", { children: ["Last Assessment: ", Q.lastAssessment] }),
|
|
333
|
+
Q.lastAssessedAt ? /* @__PURE__ */ p("span", { children: ["Last Run: ", new Date(Q.lastAssessedAt).toLocaleString()] }) : null,
|
|
334
|
+
/* @__PURE__ */ p("span", { children: ["Next Assessment: ", Q.nextAssessment] })
|
|
162
335
|
]
|
|
163
336
|
})] })] }),
|
|
164
|
-
/* @__PURE__ */ p(
|
|
337
|
+
/* @__PURE__ */ p(N, {
|
|
165
338
|
defaultValue: "attachments",
|
|
166
339
|
children: [
|
|
167
|
-
/* @__PURE__ */ p(
|
|
340
|
+
/* @__PURE__ */ p(F, { children: [/* @__PURE__ */ f(I, {
|
|
168
341
|
value: "attachments",
|
|
169
342
|
children: "Evidence"
|
|
170
|
-
}), /* @__PURE__ */ f(
|
|
343
|
+
}), /* @__PURE__ */ f(I, {
|
|
171
344
|
value: "discussion",
|
|
172
345
|
children: "Discussion"
|
|
173
346
|
})] }),
|
|
174
|
-
/* @__PURE__ */ f(
|
|
347
|
+
/* @__PURE__ */ f(P, {
|
|
175
348
|
value: "attachments",
|
|
176
349
|
className: "mt-4",
|
|
177
350
|
children: /* @__PURE__ */ f(n, {
|
|
178
351
|
entityType: "assethandler-compliance-framework",
|
|
179
|
-
entityId:
|
|
352
|
+
entityId: Q.id
|
|
180
353
|
})
|
|
181
354
|
}),
|
|
182
|
-
/* @__PURE__ */ f(
|
|
355
|
+
/* @__PURE__ */ f(P, {
|
|
183
356
|
value: "discussion",
|
|
184
357
|
className: "mt-4 space-y-6",
|
|
185
358
|
children: /* @__PURE__ */ p("div", {
|
|
186
359
|
className: "grid gap-6 lg:grid-cols-2",
|
|
187
360
|
children: [/* @__PURE__ */ f(r, {
|
|
188
361
|
entityType: "assethandler-compliance-framework",
|
|
189
|
-
entityId:
|
|
190
|
-
}), /* @__PURE__ */ f(
|
|
362
|
+
entityId: Q.id
|
|
363
|
+
}), /* @__PURE__ */ f(ee, { resourceId: Q.id })]
|
|
191
364
|
})
|
|
192
365
|
})
|
|
193
366
|
]
|
|
@@ -196,6 +369,6 @@ function k() {
|
|
|
196
369
|
});
|
|
197
370
|
}
|
|
198
371
|
//#endregion
|
|
199
|
-
export {
|
|
372
|
+
export { B as SecurityComplianceDetailPage };
|
|
200
373
|
|
|
201
374
|
//# sourceMappingURL=SecurityComplianceDetailPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityComplianceDetailPage.js","names":[],"sources":["../../../src/assethandler/pages/SecurityComplianceDetailPage.tsx"],"sourcesContent":["import { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowLeft, Shield } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { notify } from '../utils/toast';\nimport { AttachmentsPanel, CommentsPanel, ActivityTimeline, NotFoundState } from '../components';\n\nconst COMPLIANCE_QUERY = gql`\n query AssetHandlerComplianceFramework($id: ID!) {\n complianceFramework(id: $id) {\n id\n name\n version\n status\n score\n totalControls\n passedControls\n failedControls\n lastAssessment\n nextAssessment\n lastAssessedAt\n }\n }\n`;\n\ninterface ComplianceRecord {\n id: string;\n name: string;\n version: string;\n status: string;\n score: number;\n totalControls: number;\n passedControls: number;\n failedControls: number;\n lastAssessment: string;\n nextAssessment: string;\n lastAssessedAt?: string | null;\n}\n\ninterface ComplianceQueryResult {\n complianceFramework: ComplianceRecord | null;\n}\n\nconst RUN_ASSESSMENT_MUTATION = gql`\n mutation AssetHandlerRunComplianceAssessment($frameworkId: ID!) {\n runComplianceAssessment(frameworkId: $frameworkId) {\n id\n score\n lastAssessedAt\n }\n }\n`;\n\nexport function SecurityComplianceDetailPage() {\n const navigate = useAssetHandlerNavigate();\n const { frameworkId } = useParams<{ frameworkId: string }>();\n\n const { data, loading, refetch } = useQuery<ComplianceQueryResult>(COMPLIANCE_QUERY, {\n variables: { id: frameworkId ?? '' },\n skip: !frameworkId,\n fetchPolicy: 'cache-and-network',\n });\n\n const [runAssessment, { loading: running }] = useMutation(RUN_ASSESSMENT_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Assessment complete', 'The framework score has been refreshed.');\n void refetch();\n },\n onError: (error) => notify.error('Could not run assessment', error.message),\n });\n\n if (loading && !data) {\n return (\n <div className=\"space-y-3 p-6\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n );\n }\n\n const raw = data?.complianceFramework;\n if (!raw) {\n return (\n <NotFoundState\n entityLabel=\"Framework\"\n description=\"This compliance framework may have been removed. Return to the security hub to find it.\"\n backLabel=\"Back to security\"\n onBack={() => navigate('/security/security-hub')}\n />\n );\n }\n\n const framework = { ...raw, status: raw.status.toLowerCase() };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-4\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label=\"Go back\"\n onClick={() => navigate('/security/security-hub')}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl font-bold\">\n <GradientText>\n {framework.name} {framework.version}\n </GradientText>\n </h1>\n <Badge\n variant={\n framework.status === 'compliant'\n ? 'default'\n : framework.status === 'non_compliant'\n ? 'destructive'\n : 'secondary'\n }\n >\n {framework.status.replace('_', ' ')}\n </Badge>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n Compliance framework assessment details\n </p>\n </div>\n </div>\n <Button\n size=\"sm\"\n onClick={() => void runAssessment({ variables: { frameworkId: framework.id } })}\n disabled={running}\n >\n <Shield className=\"mr-2 h-4 w-4\" />\n {running ? 'Running…' : 'Run Assessment'}\n </Button>\n </div>\n\n <PagePurpose>\n See exactly how you measure up against a single compliance framework — your overall score,\n which controls pass or fail, and when the next assessment is due. Use it to close control\n gaps and stay audit-ready for standards like ISO 27001 or SOC 2.\n </PagePurpose>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <EmphasisPanel className=\"text-center\">\n <p className=\"text-4xl font-bold tabular-nums\">{framework.score}%</p>\n <p className=\"text-xs text-text-secondary\">Overall Score</p>\n </EmphasisPanel>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <p className=\"text-4xl font-bold text-status-success-text\">\n {framework.passedControls}\n </p>\n <p className=\"text-xs text-text-secondary\">Passed</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <p className=\"text-4xl font-bold text-status-error-text\">{framework.failedControls}</p>\n <p className=\"text-xs text-text-secondary\">Failed</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <p className=\"text-4xl font-bold\">{framework.totalControls}</p>\n <p className=\"text-xs text-text-secondary\">Total Controls</p>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Compliance Progress</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"h-4 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className={`h-full rounded-full ${framework.score >= 90 ? 'bg-status-success-bg' : framework.score >= 70 ? 'bg-status-warning-bg' : 'bg-status-error-bg'}`}\n style={{ width: `${framework.score}%` }}\n />\n </div>\n <div className=\"flex flex-wrap justify-between gap-2 mt-2 text-xs text-text-secondary\">\n <span>Last Assessment: {framework.lastAssessment}</span>\n {framework.lastAssessedAt ? (\n <span>Last Run: {new Date(framework.lastAssessedAt).toLocaleString()}</span>\n ) : null}\n <span>Next Assessment: {framework.nextAssessment}</span>\n </div>\n </CardContent>\n </Card>\n\n <Tabs defaultValue=\"attachments\">\n <TabsList>\n <TabsTrigger value=\"attachments\">Evidence</TabsTrigger>\n <TabsTrigger value=\"discussion\">Discussion</TabsTrigger>\n </TabsList>\n <TabsContent value=\"attachments\" className=\"mt-4\">\n <AttachmentsPanel\n entityType=\"assethandler-compliance-framework\"\n entityId={framework.id}\n />\n </TabsContent>\n <TabsContent value=\"discussion\" className=\"mt-4 space-y-6\">\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <CommentsPanel entityType=\"assethandler-compliance-framework\" entityId={framework.id} />\n <ActivityTimeline resourceId={framework.id} />\n </div>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SecurityComplianceDetailPage.js","names":[],"sources":["../../../src/assethandler/pages/SecurityComplianceDetailPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useAssetHandlerNavigate } from '../hooks/useAssetHandlerNavigate';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n GradientText,\n EmphasisPanel,\n PagePurpose,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { ArrowLeft, Edit, Trash2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { notify } from '../utils/toast';\nimport { AttachmentsPanel, CommentsPanel, ActivityTimeline, NotFoundState } from '../components';\n\nconst COMPLIANCE_QUERY = gql`\n query AssetHandlerComplianceFramework($id: ID!) {\n securityComplianceFramework(id: $id) {\n id\n name\n version\n status\n score\n totalControls\n passedControls\n failedControls\n lastAssessment\n nextAssessment\n lastAssessedAt\n }\n }\n`;\n\ninterface ComplianceRecord {\n id: string;\n name: string;\n version: string;\n status: string;\n score: number;\n totalControls: number;\n passedControls: number;\n failedControls: number;\n lastAssessment: string;\n nextAssessment: string;\n lastAssessedAt?: string | null;\n}\n\ninterface ComplianceQueryResult {\n securityComplianceFramework: ComplianceRecord | null;\n}\n\nconst UPDATE_FRAMEWORK_MUTATION = gql`\n mutation AssetHandlerUpdateSecurityComplianceFramework(\n $input: UpsertSecurityComplianceFrameworkInput!\n ) {\n upsertSecurityComplianceFramework(input: $input) {\n id\n }\n }\n`;\n\nconst DELETE_FRAMEWORK_MUTATION = gql`\n mutation AssetHandlerDeleteSecurityComplianceFramework($id: ID!) {\n deleteSecurityComplianceFramework(id: $id)\n }\n`;\n\nconst toDateInput = (iso: string): string => {\n const value = new Date(iso);\n return Number.isNaN(value.getTime()) ? '' : value.toISOString().slice(0, 10);\n};\n\nexport function SecurityComplianceDetailPage() {\n const navigate = useAssetHandlerNavigate();\n const { frameworkId } = useParams<{ frameworkId: string }>();\n\n const { data, loading, refetch } = useQuery<ComplianceQueryResult>(COMPLIANCE_QUERY, {\n variables: { id: frameworkId ?? '' },\n skip: !frameworkId,\n fetchPolicy: 'cache-and-network',\n });\n\n const [editing, setEditing] = useState(false);\n const [form, setForm] = useState<ComplianceRecord | null>(null);\n\n const [updateFramework, { loading: updating }] = useMutation(UPDATE_FRAMEWORK_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Framework updated', 'The recorded assessment has been saved.');\n setEditing(false);\n void refetch();\n },\n onError: (error) => notify.error('Could not update framework', error.message),\n });\n\n const [deleteFramework, { loading: deleting }] = useMutation(DELETE_FRAMEWORK_MUTATION, {\n refetchQueries: ['AssetHandlerBootstrap'],\n onCompleted: () => {\n notify.success('Framework deleted', 'The framework is no longer tracked.');\n navigate('/security/security-hub');\n },\n onError: (error) => notify.error('Could not delete framework', error.message),\n });\n\n if (loading && !data) {\n return (\n <div className=\"space-y-3 p-6\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-10 rounded-md bg-bg-sunken motion-safe:animate-vc-shimmer\" />\n ))}\n </div>\n );\n }\n\n const raw = data?.securityComplianceFramework;\n if (!raw) {\n return (\n <NotFoundState\n entityLabel=\"Framework\"\n description=\"This compliance framework may have been removed. Return to the security hub to find it.\"\n backLabel=\"Back to security\"\n onBack={() => navigate('/security/security-hub')}\n />\n );\n }\n\n const framework = { ...raw, status: raw.status.toLowerCase() };\n\n const openEdit = () => {\n setForm({\n ...raw,\n lastAssessment: toDateInput(raw.lastAssessment),\n nextAssessment: toDateInput(raw.nextAssessment),\n });\n setEditing(true);\n };\n\n const setField = (field: keyof ComplianceRecord, value: string | number) =>\n setForm((current) => (current ? { ...current, [field]: value } : current));\n\n const save = () => {\n if (!form) return;\n if (\n form.score < 0 ||\n form.score > 100 ||\n form.passedControls + form.failedControls > form.totalControls\n ) {\n notify.warning(\n 'Invalid assessment',\n 'Score must be 0–100 and passed plus failed cannot exceed total.'\n );\n return;\n }\n void updateFramework({\n variables: {\n input: {\n id: framework.id,\n name: form.name.trim(),\n version: form.version.trim(),\n status: form.status.toUpperCase(),\n score: form.score,\n totalControls: form.totalControls,\n passedControls: form.passedControls,\n failedControls: form.failedControls,\n lastAssessment: new Date(form.lastAssessment).toISOString(),\n lastAssessedAt: new Date(form.lastAssessment).toISOString(),\n nextAssessment: new Date(form.nextAssessment).toISOString(),\n sourceProduct: 'assethandler',\n },\n },\n });\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-4\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label=\"Go back\"\n onClick={() => navigate('/security/security-hub')}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl font-bold\">\n <GradientText>\n {framework.name} {framework.version}\n </GradientText>\n </h1>\n <Badge\n variant={\n framework.status === 'compliant'\n ? 'default'\n : framework.status === 'non_compliant'\n ? 'destructive'\n : 'secondary'\n }\n >\n {framework.status.replace('_', ' ')}\n </Badge>\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n Compliance framework assessment details\n </p>\n </div>\n </div>\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled={deleting}\n onClick={() => {\n if (window.confirm(`Delete ${framework.name}?`))\n void deleteFramework({ variables: { id: framework.id } });\n }}\n >\n <Trash2 className=\"mr-2 h-4 w-4\" />\n {deleting ? 'Deleting…' : 'Delete'}\n </Button>\n <Button size=\"sm\" onClick={openEdit}>\n <Edit className=\"mr-2 h-4 w-4\" />\n Edit Assessment\n </Button>\n </div>\n </div>\n\n <ResponsiveDialog open={editing} onOpenChange={setEditing}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-2xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit Recorded Assessment</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Update the manually recorded framework score and control totals.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n {form ? (\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Name</Label>\n <Input value={form.name} onChange={(e) => setField('name', e.target.value)} />\n </div>\n <div className=\"space-y-2\">\n <Label>Version</Label>\n <Input\n value={form.version}\n onChange={(e) => setField('version', e.target.value)}\n />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Status</Label>\n <Select\n value={form.status.toUpperCase()}\n onValueChange={(value) => setField('status', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {['COMPLIANT', 'NON_COMPLIANT', 'PARTIAL', 'UNDER_REVIEW'].map((value) => (\n <SelectItem key={value} value={value}>\n {value.replace(/_/g, ' ')}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-2\">\n <Label>Recorded Score</Label>\n <Input\n type=\"number\"\n min=\"0\"\n max=\"100\"\n value={form.score}\n onChange={(e) => setField('score', Number(e.target.value))}\n />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-3\">\n <div className=\"space-y-2\">\n <Label>Total</Label>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.totalControls}\n onChange={(e) => setField('totalControls', Number(e.target.value))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Passed</Label>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.passedControls}\n onChange={(e) => setField('passedControls', Number(e.target.value))}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Failed</Label>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.failedControls}\n onChange={(e) => setField('failedControls', Number(e.target.value))}\n />\n </div>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2\">\n <Label>Assessment Date</Label>\n <Input\n type=\"date\"\n value={form.lastAssessment}\n onChange={(e) => setField('lastAssessment', e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <Label>Next Assessment</Label>\n <Input\n type=\"date\"\n value={form.nextAssessment}\n onChange={(e) => setField('nextAssessment', e.target.value)}\n />\n </div>\n </div>\n </div>\n ) : null}\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setEditing(false)} disabled={updating}>\n Cancel\n </Button>\n <Button onClick={save} disabled={updating}>\n {updating ? 'Saving…' : 'Save Assessment'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n <PagePurpose>\n Review a manually recorded compliance snapshot: score, control totals, and assessment dates.\n Use it as governance evidence; AssetHandler does not run controls or certify compliance.\n </PagePurpose>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <EmphasisPanel className=\"text-center\">\n <p className=\"text-4xl font-bold tabular-nums\">{framework.score}%</p>\n <p className=\"text-xs text-text-secondary\">Overall Score</p>\n </EmphasisPanel>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <p className=\"text-4xl font-bold text-status-success-text\">\n {framework.passedControls}\n </p>\n <p className=\"text-xs text-text-secondary\">Passed</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <p className=\"text-4xl font-bold text-status-error-text\">{framework.failedControls}</p>\n <p className=\"text-xs text-text-secondary\">Failed</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <p className=\"text-4xl font-bold\">{framework.totalControls}</p>\n <p className=\"text-xs text-text-secondary\">Total Controls</p>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Compliance Progress</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"h-4 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className={`h-full rounded-full ${framework.score >= 90 ? 'bg-status-success-bg' : framework.score >= 70 ? 'bg-status-warning-bg' : 'bg-status-error-bg'}`}\n style={{ width: `${framework.score}%` }}\n />\n </div>\n <div className=\"flex flex-wrap justify-between gap-2 mt-2 text-xs text-text-secondary\">\n <span>Last Assessment: {framework.lastAssessment}</span>\n {framework.lastAssessedAt ? (\n <span>Last Run: {new Date(framework.lastAssessedAt).toLocaleString()}</span>\n ) : null}\n <span>Next Assessment: {framework.nextAssessment}</span>\n </div>\n </CardContent>\n </Card>\n\n <Tabs defaultValue=\"attachments\">\n <TabsList>\n <TabsTrigger value=\"attachments\">Evidence</TabsTrigger>\n <TabsTrigger value=\"discussion\">Discussion</TabsTrigger>\n </TabsList>\n <TabsContent value=\"attachments\" className=\"mt-4\">\n <AttachmentsPanel\n entityType=\"assethandler-compliance-framework\"\n entityId={framework.id}\n />\n </TabsContent>\n <TabsContent value=\"discussion\" className=\"mt-4 space-y-6\">\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <CommentsPanel entityType=\"assethandler-compliance-framework\" entityId={framework.id} />\n <ActivityTimeline resourceId={framework.id} />\n </div>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityPolicyDetailPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/SecurityPolicyDetailPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SecurityPolicyDetailPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/SecurityPolicyDetailPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAmH/B,wBAAgB,wBAAwB,sBAmXvC"}
|