@kopexa/grc 0.0.2 → 0.0.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.
Files changed (61) hide show
  1. package/dist/chunk-5TBN3JQA.mjs +66 -0
  2. package/dist/{chunk-TW3S4OE2.mjs → chunk-AGASJJ7X.mjs} +106 -82
  3. package/dist/chunk-DC44K745.mjs +46 -0
  4. package/dist/chunk-HI7F2CF4.mjs +1 -0
  5. package/dist/chunk-HJUSN7FD.mjs +1 -0
  6. package/dist/chunk-QDYL5ABK.mjs +118 -0
  7. package/dist/chunk-QS5S6V26.mjs +22 -0
  8. package/dist/chunk-VFX3DASQ.mjs +57 -0
  9. package/dist/common/control/index.d.mts +3 -0
  10. package/dist/common/control/index.d.ts +3 -0
  11. package/dist/common/control/index.js +160 -0
  12. package/dist/common/control/index.mjs +11 -0
  13. package/dist/common/control/mapped-controls.d.mts +33 -0
  14. package/dist/common/control/mapped-controls.d.ts +33 -0
  15. package/dist/common/control/mapped-controls.js +159 -0
  16. package/dist/common/control/mapped-controls.mjs +11 -0
  17. package/dist/common/control/messages.d.mts +16 -0
  18. package/dist/common/control/messages.d.ts +16 -0
  19. package/dist/common/control/messages.js +45 -0
  20. package/dist/common/control/messages.mjs +7 -0
  21. package/dist/common/impact/impact-card.d.mts +7 -1
  22. package/dist/common/impact/impact-card.d.ts +7 -1
  23. package/dist/common/impact/impact-card.js +105 -81
  24. package/dist/common/impact/impact-card.mjs +1 -1
  25. package/dist/common/impact/index.js +105 -81
  26. package/dist/common/impact/index.mjs +1 -1
  27. package/dist/common/index.d.mts +5 -0
  28. package/dist/common/index.d.ts +5 -0
  29. package/dist/common/index.js +458 -145
  30. package/dist/common/index.mjs +27 -2
  31. package/dist/common/risk/index.d.mts +4 -0
  32. package/dist/common/risk/index.d.ts +4 -0
  33. package/dist/common/risk/index.js +185 -0
  34. package/dist/common/risk/index.mjs +20 -0
  35. package/dist/common/risk/messages.d.mts +40 -0
  36. package/dist/common/risk/messages.d.ts +40 -0
  37. package/dist/common/risk/messages.js +69 -0
  38. package/dist/common/risk/messages.mjs +7 -0
  39. package/dist/common/risk/risk-rating-display.d.mts +21 -0
  40. package/dist/common/risk/risk-rating-display.d.ts +21 -0
  41. package/dist/common/risk/risk-rating-display.js +139 -0
  42. package/dist/common/risk/risk-rating-display.mjs +10 -0
  43. package/dist/common/risk/types.d.mts +37 -0
  44. package/dist/common/risk/types.d.ts +37 -0
  45. package/dist/common/risk/types.js +82 -0
  46. package/dist/common/risk/types.mjs +11 -0
  47. package/dist/index.d.mts +5 -0
  48. package/dist/index.d.ts +5 -0
  49. package/dist/index.js +458 -145
  50. package/dist/index.mjs +27 -2
  51. package/package.json +8 -7
  52. package/src/common/control/index.ts +6 -0
  53. package/src/common/control/mapped-controls.tsx +192 -0
  54. package/src/common/control/messages.ts +16 -0
  55. package/src/common/impact/impact-card.tsx +132 -79
  56. package/src/common/index.ts +2 -0
  57. package/src/common/risk/index.ts +12 -0
  58. package/src/common/risk/messages.ts +40 -0
  59. package/src/common/risk/risk-rating-display.tsx +86 -0
  60. package/src/common/risk/types.ts +91 -0
  61. /package/dist/{chunk-BFZPRJQT.mjs → chunk-CND77GVC.mjs} +0 -0
package/dist/index.js CHANGED
@@ -22,14 +22,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
24
  ComplianceBadges: () => ComplianceBadges,
25
+ ControlChip: () => ControlChip,
25
26
  DoraBadge: () => DoraBadge,
26
27
  ImpactCard: () => ImpactCard,
28
+ MappedControls: () => MappedControls,
27
29
  Nis2Badge: () => Nis2Badge,
30
+ RiskRatingDisplay: () => RiskRatingDisplay,
28
31
  assetScale: () => assetScale,
32
+ getRiskLevelFromRating: () => getRiskLevelFromRating,
29
33
  getScale: () => getScale,
30
34
  impactLevels: () => impactLevels,
