@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.
@@ -1 +1 @@
1
- {"version":3,"file":"SecurityComplianceDetailPage.d.ts","sourceRoot":"","sources":["../../../src/assethandler/pages/SecurityComplianceDetailPage.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,4BAA4B,gCAmK3C"}
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 i } from "../components/ActivityTimeline.js";
6
- import { NotFoundState as a } from "../components/NotFoundState.js";
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 { ArrowLeft as o, Shield as s } from "lucide-react";
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 b, GradientText as x, PagePurpose as S, Tabs as C, TabsContent as w, TabsList as T, TabsTrigger as E } from "@burdenoff/fe-libs/ui";
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 D = l`
16
+ var L = l`
16
17
  query AssetHandlerComplianceFramework($id: ID!) {
17
- complianceFramework(id: $id) {
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
- `, O = l`
32
- mutation AssetHandlerRunComplianceAssessment($frameworkId: ID!) {
33
- runComplianceAssessment(frameworkId: $frameworkId) {
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
- function k() {
41
- let l = e(), { frameworkId: k } = c(), { data: A, loading: j, refetch: M } = d(D, {
42
- variables: { id: k ?? "" },
43
- skip: !k,
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
- }), [N, { loading: P }] = u(O, {
53
+ }), [W, G] = i(!1), [K, q] = i(null), [J, { loading: Y }] = u(ae, {
46
54
  refetchQueries: ["AssetHandlerBootstrap"],
47
55
  onCompleted: () => {
48
- t.success("Assessment complete", "The framework score has been refreshed."), M();
56
+ t.success("Framework updated", "The recorded assessment has been saved."), G(!1), U();
49
57
  },
50
- onError: (e) => t.error("Could not run assessment", e.message)
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 (j && !A) return /* @__PURE__ */ f("div", {
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 F = A?.complianceFramework;
57
- if (!F) return /* @__PURE__ */ f(a, {
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 I = {
64
- ...F,
65
- status: F.status.toLowerCase()
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(o, { className: "h-4 w-4" })
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(x, { children: [
85
- I.name,
107
+ children: /* @__PURE__ */ p(re, { children: [
108
+ Q.name,
86
109
  " ",
87
- I.version
110
+ Q.version
88
111
  ] })
89
112
  }), /* @__PURE__ */ f(m, {
90
- variant: I.status === "compliant" ? "default" : I.status === "non_compliant" ? "destructive" : "secondary",
91
- children: I.status.replace("_", " ")
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(h, {
98
- size: "sm",
99
- onClick: () => void N({ variables: { frameworkId: I.id } }),
100
- disabled: P,
101
- children: [/* @__PURE__ */ f(s, { className: "mr-2 h-4 w-4" }), P ? "Running…" : "Run Assessment"]
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, { children: "See exactly how you measure up against a single compliance framework — your overall score, which controls pass or fail, and when the next assessment is due. Use it to close control gaps and stay audit-ready for standards like ISO 27001 or SOC 2." }),
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(b, {
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: [I.score, "%"]
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: I.passedControls
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: I.failedControls
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: I.totalControls
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 ${I.score >= 90 ? "bg-status-success-bg" : I.score >= 70 ? "bg-status-warning-bg" : "bg-status-error-bg"}`,
154
- style: { width: `${I.score}%` }
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: ", I.lastAssessment] }),
160
- I.lastAssessedAt ? /* @__PURE__ */ p("span", { children: ["Last Run: ", new Date(I.lastAssessedAt).toLocaleString()] }) : null,
161
- /* @__PURE__ */ p("span", { children: ["Next Assessment: ", I.nextAssessment] })
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(C, {
337
+ /* @__PURE__ */ p(N, {
165
338
  defaultValue: "attachments",
166
339
  children: [
167
- /* @__PURE__ */ p(T, { children: [/* @__PURE__ */ f(E, {
340
+ /* @__PURE__ */ p(F, { children: [/* @__PURE__ */ f(I, {
168
341
  value: "attachments",
169
342
  children: "Evidence"
170
- }), /* @__PURE__ */ f(E, {
343
+ }), /* @__PURE__ */ f(I, {
171
344
  value: "discussion",
172
345
  children: "Discussion"
173
346
  })] }),
174
- /* @__PURE__ */ f(w, {
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: I.id
352
+ entityId: Q.id
180
353
  })
181
354
  }),
182
- /* @__PURE__ */ f(w, {
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: I.id
190
- }), /* @__PURE__ */ f(i, { resourceId: I.id })]
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 { k as SecurityComplianceDetailPage };
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;AA2G/B,wBAAgB,wBAAwB,sBAmVvC"}
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"}