Feinschliff Risiko/Bewertung: Ampelfarben, Titel, Skala, Legende
- Detail-Popups einheitlich: Name/Bezeichner steht immer im Titel (Risiko "R-001 · <Name>", Maßnahme "M-001 · <Name>", Prozess <Name>) - Bewertungsbalken (Schutzbedarf/Schadenshöhe) + Score-Pillen als einheitliche Ampel: 1 grün, 2 gelb, 3 orange, 4 rot; Risiko-Pillen folgen exakt der Heatmap-Skala (medium = gelb statt blau, elevated = orange) — Restrisiko-Pille jetzt korrekt gelb - Risiko-Detailkarte: Farbskala von der Mitte nach oben verschoben; alle Detail-/Bearbeiten-Popups etwas breiter (max-w-4xl) - CiaGauge-Beschriftung vereinheitlicht: VER/INT/VFB erscheint einmal pro Tabelle als Spaltenüberschrift (Asset-Liste + Popup-Tabellen), keine Labels mehr pro Zeile; Standalone-Gauges in Detailkarten behalten die Labels Verifiziert: alle vier Ampelfarben im DOM, Restrisiko-Pille gelb, Legende in Tabellenköpfen, Titel mit Name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
430aaab024
commit
1b13af46db
+3
-3
@@ -163,7 +163,7 @@
|
||||
"biaReport": "BIA-Report (PDF)",
|
||||
"critTable": "Kritikalität der Prozesse",
|
||||
"process": "Geschäftsprozess",
|
||||
"detailHeading": "Prozess-Detail · {name}",
|
||||
"detailHeading": "{name}",
|
||||
"detailSub": "Zugeordnete Assets nach Rolle",
|
||||
"critLabel": "Kritikalität: {label}",
|
||||
"primaryPill": "★ Primäres Asset",
|
||||
@@ -217,7 +217,7 @@
|
||||
"status": "Status",
|
||||
"owner": "Owner",
|
||||
"empty": "Noch keine Risiken erfasst.",
|
||||
"detailHeading": "Risiko-Detail · {ref}",
|
||||
"detailHeading": "{ref} · {name}",
|
||||
"detailSub": "Bewertung, betroffene Assets & Behandlung",
|
||||
"editTitle": "Risiko bearbeiten",
|
||||
"createTitle": "Risiko anlegen",
|
||||
@@ -291,7 +291,7 @@
|
||||
"newMeasure": "Maßnahme",
|
||||
"board": "Maßnahmen-Board",
|
||||
"empty": "Keine Maßnahmen.",
|
||||
"detailHeading": "Maßnahme · {ref}",
|
||||
"detailHeading": "{ref} · {name}",
|
||||
"detailSub": "Status, Verantwortung & verknüpfte Risiken",
|
||||
"editTitle": "Maßnahme bearbeiten",
|
||||
"createTitle": "Maßnahme anlegen",
|
||||
|
||||
+3
-3
@@ -163,7 +163,7 @@
|
||||
"biaReport": "BIA report (PDF)",
|
||||
"critTable": "Process criticality",
|
||||
"process": "Business process",
|
||||
"detailHeading": "Process detail · {name}",
|
||||
"detailHeading": "{name}",
|
||||
"detailSub": "Assigned assets by role",
|
||||
"critLabel": "Criticality: {label}",
|
||||
"primaryPill": "★ Primary asset",
|
||||
@@ -217,7 +217,7 @@
|
||||
"status": "Status",
|
||||
"owner": "Owner",
|
||||
"empty": "No risks recorded yet.",
|
||||
"detailHeading": "Risk detail · {ref}",
|
||||
"detailHeading": "{ref} · {name}",
|
||||
"detailSub": "Rating, affected assets & treatment",
|
||||
"editTitle": "Edit risk",
|
||||
"createTitle": "Create risk",
|
||||
@@ -291,7 +291,7 @@
|
||||
"newMeasure": "Measure",
|
||||
"board": "Measures board",
|
||||
"empty": "No measures.",
|
||||
"detailHeading": "Measure · {ref}",
|
||||
"detailHeading": "{ref} · {name}",
|
||||
"detailSub": "Status, responsibility & linked risks",
|
||||
"editTitle": "Edit measure",
|
||||
"createTitle": "Create measure",
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { FilterTabs } from "@/components/filter-tabs";
|
||||
import {
|
||||
CiaBadge,
|
||||
CiaLegend,
|
||||
KpiCard,
|
||||
OwnerChip,
|
||||
PageHead,
|
||||
@@ -196,7 +197,12 @@ export default async function AssetsPage({
|
||||
<TableHead>Asset</TableHead>
|
||||
<TableHead>{t("type")}</TableHead>
|
||||
<TableHead>{t("owner")}</TableHead>
|
||||
<TableHead>{t("protection")} (C/I/A)</TableHead>
|
||||
<TableHead>
|
||||
<span className="flex flex-col gap-1">
|
||||
{t("protection")}
|
||||
<CiaLegend />
|
||||
</span>
|
||||
</TableHead>
|
||||
<TableHead>{t("dependencies")}</TableHead>
|
||||
<TableHead>{t("status")}</TableHead>
|
||||
</TableRow>
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Modal } from "@/components/modal";
|
||||
import { AssetForm } from "@/components/asset-form";
|
||||
import { CiaBadge, Pill, Tag } from "@/components/mockup-ui";
|
||||
import { CiaBadge, CiaLegend, Pill, Tag } from "@/components/mockup-ui";
|
||||
import { riskLevel, riskRef, RISK_PILL_TONE } from "@/lib/risk";
|
||||
|
||||
export type AssetWithRelations = Prisma.AssetGetPayload<{
|
||||
@@ -145,6 +145,15 @@ export async function AssetDetailModal({
|
||||
<p className="text-sm text-muted-foreground">{tc("none")}</p>
|
||||
)}
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th />
|
||||
<th className="pb-1 text-right font-normal">
|
||||
<CiaLegend />
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{asset.relationsFrom.map((rel) => (
|
||||
<tr key={rel.id} className="border-b last:border-0">
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function MeasureDetailModal({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("detailHeading", { ref: measureRef(measure.refNo) })}
|
||||
title={t("detailHeading", { ref: measureRef(measure.refNo), name: measure.title })}
|
||||
sub={t("detailSub")}
|
||||
headerExtra={
|
||||
<span className="flex items-center gap-2">
|
||||
|
||||
@@ -55,11 +55,11 @@ export function KpiCard({
|
||||
);
|
||||
}
|
||||
|
||||
// Segmentbalken-Farben je Stufe 1–4 (Referenz docs/ISMS-GUI-Verbesserungen-GEFIM.html)
|
||||
// Segmentbalken-Farben je Stufe 1–4 als Ampel: Grün → Gelb → Orange → Rot
|
||||
const CIA_BAR_COLORS: Record<number, string> = {
|
||||
1: "bg-[#9db2c9]",
|
||||
2: "bg-[#5d8fbf]",
|
||||
3: "bg-[#e0982e]",
|
||||
1: "bg-[#2e9e6b]",
|
||||
2: "bg-[#e5b000]",
|
||||
3: "bg-[#e07d2e]",
|
||||
4: "bg-[#d64c4c]",
|
||||
};
|
||||
|
||||
@@ -113,6 +113,19 @@ export function CiaBadge({
|
||||
);
|
||||
}
|
||||
|
||||
/** VER/INT/VFB-Beschriftung passend zur Balkenbreite — einmal pro Tabelle als Überschrift. */
|
||||
export function CiaLegend() {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-2.5 text-[9.5px] font-bold tracking-[.04em] text-muted-foreground">
|
||||
{["VER", "INT", "VFB"].map((l) => (
|
||||
<span key={l} className="w-[34px] text-center">
|
||||
{l}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function Tag({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<span className="inline-block rounded-md bg-[#eef0f4] px-2 py-0.5 text-[11px] font-bold text-[#555]">
|
||||
@@ -142,6 +155,7 @@ export function OwnerChip({ name, noOwnerLabel }: { name?: string | null; noOwne
|
||||
const PILL_TONES = {
|
||||
ok: "bg-[#e7f6ee] text-[#2e9e6b]",
|
||||
warn: "bg-[#fbf1de] text-[#a9760f]",
|
||||
orange: "bg-[#fbdcc0] text-[#c2681a]",
|
||||
risk: "bg-[#fbe7e7] text-[#d64c4c]",
|
||||
info: "bg-[#e7f1fa] text-[#3a86c8]",
|
||||
mut: "bg-[#eef0f4] text-muted-foreground",
|
||||
|
||||
@@ -26,7 +26,7 @@ export function Modal({
|
||||
}) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-black/30 p-6 backdrop-blur-[2px]">
|
||||
<div className="shadow-card my-auto w-full max-w-3xl rounded-2xl border bg-card">
|
||||
<div className="shadow-card my-auto w-full max-w-4xl rounded-2xl border bg-card">
|
||||
<div className="flex items-start justify-between gap-3 border-b p-5">
|
||||
<SectTitle title={title} sub={sub} />
|
||||
<div className="flex items-center gap-3">
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Modal } from "@/components/modal";
|
||||
import { SegmentedRating } from "@/components/segmented-rating";
|
||||
import { CiaBadge, CriticalityPill, Pill, Tag } from "@/components/mockup-ui";
|
||||
import { CiaBadge, CiaLegend, CriticalityPill, Pill, Tag } from "@/components/mockup-ui";
|
||||
import { riskLevel, riskRef, RISK_PILL_TONE } from "@/lib/risk";
|
||||
|
||||
export type LinkedRisk = {
|
||||
@@ -158,6 +158,15 @@ export async function ProcessDetailModal({
|
||||
</div>
|
||||
{secondary.length === 0 && <p className="text-sm text-muted-foreground">{t("noAssets")}</p>}
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th />
|
||||
<th className="pb-1 text-right font-normal">
|
||||
<CiaLegend />
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{secondary.map((pa) => (
|
||||
<tr key={pa.id} className="border-b last:border-0">
|
||||
|
||||
@@ -19,7 +19,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Modal } from "@/components/modal";
|
||||
import { CiaBadge, Pill, Tag } from "@/components/mockup-ui";
|
||||
import { CiaBadge, CiaLegend, Pill, Tag } from "@/components/mockup-ui";
|
||||
import { riskLevel, riskRef, RISK_PILL_TONE } from "@/lib/risk";
|
||||
import { measureRef, MEASURE_STATUS_TONE } from "@/lib/measure";
|
||||
|
||||
@@ -123,7 +123,7 @@ function RiskScoreScale({ gross, residual }: { gross: number; residual: number |
|
||||
// 5×5 → Score 1..25; Position linear auf der Skala
|
||||
const pos = (v: number) => `${((Math.min(25, Math.max(1, v)) - 1) / 24) * 100}%`;
|
||||
return (
|
||||
<div className="mt-4">
|
||||
<div className="pt-1">
|
||||
<div
|
||||
className="relative h-3 rounded-lg"
|
||||
style={{
|
||||
@@ -173,7 +173,7 @@ export async function RiskDetailModal({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("detailHeading", { ref: riskRef(risk.refNo) })}
|
||||
title={t("detailHeading", { ref: riskRef(risk.refNo), name: risk.title })}
|
||||
sub={t("detailSub")}
|
||||
headerExtra={
|
||||
<span className="flex items-center gap-2">
|
||||
@@ -241,6 +241,15 @@ export async function RiskDetailModal({
|
||||
<p className="text-sm text-muted-foreground">{t("noAssets")}</p>
|
||||
)}
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th />
|
||||
<th />
|
||||
<th className="pb-1 text-right font-normal">
|
||||
<CiaLegend />
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{risk.riskAssets.map((ra) => (
|
||||
<tr key={ra.id} className="border-b last:border-0">
|
||||
@@ -266,9 +275,10 @@ export async function RiskDetailModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Brutto → Rest-Risiko: zwei Karten mit Pfeil + Farbskala */}
|
||||
{/* Brutto → Rest-Risiko: Farbskala oben, darunter zwei Karten mit Pfeil */}
|
||||
<div className="mx-5 rounded-xl border border-[#e6e2f3] bg-[#f2f0f9] p-4">
|
||||
<div className="grid items-center gap-3 sm:grid-cols-[1fr_auto_1fr]">
|
||||
<RiskScoreScale gross={risk.score} residual={risk.residualScore} />
|
||||
<div className="mt-4 grid items-center gap-3 sm:grid-cols-[1fr_auto_1fr]">
|
||||
<RatingCard
|
||||
title={t("gross")}
|
||||
likelihood={risk.likelihood}
|
||||
@@ -285,7 +295,6 @@ export async function RiskDetailModal({
|
||||
accent="residual"
|
||||
/>
|
||||
</div>
|
||||
<RiskScoreScale gross={risk.score} residual={risk.residualScore} />
|
||||
</div>
|
||||
|
||||
{/* Notwendige Maßnahmen — echt verknüpft */}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
import { useState } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
// Farblogik Stufe 1–4 (Referenz docs/ISMS-GUI-Verbesserungen-GEFIM.html)
|
||||
// Ampel-Farblogik Stufe 1–4: Grün → Gelb → Orange → Rot
|
||||
const LEVEL_STYLE: Record<number, { on: string; border: string }> = {
|
||||
1: { on: "bg-[#9db2c9] text-white border-transparent", border: "border-[#9db2c9]" },
|
||||
2: { on: "bg-[#5d8fbf] text-white border-transparent", border: "border-[#5d8fbf]" },
|
||||
3: { on: "bg-[#e0982e] text-white border-transparent", border: "border-[#e0982e]" },
|
||||
1: { on: "bg-[#2e9e6b] text-white border-transparent", border: "border-[#2e9e6b]" },
|
||||
2: { on: "bg-[#e5b000] text-[#3b3b3a] border-transparent", border: "border-[#e5b000]" },
|
||||
3: { on: "bg-[#e07d2e] text-white border-transparent", border: "border-[#e07d2e]" },
|
||||
4: { on: "bg-[#d64c4c] text-white border-transparent", border: "border-[#d64c4c]" },
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -19,11 +19,11 @@ export const RISK_CELL_COLORS: Record<RiskLevelKey, string> = {
|
||||
critical: "#ef9a98",
|
||||
};
|
||||
|
||||
/** Pillen-Ton je Stufe (mockup-ui Pill). */
|
||||
export const RISK_PILL_TONE: Record<RiskLevelKey, "ok" | "info" | "warn" | "risk"> = {
|
||||
/** Pillen-Ton je Stufe — gemäß Heatmap-Skala: Grün → Gelb → Orange → Rot. */
|
||||
export const RISK_PILL_TONE: Record<RiskLevelKey, "ok" | "warn" | "orange" | "risk"> = {
|
||||
low: "ok",
|
||||
medium: "info",
|
||||
elevated: "warn",
|
||||
medium: "warn",
|
||||
elevated: "orange",
|
||||
high: "risk",
|
||||
critical: "risk",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user