31
- impactMessages: () => messages2,
35
+ impactMessages: () => messages3,
36
+ isRatingUnrated: () => isRatingUnrated,
32
37
  processScale: () => processScale,
38
+ riskLevelConfig: () => riskLevelConfig,
39
+ riskMessages: () => messages4,
33
40
  riskScale: () => riskScale
34
41
  });
35
42
  module.exports = __toCommonJS(index_exports);
@@ -104,16 +111,144 @@ function ComplianceBadges({
104
111
  ] });
105
112
  }
106
113
 
107
- // src/common/impact/impact-card.tsx
114
+ // src/common/control/mapped-controls.tsx
108
115
  var import_i18n4 = require("@kopexa/i18n");
109
- var import_icons2 = require("@kopexa/icons");
116
+ var import_krn = require("@kopexa/krn");
110
117
  var import_sight2 = require("@kopexa/sight");
111
118
  var import_theme2 = require("@kopexa/theme");
112
- var import_react = require("react");
113
119
 
114
- // src/common/impact/messages.ts
120
+ // src/common/control/messages.ts
115
121
  var import_i18n3 = require("@kopexa/i18n");
116
122
  var messages2 = (0, import_i18n3.defineMessages)({
123
+ more_controls: {
124
+ id: "grc.control.mapped.more",
125
+ defaultMessage: "+{count} more"
126
+ },
127
+ view_all: {
128
+ id: "grc.control.mapped.view_all",
129
+ defaultMessage: "View all {count} controls"
130
+ },
131
+ no_controls: {
132
+ id: "grc.control.mapped.no_controls",
133
+ defaultMessage: "No mapped controls"
134
+ }
135
+ });
136
+
137
+ // src/common/control/mapped-controls.tsx
138
+ var import_jsx_runtime2 = require("react/jsx-runtime");
139
+ function formatControlKrn(krnString) {
140
+ const parsed = import_krn.KRN.tryParse(krnString);
141
+ if (!parsed) {
142
+ return { framework: null, controlId: krnString, full: krnString };
143
+ }
144
+ const framework = parsed.tryResourceId("frameworks");
145
+ const control = parsed.tryResourceId("controls");
146
+ if (framework && control) {
147
+ return {
148
+ framework,
149
+ controlId: control,
150
+ full: `${framework}:${control}`
151
+ };
152
+ }
153
+ const basename = parsed.basename();
154
+ return {
155
+ framework: null,
156
+ controlId: basename || krnString,
157
+ full: basename || krnString
158
+ };
159
+ }
160
+ function ControlChip({
161
+ krn,
162
+ size = "md",
163
+ interactive = false,
164
+ onClick,
165
+ className
166
+ }) {
167
+ const styles = (0, import_theme2.relatedControlChip)({ size, interactive });
168
+ const { controlId, full } = formatControlKrn(krn);
169
+ const displayId = controlId.toUpperCase();
170
+ const content = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.chipId(), children: displayId });
171
+ const chip = interactive && onClick ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
172
+ "button",
173
+ {
174
+ type: "button",
175
+ className: styles.chip({ className }),
176
+ onClick,
177
+ children: content
178
+ }
179
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.chip({ className }), children: content });
180
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Tooltip, { content: full, children: chip });
181
+ }
182
+ function MappedControls({
183
+ controls,
184
+ maxVisible = 2,
185
+ size = "md",
186
+ interactive = false,
187
+ onControlClick,
188
+ showEmpty = false,
189
+ className,
190
+ ...rest
191
+ }) {
192
+ const intl = (0, import_i18n4.useSafeIntl)();
193
+ const styles = (0, import_theme2.relatedControlChip)({ size, interactive });
194
+ if (controls.length === 0) {
195
+ if (!showEmpty) return null;
196
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs text-muted-foreground", children: intl.formatMessage(messages2.no_controls) });
197
+ }
198
+ const visibleControls = controls.slice(0, maxVisible);
199
+ const hiddenCount = controls.length - maxVisible;
200
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: styles.root({ className }), ...rest, children: [
201
+ visibleControls.map((krn) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
202
+ ControlChip,
203
+ {
204
+ krn,
205
+ size,
206
+ interactive,
207
+ onClick: onControlClick ? () => onControlClick(krn) : void 0
208
+ },
209
+ krn
210
+ )),
211
+ hiddenCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sight2.Popover.Root, { children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Popover.Trigger, { className: styles.overflow(), children: intl.formatMessage(messages2.more_controls, {
213
+ count: hiddenCount
214
+ }) }),
215
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
216
+ import_sight2.Popover.Content,
217
+ {
218
+ align: "start",
219
+ className: "p-2 max-w-xs",
220
+ showArrow: false,
221
+ children: [
222
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "text-xs font-medium text-muted-foreground mb-2", children: intl.formatMessage(messages2.view_all, {
223
+ count: controls.length
224
+ }) }),
225
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-wrap gap-1", children: controls.map((krn) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
226
+ ControlChip,
227
+ {
228
+ krn,
229
+ size,
230
+ interactive,
231
+ onClick: onControlClick ? () => onControlClick(krn) : void 0
232
+ },
233
+ krn
234
+ )) })
235
+ ]
236
+ }
237
+ )
238
+ ] })
239
+ ] });
240
+ }
241
+
242
+ // src/common/impact/impact-card.tsx
243
+ var import_i18n6 = require("@kopexa/i18n");
244
+ var import_icons2 = require("@kopexa/icons");
245
+ var import_sight3 = require("@kopexa/sight");
246
+ var import_theme3 = require("@kopexa/theme");
247
+ var import_react = require("react");
248
+
249
+ // src/common/impact/messages.ts
250
+ var import_i18n5 = require("@kopexa/i18n");
251
+ var messages3 = (0, import_i18n5.defineMessages)({
117
252
  // Card titles
118
253
  title_cia: {
119
254
  id: "grc.impact.title_cia",
@@ -250,42 +385,42 @@ var messages2 = (0, import_i18n3.defineMessages)({
250
385
  // src/common/impact/scales.ts
251
386
  var riskScale = {
252
387
  0: {
253
- message: messages2.risk_0,
388
+ message: messages3.risk_0,
254
389
  fallbackLabel: "Not rated",
255
390
  color: "text-muted-foreground",
256
391
  bgColor: "bg-muted",
257
392
  barColor: "bg-muted"
258
393
  },
259
394
  1: {
260
- message: messages2.risk_1,
395
+ message: messages3.risk_1,
261
396
  fallbackLabel: "Negligible",
262
397
  color: "text-green-700",
263
398
  bgColor: "bg-green-100",
264
399
  barColor: "bg-green-500"
265
400
  },
266
401
  2: {
267
- message: messages2.risk_2,
402
+ message: messages3.risk_2,
268
403
  fallbackLabel: "Low",
269
404
  color: "text-lime-700",
270
405
  bgColor: "bg-lime-100",
271
406
  barColor: "bg-lime-500"
272
407
  },
273
408
  3: {
274
- message: messages2.risk_3,
409
+ message: messages3.risk_3,
275
410
  fallbackLabel: "Medium",
276
411
  color: "text-yellow-700",
277
412
  bgColor: "bg-yellow-100",
278
413
  barColor: "bg-yellow-500"
279
414
  },
280
415
  4: {
281
- message: messages2.risk_4,
416
+ message: messages3.risk_4,
282
417
  fallbackLabel: "High",
283
418
  color: "text-orange-700",
284
419
  bgColor: "bg-orange-100",
285
420
  barColor: "bg-orange-500"
286
421
  },
287
422
  5: {
288
- message: messages2.risk_5,
423
+ message: messages3.risk_5,
289
424
  fallbackLabel: "Critical",
290
425
  color: "text-red-700",
291
426
  bgColor: "bg-red-100",
@@ -294,42 +429,42 @@ var riskScale = {
294
429
  };
295
430
  var processScale = {
296
431
  0: {
297
- message: messages2.process_0,
432
+ message: messages3.process_0,
298
433
  fallbackLabel: "Not rated",
299
434
  color: "text-muted-foreground",
300
435
  bgColor: "bg-muted",
301
436
  barColor: "bg-muted"
302
437
  },
303
438
  1: {
304
- message: messages2.process_1,
439
+ message: messages3.process_1,
305
440
  fallbackLabel: "Insignificant",
306
441
  color: "text-green-700",
307
442
  bgColor: "bg-green-100",
308
443
  barColor: "bg-green-500"
309
444
  },
310
445
  2: {
311
- message: messages2.process_2,
446
+ message: messages3.process_2,
312
447
  fallbackLabel: "Low",
313
448
  color: "text-lime-700",
314
449
  bgColor: "bg-lime-100",
315
450
  barColor: "bg-lime-500"
316
451
  },
317
452
  3: {
318
- message: messages2.process_3,
453
+ message: messages3.process_3,
319
454
  fallbackLabel: "Relevant",
320
455
  color: "text-yellow-700",
321
456
  bgColor: "bg-yellow-100",
322
457
  barColor: "bg-yellow-500"
323
458
  },
324
459
  4: {
325
- message: messages2.process_4,
460
+ message: messages3.process_4,
326
461
  fallbackLabel: "Important",
327
462
  color: "text-orange-700",
328
463
  bgColor: "bg-orange-100",
329
464
  barColor: "bg-orange-500"
330
465
  },
331
466
  5: {
332
- message: messages2.process_5,
467
+ message: messages3.process_5,
333
468
  fallbackLabel: "Vital",
334
469
  color: "text-red-700",
335
470
  bgColor: "bg-red-100",
@@ -338,42 +473,42 @@ var processScale = {
338
473
  };
339
474
  var assetScale = {
340
475
  0: {
341
- message: messages2.asset_0,
476
+ message: messages3.asset_0,
342
477
  fallbackLabel: "Not classified",
343
478
  color: "text-muted-foreground",
344
479
  bgColor: "bg-muted",
345
480
  barColor: "bg-muted"
346
481
  },
347
482
  1: {
348
- message: messages2.asset_1,
483
+ message: messages3.asset_1,
349
484
  fallbackLabel: "Insignificant",
350
485
  color: "text-green-700",
351
486
  bgColor: "bg-green-100",
352
487
  barColor: "bg-green-500"
353
488
  },
354
489
  2: {
355
- message: messages2.asset_2,
490
+ message: messages3.asset_2,
356
491
  fallbackLabel: "Low",
357
492
  color: "text-lime-700",
358
493
  bgColor: "bg-lime-100",
359
494
  barColor: "bg-lime-500"
360
495
  },
361
496
  3: {
362
- message: messages2.asset_3,
497
+ message: messages3.asset_3,
363
498
  fallbackLabel: "Medium",
364
499
  color: "text-yellow-700",
365
500
  bgColor: "bg-yellow-100",
366
501
  barColor: "bg-yellow-500"
367
502
  },
368
503
  4: {
369
- message: messages2.asset_4,
504
+ message: messages3.asset_4,
370
505
  fallbackLabel: "High",
371
506
  color: "text-orange-700",
372
507
  bgColor: "bg-orange-100",
373
508
  barColor: "bg-orange-500"
374
509
  },
375
510
  5: {
376
- message: messages2.asset_5,
511
+ message: messages3.asset_5,
377
512
  fallbackLabel: "Business Critical",
378
513
  color: "text-red-700",
379
514
  bgColor: "bg-red-100",
@@ -393,7 +528,7 @@ function getScale(preset) {
393
528
  }
394
529
 
395
530
  // src/common/impact/impact-card.tsx
396
- var import_jsx_runtime2 = require("react/jsx-runtime");
531
+ var import_jsx_runtime3 = require("react/jsx-runtime");
397
532
  function ImpactItemRow({
398
533
  label,
399
534
  shortLabel,
@@ -406,29 +541,29 @@ function ImpactItemRow({
406
541
  const config = scale[value];
407
542
  const isUnrated = value === 0;
408
543
  const percentage = isUnrated ? 0 : value / 5 * 100;
409
- const styles = (0, import_theme2.impactCard)({ unrated: isUnrated });
410
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.row(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: styles.rowContent(), children: [
411
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.rowIcon(), children: shortLabel }),
412
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: styles.rowBody(), children: [
413
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: styles.rowHeader(), children: [
414
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.rowLabel(), children: label }),
415
- isEditing ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
416
- import_sight2.Select,
544
+ const styles = (0, import_theme3.impactCard)({ unrated: isUnrated });
545
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: styles.row(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: styles.rowContent(), children: [
546
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.rowIcon(), children: shortLabel }),
547
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: styles.rowBody(), children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: styles.rowHeader(), children: [
549
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.rowLabel(), children: label }),
550
+ isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
551
+ import_sight3.Select,
417
552
  {
418
553
  value: String(value),
419
554
  onValueChange: (val) => onLevelChange(Number(val)),
420
555
  size: "sm",
421
556
  children: [
422
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Select.Trigger, { className: "w-36", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Select.Value, {}) }),
423
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Select.Content, { children: impactLevels.map((level) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Select.Item, { value: String(level), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "flex items-center gap-2", children: [
424
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs text-muted-foreground w-3", children: level }),
557
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Select.Trigger, { className: "w-36", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Select.Value, {}) }),
558
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Select.Content, { children: impactLevels.map((level) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Select.Item, { value: String(level), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "flex items-center gap-2", children: [
559
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-xs text-muted-foreground w-3", children: level }),
425
560
  formatLabel(level)
426
561
  ] }) }, level)) })
427
562
  ]
428
563
  }
429
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: styles.rowValue(), children: [
430
- !isUnrated && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.rowValueNumber(), children: value }),
431
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
564
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: styles.rowValue(), children: [
565
+ !isUnrated && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.rowValueNumber(), children: value }),
566
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
432
567
  "span",
433
568
  {
434
569
  className: styles.rowValueBadge({
@@ -439,7 +574,7 @@ function ImpactItemRow({
439
574
  )
440
575
  ] })
441
576
  ] }),
442
- !isUnrated && !isEditing && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.progressContainer(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
577
+ !isUnrated && !isEditing && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: styles.progressContainer(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
443
578
  "div",
444
579
  {
445
580
  className: styles.progressBar({ className: config.barColor }),
@@ -462,35 +597,38 @@ function ImpactCard({
462
597
  showAuthenticity = false,
463
598
  readOnly = false,
464
599
  scale = "risk",
465
- title
600
+ title,
601
+ variant = "card"
466
602
  }) {
467
603
  var _a, _b;
468
- const intl = (0, import_i18n4.useSafeIntl)();
604
+ const intl = (0, import_i18n6.useSafeIntl)();
605
+ const isInline = variant === "inline";
469
606
  const [isEditing, setIsEditing] = (0, import_react.useState)(false);
470
607
  const [editValues, setEditValues] = (0, import_react.useState)(
471
608
  value || defaultImpact
472
609
  );
473
- const styles = (0, import_theme2.impactCard)({ editing: isEditing });
610
+ const effectiveIsEditing = isInline ? !readOnly : isEditing;
611
+ const styles = (0, import_theme3.impactCard)({ editing: !isInline && isEditing });
474
612
  const scaleConfig = typeof scale === "string" ? getScale(scale) : scale;
475
613
  const formatLabel = (level) => {
476
614
  const config = scaleConfig[level];
477
615
  return intl.formatMessage(config.message);
478
616
  };
479
617
  const t = {
480
- titleCia: intl.formatMessage(messages2.title_cia),
481
- titleCiaa: intl.formatMessage(messages2.title_ciaa),
482
- confidentiality: intl.formatMessage(messages2.confidentiality),
483
- integrity: intl.formatMessage(messages2.integrity),
484
- availability: intl.formatMessage(messages2.availability),
485
- authenticity: intl.formatMessage(messages2.authenticity),
486
- justification: intl.formatMessage(messages2.justification),
618
+ titleCia: intl.formatMessage(messages3.title_cia),
619
+ titleCiaa: intl.formatMessage(messages3.title_ciaa),
620
+ confidentiality: intl.formatMessage(messages3.confidentiality),
621
+ integrity: intl.formatMessage(messages3.integrity),
622
+ availability: intl.formatMessage(messages3.availability),
623
+ authenticity: intl.formatMessage(messages3.authenticity),
624
+ justification: intl.formatMessage(messages3.justification),
487
625
  justificationPlaceholder: intl.formatMessage(
488
- messages2.justification_placeholder
626
+ messages3.justification_placeholder
489
627
  ),
490
- noJustification: intl.formatMessage(messages2.no_justification),
491
- edit: intl.formatMessage(messages2.edit),
492
- cancel: intl.formatMessage(messages2.cancel),
493
- save: intl.formatMessage(messages2.save)
628
+ noJustification: intl.formatMessage(messages3.no_justification),
629
+ edit: intl.formatMessage(messages3.edit),
630
+ cancel: intl.formatMessage(messages3.cancel),
631
+ save: intl.formatMessage(messages3.save)
494
632
  };
495
633
  const defaultTitle = showAuthenticity ? t.titleCiaa : t.titleCia;
496
634
  const cardTitle = title != null ? title : defaultTitle;
@@ -506,18 +644,28 @@ function ImpactCard({
506
644
  setEditValues(value || defaultImpact);
507
645
  setIsEditing(true);
508
646
  };
509
- const currentImpact = isEditing ? editValues : value || defaultImpact;
647
+ const currentImpact = isInline ? value || defaultImpact : isEditing ? editValues : value || defaultImpact;
510
648
  const handleLevelChange = (key) => (level) => {
511
- setEditValues((prev) => ({
512
- ...prev,
649
+ const newValues = {
650
+ ...isInline ? value || defaultImpact : editValues,
513
651
  [key]: level
514
- }));
652
+ };
653
+ if (isInline) {
654
+ onChange == null ? void 0 : onChange(newValues);
655
+ } else {
656
+ setEditValues(newValues);
657
+ }
515
658
  };
516
659
  const handleJustificationChange = (justification) => {
517
- setEditValues((prev) => ({
518
- ...prev,
660
+ const newValues = {
661
+ ...isInline ? value || defaultImpact : editValues,
519
662
  impactJustification: justification || void 0
520
- }));
663
+ };
664
+ if (isInline) {
665
+ onChange == null ? void 0 : onChange(newValues);
666
+ } else {
667
+ setEditValues(newValues);
668
+ }
521
669
  };
522
670
  const highestImpact = Math.max(
523
671
  currentImpact.impactConfidentiality,
@@ -526,115 +674,280 @@ function ImpactCard({
526
674
  (_a = currentImpact.impactAuthenticity) != null ? _a : 0
527
675
  );
528
676
  const highestLabel = formatLabel(highestImpact);
529
- const justificationHint = intl.formatMessage(messages2.justification_hint, {
677
+ const justificationHint = intl.formatMessage(messages3.justification_hint, {
530
678
  level: highestLabel
531
679
  });
532
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sight2.Card.Root, { className: styles.root(), children: [
533
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sight2.Card.Header, { className: "flex flex-row items-center justify-between", children: [
534
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
535
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Heading, { level: "h4", className: "text-sm font-medium", children: cardTitle }),
536
- isEditing && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Chip, { size: "sm", color: "primary", children: t.edit })
680
+ const impactRows = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
681
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
682
+ ImpactItemRow,
683
+ {
684
+ label: t.confidentiality,
685
+ shortLabel: "C",
686
+ value: currentImpact.impactConfidentiality,
687
+ isEditing: effectiveIsEditing,
688
+ scale: scaleConfig,
689
+ formatLabel,
690
+ onLevelChange: handleLevelChange("impactConfidentiality")
691
+ }
692
+ ),
693
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
694
+ ImpactItemRow,
695
+ {
696
+ label: t.integrity,
697
+ shortLabel: "I",
698
+ value: currentImpact.impactIntegrity,
699
+ isEditing: effectiveIsEditing,
700
+ scale: scaleConfig,
701
+ formatLabel,
702
+ onLevelChange: handleLevelChange("impactIntegrity")
703
+ }
704
+ ),
705
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
706
+ ImpactItemRow,
707
+ {
708
+ label: t.availability,
709
+ shortLabel: "A",
710
+ value: currentImpact.impactAvailability,
711
+ isEditing: effectiveIsEditing,
712
+ scale: scaleConfig,
713
+ formatLabel,
714
+ onLevelChange: handleLevelChange("impactAvailability")
715
+ }
716
+ ),
717
+ showAuthenticity && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
718
+ ImpactItemRow,
719
+ {
720
+ label: t.authenticity,
721
+ shortLabel: "Au",
722
+ value: (_b = currentImpact.impactAuthenticity) != null ? _b : 0,
723
+ isEditing: effectiveIsEditing,
724
+ scale: scaleConfig,
725
+ formatLabel,
726
+ onLevelChange: handleLevelChange("impactAuthenticity")
727
+ }
728
+ )
729
+ ] });
730
+ const justificationContent = showJustification && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: styles.justificationSection(), children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
732
+ "label",
733
+ {
734
+ htmlFor: "impact-justification",
735
+ className: styles.justificationLabel(),
736
+ children: [
737
+ t.justification,
738
+ highestImpact > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.justificationHint(), children: justificationHint })
739
+ ]
740
+ }
741
+ ),
742
+ effectiveIsEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
743
+ import_sight3.Textarea,
744
+ {
745
+ id: "impact-justification",
746
+ value: currentImpact.impactJustification || "",
747
+ onChange: (e) => handleJustificationChange(e.target.value),
748
+ placeholder: t.justificationPlaceholder,
749
+ rows: 3,
750
+ className: "text-sm"
751
+ }
752
+ ) : currentImpact.impactJustification ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: styles.justificationText(), children: currentImpact.impactJustification }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: styles.justificationEmpty(), children: t.noJustification })
753
+ ] });
754
+ if (isInline) {
755
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: styles.wrapper(), children: [
756
+ title && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: styles.inlineHeader(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Heading, { level: "h4", className: "text-sm font-medium", children: title }) }),
757
+ impactRows,
758
+ justificationContent
759
+ ] });
760
+ }
761
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sight3.Card.Root, { className: styles.root(), children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sight3.Card.Header, { className: "flex flex-row items-center justify-between", children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", children: [
764
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Heading, { level: "h4", className: "text-sm font-medium", children: cardTitle }),
765
+ isEditing && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Chip, { size: "sm", color: "primary", children: t.edit })
537
766
  ] }),
