@kopexa/grc 0.0.3 → 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.
- package/dist/{chunk-TW3S4OE2.mjs → chunk-AGASJJ7X.mjs} +106 -82
- package/dist/common/impact/impact-card.d.mts +7 -1
- package/dist/common/impact/impact-card.d.ts +7 -1
- package/dist/common/impact/impact-card.js +105 -81
- package/dist/common/impact/impact-card.mjs +1 -1
- package/dist/common/impact/index.js +105 -81
- package/dist/common/impact/index.mjs +1 -1
- package/dist/common/index.js +105 -81
- package/dist/common/index.mjs +1 -1
- package/dist/index.js +105 -81
- package/dist/index.mjs +1 -1
- package/package.json +7 -7
- package/src/common/impact/impact-card.tsx +132 -79
package/dist/index.js
CHANGED
|
@@ -597,15 +597,18 @@ function ImpactCard({
|
|
|
597
597
|
showAuthenticity = false,
|
|
598
598
|
readOnly = false,
|
|
599
599
|
scale = "risk",
|
|
600
|
-
title
|
|
600
|
+
title,
|
|
601
|
+
variant = "card"
|
|
601
602
|
}) {
|
|
602
603
|
var _a, _b;
|
|
603
604
|
const intl = (0, import_i18n6.useSafeIntl)();
|
|
605
|
+
const isInline = variant === "inline";
|
|
604
606
|
const [isEditing, setIsEditing] = (0, import_react.useState)(false);
|
|
605
607
|
const [editValues, setEditValues] = (0, import_react.useState)(
|
|
606
608
|
value || defaultImpact
|
|
607
609
|
);
|
|
608
|
-
const
|
|
610
|
+
const effectiveIsEditing = isInline ? !readOnly : isEditing;
|
|
611
|
+
const styles = (0, import_theme3.impactCard)({ editing: !isInline && isEditing });
|
|
609
612
|
const scaleConfig = typeof scale === "string" ? getScale(scale) : scale;
|
|
610
613
|
const formatLabel = (level) => {
|
|
611
614
|
const config = scaleConfig[level];
|
|
@@ -641,18 +644,28 @@ function ImpactCard({
|
|
|
641
644
|
setEditValues(value || defaultImpact);
|
|
642
645
|
setIsEditing(true);
|
|
643
646
|
};
|
|
644
|
-
const currentImpact = isEditing ? editValues : value || defaultImpact;
|
|
647
|
+
const currentImpact = isInline ? value || defaultImpact : isEditing ? editValues : value || defaultImpact;
|
|
645
648
|
const handleLevelChange = (key) => (level) => {
|
|
646
|
-
|
|
647
|
-
...
|
|
649
|
+
const newValues = {
|
|
650
|
+
...isInline ? value || defaultImpact : editValues,
|
|
648
651
|
[key]: level
|
|
649
|
-
}
|
|
652
|
+
};
|
|
653
|
+
if (isInline) {
|
|
654
|
+
onChange == null ? void 0 : onChange(newValues);
|
|
655
|
+
} else {
|
|
656
|
+
setEditValues(newValues);
|
|
657
|
+
}
|
|
650
658
|
};
|
|
651
659
|
const handleJustificationChange = (justification) => {
|
|
652
|
-
|
|
653
|
-
...
|
|
660
|
+
const newValues = {
|
|
661
|
+
...isInline ? value || defaultImpact : editValues,
|
|
654
662
|
impactJustification: justification || void 0
|
|
655
|
-
}
|
|
663
|
+
};
|
|
664
|
+
if (isInline) {
|
|
665
|
+
onChange == null ? void 0 : onChange(newValues);
|
|
666
|
+
} else {
|
|
667
|
+
setEditValues(newValues);
|
|
668
|
+
}
|
|
656
669
|
};
|
|
657
670
|
const highestImpact = Math.max(
|
|
658
671
|
currentImpact.impactConfidentiality,
|
|
@@ -664,6 +677,87 @@ function ImpactCard({
|
|
|
664
677
|
const justificationHint = intl.formatMessage(messages3.justification_hint, {
|
|
665
678
|
level: highestLabel
|
|
666
679
|
});
|
|
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
|
+
}
|
|
667
761
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sight3.Card.Root, { className: styles.root(), children: [
|
|
668
762
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sight3.Card.Header, { className: "flex flex-row items-center justify-between", children: [
|
|
669
763
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
@@ -685,78 +779,8 @@ function ImpactCard({
|
|
|
685
779
|
] }))
|
|
686
780
|
] }),
|
|
687
781
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_sight3.Card.Body, { className: "space-y-3", children: [
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
{
|
|
691
|
-
label: t.confidentiality,
|
|
692
|
-
shortLabel: "C",
|
|
693
|
-
value: currentImpact.impactConfidentiality,
|
|
694
|
-
isEditing,
|
|
695
|
-
scale: scaleConfig,
|
|
696
|
-
formatLabel,
|
|
697
|
-
onLevelChange: handleLevelChange("impactConfidentiality")
|
|
698
|
-
}
|
|
699
|
-
),
|
|
700
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
701
|
-
ImpactItemRow,
|
|
702
|
-
{
|
|
703
|
-
label: t.integrity,
|
|
704
|
-
shortLabel: "I",
|
|
705
|
-
value: currentImpact.impactIntegrity,
|
|
706
|
-
isEditing,
|
|
707
|
-
scale: scaleConfig,
|
|
708
|
-
formatLabel,
|
|
709
|
-
onLevelChange: handleLevelChange("impactIntegrity")
|
|
710
|
-
}
|
|
711
|
-
),
|
|
712
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
713
|
-
ImpactItemRow,
|
|
714
|
-
{
|
|
715
|
-
label: t.availability,
|
|
716
|
-
shortLabel: "A",
|
|
717
|
-
value: currentImpact.impactAvailability,
|
|
718
|
-
isEditing,
|
|
719
|
-
scale: scaleConfig,
|
|
720
|
-
formatLabel,
|
|
721
|
-
onLevelChange: handleLevelChange("impactAvailability")
|
|
722
|
-
}
|
|
723
|
-
),
|
|
724
|
-
showAuthenticity && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
725
|
-
ImpactItemRow,
|
|
726
|
-
{
|
|
727
|
-
label: t.authenticity,
|
|
728
|
-
shortLabel: "Au",
|
|
729
|
-
value: (_b = currentImpact.impactAuthenticity) != null ? _b : 0,
|
|
730
|
-
isEditing,
|
|
731
|
-
scale: scaleConfig,
|
|
732
|
-
formatLabel,
|
|
733
|
-
onLevelChange: handleLevelChange("impactAuthenticity")
|
|
734
|
-
}
|
|
735
|
-
),
|
|
736
|
-
showJustification && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: styles.justificationSection(), children: [
|
|
737
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
738
|
-
"label",
|
|
739
|
-
{
|
|
740
|
-
htmlFor: "impact-justification",
|
|
741
|
-
className: styles.justificationLabel(),
|
|
742
|
-
children: [
|
|
743
|
-
t.justification,
|
|
744
|
-
highestImpact > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.justificationHint(), children: justificationHint })
|
|
745
|
-
]
|
|
746
|
-
}
|
|
747
|
-
),
|
|
748
|
-
isEditing ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
749
|
-
import_sight3.Textarea,
|
|
750
|
-
{
|
|
751
|
-
id: "impact-justification",
|
|
752
|
-
value: currentImpact.impactJustification || "",
|
|
753
|
-
onChange: (e) => handleJustificationChange(e.target.value),
|
|
754
|
-
placeholder: t.justificationPlaceholder,
|
|
755
|
-
rows: 3,
|
|
756
|
-
className: "text-sm"
|
|
757
|
-
}
|
|
758
|
-
) : 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 })
|
|
759
|
-
] })
|
|
782
|
+
impactRows,
|
|
783
|
+
justificationContent
|
|
760
784
|
] })
|
|
761
785
|
] });
|
|
762
786
|
}
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kopexa/grc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "GRC (Governance, Risk, Compliance) components for Kopexa",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -42,15 +42,15 @@
|
|
|
42
42
|
"react": ">=19.0.0-rc.0",
|
|
43
43
|
"react-dom": ">=19.0.0-rc.0",
|
|
44
44
|
"react-intl": "^7.1.14",
|
|
45
|
-
"@kopexa/theme": "17.
|
|
46
|
-
"@kopexa/sight": "17.
|
|
45
|
+
"@kopexa/theme": "17.8.0",
|
|
46
|
+
"@kopexa/sight": "17.2.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@kopexa/krn": "^1.0.1",
|
|
50
|
-
"@kopexa/react-utils": "17.0.
|
|
51
|
-
"@kopexa/
|
|
52
|
-
"@kopexa/
|
|
53
|
-
"@kopexa/
|
|
50
|
+
"@kopexa/react-utils": "17.0.14",
|
|
51
|
+
"@kopexa/shared-utils": "17.0.14",
|
|
52
|
+
"@kopexa/icons": "17.2.2",
|
|
53
|
+
"@kopexa/i18n": "17.3.0"
|
|
54
54
|
},
|
|
55
55
|
"clean-package": "../../clean-package.config.json",
|
|
56
56
|
"publishConfig": {
|
|
@@ -145,6 +145,12 @@ export interface ImpactCardProps {
|
|
|
145
145
|
scale?: ImpactScalePreset | ImpactScaleConfig;
|
|
146
146
|
/** Custom title for the card */
|
|
147
147
|
title?: string;
|
|
148
|
+
/**
|
|
149
|
+
* Display variant:
|
|
150
|
+
* - `card`: Wrapped in a Card with edit/save/cancel buttons (default)
|
|
151
|
+
* - `inline`: No card wrapper, always editable, changes propagate immediately
|
|
152
|
+
*/
|
|
153
|
+
variant?: "card" | "inline";
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
const defaultImpact: ImpactValue = {
|
|
@@ -162,14 +168,19 @@ export function ImpactCard({
|
|
|
162
168
|
readOnly = false,
|
|
163
169
|
scale = "risk",
|
|
164
170
|
title,
|
|
171
|
+
variant = "card",
|
|
165
172
|
}: ImpactCardProps) {
|
|
166
173
|
const intl = useSafeIntl();
|
|
174
|
+
const isInline = variant === "inline";
|
|
167
175
|
const [isEditing, setIsEditing] = useState(false);
|
|
168
176
|
const [editValues, setEditValues] = useState<ImpactValue>(
|
|
169
177
|
value || defaultImpact,
|
|
170
178
|
);
|
|
171
179
|
|
|
172
|
-
|
|
180
|
+
// For inline variant, always show as editing (form mode)
|
|
181
|
+
const effectiveIsEditing = isInline ? !readOnly : isEditing;
|
|
182
|
+
|
|
183
|
+
const styles = impactCard({ editing: !isInline && isEditing });
|
|
173
184
|
|
|
174
185
|
// Resolve scale config
|
|
175
186
|
const scaleConfig: ImpactScaleConfig =
|
|
@@ -218,7 +229,12 @@ export function ImpactCard({
|
|
|
218
229
|
setIsEditing(true);
|
|
219
230
|
};
|
|
220
231
|
|
|
221
|
-
|
|
232
|
+
// In inline mode, always use value; in card mode, use editValues when editing
|
|
233
|
+
const currentImpact = isInline
|
|
234
|
+
? value || defaultImpact
|
|
235
|
+
: isEditing
|
|
236
|
+
? editValues
|
|
237
|
+
: value || defaultImpact;
|
|
222
238
|
|
|
223
239
|
const handleLevelChange =
|
|
224
240
|
(
|
|
@@ -229,17 +245,29 @@ export function ImpactCard({
|
|
|
229
245
|
| "impactAuthenticity",
|
|
230
246
|
) =>
|
|
231
247
|
(level: ImpactLevel) => {
|
|
232
|
-
|
|
233
|
-
...
|
|
248
|
+
const newValues = {
|
|
249
|
+
...(isInline ? value || defaultImpact : editValues),
|
|
234
250
|
[key]: level,
|
|
235
|
-
}
|
|
251
|
+
};
|
|
252
|
+
if (isInline) {
|
|
253
|
+
// Inline mode: propagate changes immediately
|
|
254
|
+
onChange?.(newValues);
|
|
255
|
+
} else {
|
|
256
|
+
setEditValues(newValues);
|
|
257
|
+
}
|
|
236
258
|
};
|
|
237
259
|
|
|
238
260
|
const handleJustificationChange = (justification: string) => {
|
|
239
|
-
|
|
240
|
-
...
|
|
261
|
+
const newValues = {
|
|
262
|
+
...(isInline ? value || defaultImpact : editValues),
|
|
241
263
|
impactJustification: justification || undefined,
|
|
242
|
-
}
|
|
264
|
+
};
|
|
265
|
+
if (isInline) {
|
|
266
|
+
// Inline mode: propagate changes immediately
|
|
267
|
+
onChange?.(newValues);
|
|
268
|
+
} else {
|
|
269
|
+
setEditValues(newValues);
|
|
270
|
+
}
|
|
243
271
|
};
|
|
244
272
|
|
|
245
273
|
// Calculate highest impact for justification hint
|
|
@@ -255,6 +283,100 @@ export function ImpactCard({
|
|
|
255
283
|
level: highestLabel,
|
|
256
284
|
});
|
|
257
285
|
|
|
286
|
+
// Shared content for both variants
|
|
287
|
+
const impactRows = (
|
|
288
|
+
<>
|
|
289
|
+
<ImpactItemRow
|
|
290
|
+
label={t.confidentiality}
|
|
291
|
+
shortLabel="C"
|
|
292
|
+
value={currentImpact.impactConfidentiality}
|
|
293
|
+
isEditing={effectiveIsEditing}
|
|
294
|
+
scale={scaleConfig}
|
|
295
|
+
formatLabel={formatLabel}
|
|
296
|
+
onLevelChange={handleLevelChange("impactConfidentiality")}
|
|
297
|
+
/>
|
|
298
|
+
<ImpactItemRow
|
|
299
|
+
label={t.integrity}
|
|
300
|
+
shortLabel="I"
|
|
301
|
+
value={currentImpact.impactIntegrity}
|
|
302
|
+
isEditing={effectiveIsEditing}
|
|
303
|
+
scale={scaleConfig}
|
|
304
|
+
formatLabel={formatLabel}
|
|
305
|
+
onLevelChange={handleLevelChange("impactIntegrity")}
|
|
306
|
+
/>
|
|
307
|
+
<ImpactItemRow
|
|
308
|
+
label={t.availability}
|
|
309
|
+
shortLabel="A"
|
|
310
|
+
value={currentImpact.impactAvailability}
|
|
311
|
+
isEditing={effectiveIsEditing}
|
|
312
|
+
scale={scaleConfig}
|
|
313
|
+
formatLabel={formatLabel}
|
|
314
|
+
onLevelChange={handleLevelChange("impactAvailability")}
|
|
315
|
+
/>
|
|
316
|
+
{showAuthenticity && (
|
|
317
|
+
<ImpactItemRow
|
|
318
|
+
label={t.authenticity}
|
|
319
|
+
shortLabel="Au"
|
|
320
|
+
value={currentImpact.impactAuthenticity ?? 0}
|
|
321
|
+
isEditing={effectiveIsEditing}
|
|
322
|
+
scale={scaleConfig}
|
|
323
|
+
formatLabel={formatLabel}
|
|
324
|
+
onLevelChange={handleLevelChange("impactAuthenticity")}
|
|
325
|
+
/>
|
|
326
|
+
)}
|
|
327
|
+
</>
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const justificationContent = showJustification && (
|
|
331
|
+
<div className={styles.justificationSection()}>
|
|
332
|
+
<label
|
|
333
|
+
htmlFor="impact-justification"
|
|
334
|
+
className={styles.justificationLabel()}
|
|
335
|
+
>
|
|
336
|
+
{t.justification}
|
|
337
|
+
{highestImpact > 0 && (
|
|
338
|
+
<span className={styles.justificationHint()}>
|
|
339
|
+
{justificationHint}
|
|
340
|
+
</span>
|
|
341
|
+
)}
|
|
342
|
+
</label>
|
|
343
|
+
{effectiveIsEditing ? (
|
|
344
|
+
<Textarea
|
|
345
|
+
id="impact-justification"
|
|
346
|
+
value={currentImpact.impactJustification || ""}
|
|
347
|
+
onChange={(e) => handleJustificationChange(e.target.value)}
|
|
348
|
+
placeholder={t.justificationPlaceholder}
|
|
349
|
+
rows={3}
|
|
350
|
+
className="text-sm"
|
|
351
|
+
/>
|
|
352
|
+
) : currentImpact.impactJustification ? (
|
|
353
|
+
<p className={styles.justificationText()}>
|
|
354
|
+
{currentImpact.impactJustification}
|
|
355
|
+
</p>
|
|
356
|
+
) : (
|
|
357
|
+
<p className={styles.justificationEmpty()}>{t.noJustification}</p>
|
|
358
|
+
)}
|
|
359
|
+
</div>
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
// Inline variant: no card wrapper, always editable
|
|
363
|
+
if (isInline) {
|
|
364
|
+
return (
|
|
365
|
+
<div className={styles.wrapper()}>
|
|
366
|
+
{title && (
|
|
367
|
+
<div className={styles.inlineHeader()}>
|
|
368
|
+
<Heading level="h4" className="text-sm font-medium">
|
|
369
|
+
{title}
|
|
370
|
+
</Heading>
|
|
371
|
+
</div>
|
|
372
|
+
)}
|
|
373
|
+
{impactRows}
|
|
374
|
+
{justificationContent}
|
|
375
|
+
</div>
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Card variant: wrapped in Card with edit/save/cancel
|
|
258
380
|
return (
|
|
259
381
|
<Card.Root className={styles.root()}>
|
|
260
382
|
<Card.Header className="flex flex-row items-center justify-between">
|
|
@@ -290,77 +412,8 @@ export function ImpactCard({
|
|
|
290
412
|
))}
|
|
291
413
|
</Card.Header>
|
|
292
414
|
<Card.Body className="space-y-3">
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
shortLabel="C"
|
|
296
|
-
value={currentImpact.impactConfidentiality}
|
|
297
|
-
isEditing={isEditing}
|
|
298
|
-
scale={scaleConfig}
|
|
299
|
-
formatLabel={formatLabel}
|
|
300
|
-
onLevelChange={handleLevelChange("impactConfidentiality")}
|
|
301
|
-
/>
|
|
302
|
-
<ImpactItemRow
|
|
303
|
-
label={t.integrity}
|
|
304
|
-
shortLabel="I"
|
|
305
|
-
value={currentImpact.impactIntegrity}
|
|
306
|
-
isEditing={isEditing}
|
|
307
|
-
scale={scaleConfig}
|
|
308
|
-
formatLabel={formatLabel}
|
|
309
|
-
onLevelChange={handleLevelChange("impactIntegrity")}
|
|
310
|
-
/>
|
|
311
|
-
<ImpactItemRow
|
|
312
|
-
label={t.availability}
|
|
313
|
-
shortLabel="A"
|
|
314
|
-
value={currentImpact.impactAvailability}
|
|
315
|
-
isEditing={isEditing}
|
|
316
|
-
scale={scaleConfig}
|
|
317
|
-
formatLabel={formatLabel}
|
|
318
|
-
onLevelChange={handleLevelChange("impactAvailability")}
|
|
319
|
-
/>
|
|
320
|
-
{showAuthenticity && (
|
|
321
|
-
<ImpactItemRow
|
|
322
|
-
label={t.authenticity}
|
|
323
|
-
shortLabel="Au"
|
|
324
|
-
value={currentImpact.impactAuthenticity ?? 0}
|
|
325
|
-
isEditing={isEditing}
|
|
326
|
-
scale={scaleConfig}
|
|
327
|
-
formatLabel={formatLabel}
|
|
328
|
-
onLevelChange={handleLevelChange("impactAuthenticity")}
|
|
329
|
-
/>
|
|
330
|
-
)}
|
|
331
|
-
|
|
332
|
-
{/* Justification - single field for all dimensions */}
|
|
333
|
-
{showJustification && (
|
|
334
|
-
<div className={styles.justificationSection()}>
|
|
335
|
-
<label
|
|
336
|
-
htmlFor="impact-justification"
|
|
337
|
-
className={styles.justificationLabel()}
|
|
338
|
-
>
|
|
339
|
-
{t.justification}
|
|
340
|
-
{highestImpact > 0 && (
|
|
341
|
-
<span className={styles.justificationHint()}>
|
|
342
|
-
{justificationHint}
|
|
343
|
-
</span>
|
|
344
|
-
)}
|
|
345
|
-
</label>
|
|
346
|
-
{isEditing ? (
|
|
347
|
-
<Textarea
|
|
348
|
-
id="impact-justification"
|
|
349
|
-
value={currentImpact.impactJustification || ""}
|
|
350
|
-
onChange={(e) => handleJustificationChange(e.target.value)}
|
|
351
|
-
placeholder={t.justificationPlaceholder}
|
|
352
|
-
rows={3}
|
|
353
|
-
className="text-sm"
|
|
354
|
-
/>
|
|
355
|
-
) : currentImpact.impactJustification ? (
|
|
356
|
-
<p className={styles.justificationText()}>
|
|
357
|
-
{currentImpact.impactJustification}
|
|
358
|
-
</p>
|
|
359
|
-
) : (
|
|
360
|
-
<p className={styles.justificationEmpty()}>{t.noJustification}</p>
|
|
361
|
-
)}
|
|
362
|
-
</div>
|
|
363
|
-
)}
|
|
415
|
+
{impactRows}
|
|
416
|
+
{justificationContent}
|
|
364
417
|
</Card.Body>
|
|
365
418
|
</Card.Root>
|
|
366
419
|
);
|