import Link from "next/link"; import { notFound } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { ArrowLeft, Pencil } from "lucide-react"; import { requireSession } from "@/server/auth"; import { dbForTenant } from "@/server/db"; import { hasPermission, requirePermission } from "@/server/rbac"; import { Button } from "@/components/ui/button"; import { Pill } from "@/components/mockup-ui"; import { PolicyReadView, POLICY_STATUS_LABEL, POLICY_STATUS_TONE, POLICY_TYPE_LABEL, } from "@/components/policy-modals"; import { PolicyRegisterView } from "@/components/policy-registers"; import { GenericRegisterView, type RegisterColumn } from "@/components/generic-register"; const REGISTER_CODES = new Set(["CRYPTO", "RISKMATRIX", "CLASSIFICATION", "HANDBUCH"]); // Aus Vorlagen-Markdown gerenderte Dokumente (bearbeitbar über den Editor) const TEMPLATE_TYPES = new Set(["LEITLINIE", "RICHTLINIE", "VERFAHREN"]); export default async function PolicyDetailPage({ params, }: { params: Promise<{ code: string }>; }) { const session = await requireSession(); requirePermission(session, "policy:read"); const t = await getTranslations("policies"); const db = dbForTenant(session.user.tenantId); const { code } = await params; const doc = await db.policyDocument.findFirst({ where: { code: decodeURIComponent(code) } }); if (!doc) notFound(); const canWrite = hasPermission(session, "policy:write"); const isRegister = REGISTER_CODES.has(doc.code); // Generisches verwaltetes Register (WP3.0): editierbare Zeilen nach Pflichtspalten. const genericReg = doc.type === "REGISTER" && !isRegister ? await db.managedRegister.findFirst({ where: { code: doc.code } }) : null; let content: React.ReactNode; if (genericReg) { const [rows, allAssets] = await Promise.all([ db.registerRow.findMany({ where: { registerId: genericReg.id }, orderBy: { orderIdx: "asc" } }), db.asset.findMany({ select: { id: true, name: true, type: true }, orderBy: { name: "asc" } }), ]); content = ( ({ id: r.id, values: (r.values as Record) ?? {}, supplierRef: r.supplierRef, assetRef: r.assetRef }))} canWrite={canWrite} suppliers={allAssets.filter((a) => a.type === "SUPPLIER" || a.type === "IT_SERVICE").map((a) => ({ id: a.id, name: a.name }))} assets={allAssets.map((a) => ({ id: a.id, name: a.name }))} /> ); } else if (isRegister) { const data = doc.code === "CRYPTO" ? { crypto: await db.cryptoEntry.findMany({ orderBy: { orderIdx: "asc" } }) } : doc.code === "CLASSIFICATION" ? { classes: await db.classificationClass.findMany({ orderBy: { orderIdx: "asc" } }), aspects: await db.handlingAspect.findMany({ orderBy: { orderIdx: "asc" } }), rules: await db.handlingRule.findMany(), } : doc.code === "RISKMATRIX" ? { riskClasses: await db.riskMatrixClass.findMany({ orderBy: { orderIdx: "asc" } }), ewLevels: await db.riskEwLevel.findMany({ orderBy: { level: "asc" } }), damage: await db.riskDamageDimension.findMany({ orderBy: { orderIdx: "asc" } }), } : { handbook: await db.handbookTopic.findMany({ orderBy: { orderIdx: "asc" } }), variables: await db.policyVariable.findMany({ orderBy: { orderIdx: "asc" } }) }; content = ; } else { const [variables, reqs] = await Promise.all([ db.policyVariable.findMany({ orderBy: { orderIdx: "asc" } }), db.policyRequirement.findMany({ where: { policyCode: doc.code, archivedAt: null } }), ]); const controls = [...new Set(reqs.map((r) => r.control))].sort(); const relatedVas = [...new Set(reqs.flatMap((r) => r.vaCodes))].sort(); content = ; } const showEdit = canWrite && TEMPLATE_TYPES.has(doc.type); return (
{t("backToLibrary")}

{doc.code} · {doc.title}

{POLICY_STATUS_LABEL[doc.status]}

{POLICY_TYPE_LABEL[doc.type]} · v{doc.version} {(isRegister || genericReg) && canWrite ? ` · ${t("editableInline")}` : ""}

{showEdit && ( )}
{content}
); }