538
- !readOnly && (!isEditing ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
767
+ !readOnly && (!isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
539
768
  "button",
540
769
  {
541
770
  type: "button",
542
771
  onClick: handleStartEdit,
543
772
  className: styles.editButton(),
544
773
  "aria-label": t.edit,
545
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons2.EditIcon, { className: "size-4" })
774
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons2.EditIcon, { className: "size-4" })
546
775
  }
547
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
548
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Button, { variant: "ghost", size: "sm", onClick: handleCancel, children: t.cancel }),
549
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_sight2.Button, { size: "sm", onClick: handleSave, children: t.save })
776
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", children: [
777
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Button, { variant: "ghost", size: "sm", onClick: handleCancel, children: t.cancel }),
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sight3.Button, { size: "sm", onClick: handleSave, children: t.save })
550
779
  ] }))
551
780
  ] }),
552
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_sight2.Card.Body, { className: "space-y-3", children: [
553
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
554
- ImpactItemRow,
555
- {
556
- label: t.confidentiality,
557
- shortLabel: "C",
558
- value: currentImpact.impactConfidentiality,
559
- isEditing,
560
- scale: scaleConfig,
561
- formatLabel,
562
- onLevelChange: handleLevelChange("impactConfidentiality")
563
- }
564
- ),
565
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
566
- ImpactItemRow,
567
- {
568
- label: t.integrity,
569
- shortLabel: "I",
570
- value: currentImpact.impactIntegrity,
571
- isEditing,
572
- scale: scaleConfig,
573
- formatLabel,
574
- onLevelChange: handleLevelChange("impactIntegrity")
575
- }
576
- ),
577
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
578
- ImpactItemRow,
579
- {
580
- label: t.availability,
581
- shortLabel: "A",
582
- value: currentImpact.impactAvailability,
583
- isEditing,
584
- scale: scaleConfig,
585
- formatLabel,
586
- onLevelChange: handleLevelChange("impactAvailability")
587
- }
588
- ),
589
- showAuthenticity && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
590
- ImpactItemRow,
591
- {
592
- label: t.authenticity,
593
- shortLabel: "Au",
594
- value: (_b = currentImpact.impactAuthenticity) != null ? _b : 0,
595
- isEditing,
596
- scale: scaleConfig,
597
- formatLabel,
598
- onLevelChange: handleLevelChange("impactAuthenticity")
599
- }
600
- ),
601
- showJustification && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: styles.justificationSection(), children: [
602
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
603
- "label",
604
- {
605
- htmlFor: "impact-justification",
606
- className: styles.justificationLabel(),
607
- children: [
608
- t.justification,
609
- highestImpact > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.justificationHint(), children: justificationHint })
610
- ]
611
- }
612
- ),
613
- isEditing ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
614
- import_sight2.Textarea,
615
- {
616
- id: "impact-justification",
617
- value: currentImpact.impactJustification || "",
618
- onChange: (e) => handleJustificationChange(e.target.value),
619
- placeholder: t.justificationPlaceholder,
620
- rows: 3,
621
- className: "text-sm"
622
- }
623
- ) : currentImpact.impactJustification ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: styles.justificationText(), children: currentImpact.impactJustification }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: styles.justificationEmpty(), children: t.noJustification })
624
- ] })
781
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sight3.Card.Body, { className: "space-y-3", children: [
782
+ impactRows,
783
+ justificationContent
625
784
  ] })
