Richtlinien: Lesemodus als eigene Seite + Bearbeitungsmodus (§8)

Auf Wunsch: Dokumente öffnen nicht mehr als Popup, sondern als eigene Seite
mit Zurück-Navigation.

- Neue Routen /policies/[code] (Lesen) und /policies/[code]/edit (Bearbeiten);
  Popup-Modals durch PolicyReadView/PolicyRegisterView ersetzt. Alle Deeplinks
  ({{LINK}}, Coverage, Bibliothek, Register-Callouts) zeigen auf /policies/CODE.
- Detailseite: „← Zurück zur Bibliothek", Titel/Status/Version, Control-Chips,
  „Bearbeiten"-Button (nur Vorlagen-Dokumente, policy:write).
- Bearbeitungsmodus für Leitlinie/Richtlinien/Verfahren:
  * Vorlagen-Markdown-Editor (Textarea) mit Live-Vorschau (Lesemodus),
    Speichern via updatePolicyTemplate.
  * Dokument-bezogene Variablen (§8 Variablen-Scoping): nur die im Dokument
    vorkommenden Variablen — inkl. über BL-Referenzen gebundene — einzeln
    editierbar; Änderung propagiert zentral in alle Dokumente (eine Pflegestelle).
- Register-Actions revalidieren jetzt /policies per layout (Detailseiten aktuell).

Verifiziert: R08 als Seite (kein Modal), Editor mit 23 dokument-bezogenen
Variablen; PW_MIN_LENGTH 12→14 propagiert in R08 und Handbuch; Template-Speichern
mit Redirect zur Ansicht; zurückgesetzt.

Hinweis: Der Vier-Augen-Freigabe-Workflow mit Versionierung/Diff (§8) folgt als
nächster Schritt — aktuell speichert der Editor direkt (mit Audit-Log).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 16:29:18 +02:00
co-authored by Claude Opus 4.8
parent e2b6dee788
commit 094229011d
9 changed files with 365 additions and 163 deletions
+14 -27
View File
@@ -1,5 +1,5 @@
import Link from "next/link";
import { getFormatter, getTranslations } from "next-intl/server";
import { getFormatter } from "next-intl/server";
import { Pencil, Plus, Trash2 } from "lucide-react";
import type {
ClassificationClass,
@@ -15,8 +15,7 @@ import type {
} from "@prisma/client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Modal } from "@/components/modal";
import { Pill, Tag } from "@/components/mockup-ui";
import { Pill } from "@/components/mockup-ui";
import { isExpired, isExpiring } from "@/lib/supplier";
import { buildContext, renderPolicyHtml, resolveLink } from "@/lib/policy-render";
import {
@@ -43,39 +42,27 @@ export interface RegisterData {
variables?: PolicyVariable[];
}
/** Popup für die verwalteten Register (§7b) und das Anwender-Handbuch (§9.7). */
export async function PolicyRegisterModal({
/** Inhalt der verwalteten Register (§7b) und des Anwender-Handbuchs (§9.7) — auf eigener Seite. */
export async function PolicyRegisterView({
doc,
data,
canWrite,
backHref,
}: {
doc: PolicyDocument;
data: RegisterData;
canWrite: boolean;
backHref: string;
}) {
const t = await getTranslations("policies");
return (
<Modal
title={`${doc.code} · ${doc.title}`}
sub={doc.type === "HANDBUCH" ? "Handbuch" : "Register"}
headerExtra={<Tag>{doc.type === "HANDBUCH" ? "Handbuch" : "Register"}</Tag>}
closeHref={backHref}
closeLabel={t("close")}
footer={<Button nativeButton={false} render={<Link href={backHref} />}>{t("close")}</Button>}
>
<div className="max-h-[74vh] overflow-y-auto p-5">
{doc.code === "CRYPTO" && <CryptoRegister entries={data.crypto ?? []} canWrite={canWrite} />}
{doc.code === "CLASSIFICATION" && (
<ClassificationMatrix classes={data.classes ?? []} aspects={data.aspects ?? []} rules={data.rules ?? []} canWrite={canWrite} />
)}
{doc.code === "RISKMATRIX" && (
<RiskMatrix classes={data.riskClasses ?? []} ew={data.ewLevels ?? []} damage={data.damage ?? []} canWrite={canWrite} />
)}
{doc.code === "HANDBUCH" && <Handbook topics={data.handbook ?? []} variables={data.variables ?? []} />}
</div>
</Modal>
<div>
{doc.code === "CRYPTO" && <CryptoRegister entries={data.crypto ?? []} canWrite={canWrite} />}
{doc.code === "CLASSIFICATION" && (
<ClassificationMatrix classes={data.classes ?? []} aspects={data.aspects ?? []} rules={data.rules ?? []} canWrite={canWrite} />
)}
{doc.code === "RISKMATRIX" && (
<RiskMatrix classes={data.riskClasses ?? []} ew={data.ewLevels ?? []} damage={data.damage ?? []} canWrite={canWrite} />
)}
{doc.code === "HANDBUCH" && <Handbook topics={data.handbook ?? []} variables={data.variables ?? []} />}
</div>
);
}