Files
craftvia/src/lib/incident.ts
T
msolarczekandClaude Opus 5 c8e6f30a27
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s
Basis: Certvia dev@a48c5fb als Fundament für Craftvia
Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:05:39 +02:00

117 lines
3.7 KiB
TypeScript

/**
* Zentrale Konstanten/Helfer für das Modul „Vorfälle" (client-safe).
* Kategorien (§4-Taxonomie), Status (§3-Lebenszyklus) und die refNo-Anzeige.
*/
import type { IncidentSeverity } from "@/lib/incident-severity";
export const INCIDENT_CATEGORIES = [
"malware",
"phishing",
"unauthorized_access",
"data_loss",
"outage",
"physical",
"misconfiguration",
"supplier",
"prototype_customer_data",
"other",
] as const;
export type IncidentCategory = (typeof INCIDENT_CATEGORIES)[number];
export const INCIDENT_STATUSES = [
"neu",
"triage",
"in_bearbeitung",
"eingedaemmt",
"behoben",
"abgeschlossen",
"wiedereroeffnet",
] as const;
export type IncidentStatus = (typeof INCIDENT_STATUSES)[number];
export const INCIDENT_SOURCES = ["manual", "email"] as const;
export type IncidentSource = (typeof INCIDENT_SOURCES)[number];
export const INCIDENT_PRIORITIES = ["niedrig", "mittel", "hoch", "kritisch"] as const;
/**
* Erlaubte Statusübergänge (§3). Jeder Übergang wird serverseitig geprüft;
* bestimmte Übergänge verlangen zusätzlich Pflichtfelder (siehe TRANSITION_REQUIRES).
*/
export const STATUS_TRANSITIONS: Record<IncidentStatus, IncidentStatus[]> = {
neu: ["triage", "in_bearbeitung"],
triage: ["in_bearbeitung", "eingedaemmt"],
in_bearbeitung: ["eingedaemmt", "behoben"],
eingedaemmt: ["behoben", "in_bearbeitung"],
behoben: ["abgeschlossen", "wiedereroeffnet"],
abgeschlossen: ["wiedereroeffnet"],
wiedereroeffnet: ["triage", "in_bearbeitung"],
};
/**
* Pflichtfelder je Zielstatus (§8). Ein Übergang nach „behoben" verlangt Ursache +
* Lösung; „abgeschlossen" zusätzlich Abschlussnotiz + Lessons Learned.
*/
export const TRANSITION_REQUIRES: Partial<Record<IncidentStatus, string[]>> = {
behoben: ["rootCause", "resolution"],
abgeschlossen: ["rootCause", "resolution", "closingNote", "lessonsLearned"],
};
export function canTransition(from: IncidentStatus, to: IncidentStatus): boolean {
return STATUS_TRANSITIONS[from]?.includes(to) ?? false;
}
/**
* Ob der Nutzer vertrauliche Vorfälle sehen darf (§11): nur Rollen mit
* incident:manage oder incident:close. Der owner sieht seine eigenen zusätzlich
* über den ownerId-Filter (serverseitig in der Liste). Pure/client-safe.
*/
export function canSeeRestricted(session: { user?: { permissions?: string[] } }): boolean {
const p = session.user?.permissions ?? [];
return p.includes("incident:manage") || p.includes("incident:close");
}
/**
* Fehlende Pflichtfelder für einen Zielstatus (§8). Prüft die effektiven Werte
* (bereits gesetzt oder beim Übergang mitgeliefert). Leeres Array = alles erfüllt.
* Pure Funktion — von Action und Test gemeinsam genutzt.
*/
export function missingRequiredFields(
target: IncidentStatus,
values: Record<string, unknown>,
): string[] {
const required = TRANSITION_REQUIRES[target] ?? [];
return required.filter((field) => {
const v = values[field];
return v === null || v === undefined || (typeof v === "string" && v.trim() === "");
});
}
/** Farb-Ton (mockup-ui Pill) je Severity. */
export const SEVERITY_TONE: Record<IncidentSeverity, "mut" | "info" | "warn" | "risk"> = {
niedrig: "mut",
mittel: "info",
hoch: "warn",
kritisch: "risk",
};
/** Farb-Ton je Status. */
export const STATUS_TONE: Record<IncidentStatus, "mut" | "info" | "warn" | "risk" | "ok"> = {
neu: "risk",
triage: "warn",
in_bearbeitung: "info",
eingedaemmt: "info",
behoben: "ok",
abgeschlossen: "mut",
wiedereroeffnet: "warn",
};
/** Farb-Ton je Meldung-Track-Status (reportStatus, §3 Parallel-Track). */
export const REPORT_STATUS_TONE: Record<string, "mut" | "info" | "warn" | "orange" | "ok"> = {
none: "mut",
pruefung: "warn",
erstmeldung: "orange",
folgemeldung: "info",
abschluss: "ok",
};