626
785
  ] });
627
786
  }
787
+
788
+ // src/common/risk/messages.ts
789
+ var import_i18n7 = require("@kopexa/i18n");
790
+ var messages4 = (0, import_i18n7.defineMessages)({
791
+ level_unrated: {
792
+ id: "grc.risk.level.unrated",
793
+ defaultMessage: "Not rated"
794
+ },
795
+ level_low: {
796
+ id: "grc.risk.level.low",
797
+ defaultMessage: "Low"
798
+ },
799
+ level_medium: {
800
+ id: "grc.risk.level.medium",
801
+ defaultMessage: "Medium"
802
+ },
803
+ level_high: {
804
+ id: "grc.risk.level.high",
805
+ defaultMessage: "High"
806
+ },
807
+ level_critical: {
808
+ id: "grc.risk.level.critical",
809
+ defaultMessage: "Critical"
810
+ },
811
+ likelihood: {
812
+ id: "grc.risk.likelihood",
813
+ defaultMessage: "Likelihood"
814
+ },
815
+ consequence: {
816
+ id: "grc.risk.consequence",
817
+ defaultMessage: "Consequence"
818
+ },
819
+ rating: {
820
+ id: "grc.risk.rating",
821
+ defaultMessage: "Rating"
822
+ },
823
+ not_rated_hint: {
824
+ id: "grc.risk.not_rated_hint",
825
+ defaultMessage: "Risk has not been assessed yet"
826
+ }
827
+ });
828
+
829
+ // src/common/risk/risk-rating-display.tsx
830
+ var import_i18n8 = require("@kopexa/i18n");
831
+ var import_sight4 = require("@kopexa/sight");
832
+ var import_theme4 = require("@kopexa/theme");
833
+
834
+ // src/common/risk/types.ts
835
+ function getRiskLevelFromRating(rating) {
836
+ if (rating === 0) return "unrated";
837
+ if (rating <= 4) return "low";
838
+ if (rating <= 9) return "medium";
839
+ if (rating <= 16) return "high";
840
+ return "critical";
841
+ }
842
+ function isRatingUnrated(rating) {
843
+ if (!rating) return true;
844
+ return rating.rating === 0 || rating.likelihood === 0 || rating.consequence === 0;
845
+ }
846
+ var riskLevelConfig = {
847
+ unrated: {
848
+ label: "Nicht bewertet",
849
+ bgColor: "bg-muted",
850
+ textColor: "text-muted-foreground",
851
+ iconBgColor: "bg-muted/50",
852
+ iconTextColor: "text-muted-foreground"
853
+ },
854
+ low: {
855
+ label: "Niedrig",
856
+ bgColor: "bg-success",
857
+ textColor: "text-success",
858
+ iconBgColor: "bg-success/10",
859
+ iconTextColor: "text-success"
860
+ },
861
+ medium: {
862
+ label: "Mittel",
863
+ bgColor: "bg-warning",
864
+ textColor: "text-warning",
865
+ iconBgColor: "bg-warning/10",
866
+ iconTextColor: "text-warning"
867
+ },
868
+ high: {
869
+ label: "Hoch",
870
+ bgColor: "bg-orange-500",
871
+ textColor: "text-orange-500",
872
+ iconBgColor: "bg-orange-500/10",
873
+ iconTextColor: "text-orange-500"
874
+ },
875
+ critical: {
876
+ label: "Kritisch",
877
+ bgColor: "bg-destructive",
878
+ textColor: "text-destructive",
879
+ iconBgColor: "bg-destructive/10",
880
+ iconTextColor: "text-destructive"
881
+ }
882
+ };
883
+
884
+ // src/common/risk/risk-rating-display.tsx
885
+ var import_jsx_runtime4 = require("react/jsx-runtime");
886
+ function RiskRatingDisplay({
887
+ rating,
888
+ showBadge = true,
889
+ showLabel = true,
890
+ size = "md"
891
+ }) {
892
+ const intl = (0, import_i18n8.useSafeIntl)();
893
+ if (isRatingUnrated(rating)) {
894
+ const styles2 = (0, import_theme4.riskRating)({ size, level: "unrated" });
895
+ const levelLabel2 = intl.formatMessage(messages4.level_unrated);
896
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_sight4.Tooltip, { content: intl.formatMessage(messages4.not_rated_hint), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles2.base(), children: [
897
+ showBadge && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles2.badge(), children: "\u2014" }),
898
+ showLabel && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles2.label(), children: levelLabel2 })
899
+ ] }) });
900
+ }
901
+ const ratedValue = rating;
902
+ const level = getRiskLevelFromRating(ratedValue.rating);
903
+ const styles = (0, import_theme4.riskRating)({
904
+ size,
905
+ level
906
+ });
907
+ const levelLabelKey = `level_${level}`;
908
+ const levelLabel = intl.formatMessage(messages4[levelLabelKey]);
909
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
910
+ import_sight4.Tooltip,
911
+ {
912
+ content: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "text-xs space-y-1", children: [
913
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
914
+ intl.formatMessage(messages4.likelihood),
915
+ ": ",
916
+ ratedValue.likelihood,
917
+ "/5"
918
+ ] }),
919
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
920
+ intl.formatMessage(messages4.consequence),
921
+ ": ",
922
+ ratedValue.consequence,
923
+ "/5"
924
+ ] }),
925
+ ratedValue.comment && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-muted-foreground", children: ratedValue.comment })
926
+ ] }),
927
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: styles.base(), children: [
928
+ showBadge && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: styles.badge(), children: ratedValue.rating }),
929
+ showLabel && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: styles.label(), children: levelLabel })
930
+ ] })
931
+ }
932
+ );
933
+ }
628
934
  // Annotate the CommonJS export names for ESM import in node:
629
935
  0 && (module.exports = {
630
936
  ComplianceBadges,
937
+ ControlChip,
631
938
  DoraBadge,
632
939
  ImpactCard,
940
+ MappedControls,
633
941
  Nis2Badge,
942
+ RiskRatingDisplay,
634
943
  assetScale,
944
+ getRiskLevelFromRating,
635
945
  getScale,
636
946
  impactLevels,
637
947
  impactMessages,
948
+ isRatingUnrated,
638
949
  processScale,
950
+ riskLevelConfig,
951
+ riskMessages,
639
952
  riskScale
640
953
  });