import Link from "next/link"; import { getTranslations } from "next-intl/server"; import { Network, Pencil, Plus, Trash2 } from "lucide-react"; import type { Prisma } from "@prisma/client"; import { addRaci, createService, cycleRaci, deleteRaci, deleteService, updateService, } from "@/server/actions/services"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; 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, CiaLegend, CriticalityPill, Pill, Tag } from "@/components/mockup-ui"; import { serviceRef, withQuery } from "@/lib/supplier"; import { riskLevel, riskRef, RISK_PILL_TONE } from "@/lib/risk"; import { ISA_CONTROLS } from "@/lib/isa-controls"; export type ServiceAssetDetail = Prisma.AssetGetPayload<{ include: { serviceProfile: { include: { provider: { select: { id: true; name: true } } } }; raci: true; riskAssets: { include: { risk: { select: { id: true; refNo: true; title: true; score: true } } } }; relationsFrom: { include: { relatedAsset: { select: { id: true; name: true; type: true; confidentiality: true; integrity: true; availability: true } } } }; relationsTo: { include: { asset: { select: { id: true; name: true } } } }; processAssets: { include: { process: { select: { id: true; name: true } } } }; }; }>; const inputCls = "h-9 w-full rounded-md border border-input bg-transparent px-3 text-sm"; const RACI_TONE = { PROVIDER: "info", US: "violet", SHARED: "warn" } as const; /* ─────────────────────── Detail-Cockpit ─────────────────────── */ export async function ServiceDetailModal({ service, canWrite, backHref = "/suppliers?tab=services" }: { service: ServiceAssetDetail; canWrite: boolean; backHref?: string }) { const t = await getTranslations("services"); const ta = await getTranslations("assets"); const tParty = await getTranslations("raciParty"); const tCrit = await getTranslations("criticality"); const tType = await getTranslations("assetType"); const tLevel = await getTranslations("riskLevel"); const tRisks = await getTranslations("risks"); const tDep = await getTranslations("dependencies"); const tc = await getTranslations("common"); const tp = await getTranslations("processes"); const p = service.serviceProfile!; return ( {tType(service.type)} } closeHref={backHref} closeLabel={tp("close")} footer={ <> {canWrite && ( )} } >
{/* Stammdaten — violette Karte wie beim Asset-Detail */}
{ta("masterPill")} {ta("masterNote")}
{serviceRef(p.refNo)} · {service.name}
{t("provider")}:{" "} {p.provider ? ( {p.provider.name} ) : ( p.internal ? t("internal") : t("noProvider") )} {" · "} {t("criticality")}: {tCrit(String(p.criticality))}
{p.notes &&

{p.notes}

}

{ta("processes")}

{service.processAssets.length === 0 &&

{tc("none")}

}
    {service.processAssets.map((pa) => (
  • {pa.process.name}
  • ))}
{/* Verknüpfte Assets — Tabelle wie beim Asset-Detail */}
{ta("depPill")} {ta("depNote")}
{service.relationsFrom.length === 0 && service.relationsTo.length === 0 && (

{tc("none")}

)} {service.relationsFrom.length > 0 && ( {service.relationsFrom.map((rel) => ( ))}
{rel.relatedAsset.name} {tType(rel.relatedAsset.type)}
)} {service.relationsTo.length > 0 && ( <>

{ta("relationReverseHint")}

    {service.relationsTo.map((rel) => (
  • {rel.asset.name}
  • ))}
)} {tDep("openGraph")} →
{/* Zugeordnete Risiken — Band wie beim Asset-Detail */}
{ta("linkedRisks")} {canWrite && ( )}
{service.riskAssets.length === 0 &&

{tc("none")}

}
    {service.riskAssets.map((ra) => (
  • {riskRef(ra.risk.refNo)} {ra.risk.title} {ra.risk.score} · {tLevel(riskLevel(ra.risk.score))}
  • ))}
{/* Verantwortungsmatrix (RACI) — darunter, über die volle Breite */}

{t("raci")}

{t("raciNote")}

{canWrite && (
{t("addControl")}
{ISA_CONTROLS.map((c) => ( ))}
)}
{service.raci.length === 0 ? (

{t("raciEmpty")}

) : (
{t("control")} {t("controlTitle")} {t("responsibility")} {t("evidence")}
{service.raci.map((r) => { const ctrl = ISA_CONTROLS.find((c) => c.ref === r.controlRef); return (
{r.controlRef} {r.title ?? ctrl?.title ?? ""} {canWrite ? (
) : ( {tParty(r.responsibility)} )}
{r.evidenceRef ?? tc("none")} {canWrite && (
)}
); })}
)}
); } /* ─────────────────────── Formular ─────────────────────── */ async function ServiceFields({ service, providers, formId }: { service?: ServiceAssetDetail; providers: { id: string; name: string }[]; formId?: string }) { const t = await getTranslations("services"); const tA = await getTranslations("assets"); const tLevel = await getTranslations("protectionLevel"); const f = formId ? { form: formId } : {}; const p = service?.serviceProfile; return (
{t("protection")} (1–4)
{( [ ["confidentiality", tA("confidentiality")], ["integrity", tA("integrity")], ["availability", tA("availability")], ] as const ).map(([n, label]) => (
))}