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 { createSoftware, deleteSoftware, updateSoftware } from "@/server/actions/software"; 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 { softwareRef, withQuery } from "@/lib/supplier"; import { riskLevel, riskRef, RISK_PILL_TONE } from "@/lib/risk"; export type SoftwareAssetDetail = Prisma.AssetGetPayload<{ include: { softwareProfile: { include: { provider: { select: { id: true; name: 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 } } } }; }; }>; const inputCls = "h-9 w-full rounded-md border border-input bg-transparent px-3 text-sm"; const APPROVAL_TONE = { BEANTRAGT: "warn", FREIGEGEBEN: "ok", GESPERRT: "risk" } as const; const APPROVAL_VALUES = ["BEANTRAGT", "FREIGEGEBEN", "GESPERRT"] as const; /* ─────────────────────── Detail-Cockpit ─────────────────────── */ export async function SoftwareDetailModal({ software, canWrite, backHref = "/suppliers?tab=software" }: { software: SoftwareAssetDetail; canWrite: boolean; backHref?: string }) { const t = await getTranslations("software"); const ta = await getTranslations("assets"); const tCrit = await getTranslations("criticality"); const tType = await getTranslations("assetType"); const tStatus = await getTranslations("softwareStatus"); const tLevel = await getTranslations("riskLevel"); const tRisks = await getTranslations("risks"); const tDep = await getTranslations("dependencies"); const tc = await getTranslations("common"); const p = software.softwareProfile!; return ( {tType(software.type)} {tStatus(p.approvalStatus)} } closeHref={backHref} closeLabel={t("close")} footer={ <> {canWrite && ( )} } >
{/* Stammdaten */}
{t("masterPill")}
{softwareRef(p.refNo)} · {software.name}
{t("provider")}:{" "} {p.provider ? ( {p.provider.name} ) : t("noProvider")}
{p.version &&
{t("version")}: {p.version}
}
{t("approvalStatus")}: {tStatus(p.approvalStatus)}{p.approvedBy ? ` · ${t("approvedBy")}: ${p.approvedBy}` : ""}
{t("criticality")}: {tCrit(String(p.criticality))}
{p.nextReview &&
{t("nextReview")}: {p.nextReview.toISOString().slice(0, 10)}
}
{p.notes &&

{p.notes}

}
{/* Verknüpfte Assets */}
{ta("depPill")}
{software.relationsFrom.length === 0 && software.relationsTo.length === 0 && (

{tc("none")}

)} {software.relationsFrom.length > 0 && ( {software.relationsFrom.map((rel) => ( ))}
{rel.relatedAsset.name} {tType(rel.relatedAsset.type)}
)} {tDep("openGraph")} →
{/* Zugeordnete Risiken */}
{t("linkedRisks")} {canWrite && ( )}
{software.riskAssets.length === 0 &&

{tc("none")}

}
    {software.riskAssets.map((ra) => (
  • {riskRef(ra.risk.refNo)} {ra.risk.title} {ra.risk.score} · {tLevel(riskLevel(ra.risk.score))}
  • ))}
); } /* ─────────────────────── Formular ─────────────────────── */ async function SoftwareFields({ software, providers, formId }: { software?: SoftwareAssetDetail; providers: { id: string; name: string }[]; formId?: string }) { const t = await getTranslations("software"); const tA = await getTranslations("assets"); const tLevel = await getTranslations("protectionLevel"); const tStatus = await getTranslations("softwareStatus"); const f = formId ? { form: formId } : {}; const p = software?.softwareProfile; return (
{t("protection")} (1–4)
{( [ ["confidentiality", tA("confidentiality")], ["integrity", tA("integrity")], ["availability", tA("availability")], ] as const ).map(([n, label]) => (
))}