import type { Metadata } from 'next' import { db } from '@/lib/db' import { visibleEntryWhere } from '@/lib/access/caseFilter' import { visibleAttachmentWhere } from '@/lib/access/attachmentFilter' import { loadVisibleCase } from '@/lib/cases/service' import { loadSections } from '@/lib/cases/sectionService' import { permissionsForPrint } from '@/lib/export/printAccess' import { isInputField, fillPlaceholders } from '@/lib/templates/fields' import { de } from '@/lib/i18n/de' import { NotAvailable } from '../../../NotAvailable' import type { RenderedSection } from '@/lib/sections/render' export const dynamic = 'force-dynamic' export async function generateMetadata({ params, }: { params: Promise<{ caseNumber: string }> }): Promise { const { caseNumber } = await params return { title: caseNumber, robots: { index: false, follow: false } } } /** * Druckansicht einer Fallakte. * * Sie ist die einzige Vorlage für den PDF-Export: kein zweites Layout von Hand * in einer PDF-Bibliothek. Der Playwright-Dienst ruft genau diese Seite auf. * * **Ein Export darf nie sauberer aussehen als die Akte ist.** Deshalb: * gesperrte Felder erscheinen als Balken mit Vermerk statt zu fehlen, * geänderte Einträge sind als geändert gekennzeichnet, zurückgezogene stehen * als Grabstein mit Begründung. Siehe docs/08-suche-und-export.md. */ export default async function PrintCasePage({ params, searchParams, }: { params: Promise<{ caseNumber: string }> searchParams: Promise<{ token?: string }> }) { const { caseNumber } = await params const { token } = await searchParams const zugang = await permissionsForPrint(token) if (!zugang) return const { permissions, displayName } = zugang const akte = await loadVisibleCase(permissions, caseNumber) if (!akte) return const [sections, entries, anhaenge, levels, autoren] = await Promise.all([ loadSections(permissions, akte.id, akte.templateId), db.caseEntry.findMany({ where: { AND: [visibleEntryWhere(permissions), { caseId: akte.id }] }, orderBy: [{ createdAt: 'asc' }, { id: 'asc' }], select: { id: true, type: true, body: true, authorId: true, createdAt: true, version: true, editedAt: true, editVisible: true, retractedAt: true, retractionReason: true, }, }), db.attachment.findMany({ where: { AND: [visibleAttachmentWhere(permissions), { caseId: akte.id }] }, orderBy: { createdAt: 'asc' }, select: { id: true, originalName: true, sha256: true, sizeBytes: true, scanStatus: true }, }), db.clearanceLevelConfig.findMany({ orderBy: { level: 'asc' } }), db.user.findMany({ select: { id: true, displayName: true } }), ]) const levelName = new Map(levels.map((l) => [l.level, l.name])) const nameVon = new Map(autoren.map((a) => [a.id, a.displayName])) const stand = new Date() return (
{akte.classification} · {levelName.get(akte.classification) ?? de.common.unknown}

{akte.title}

{akte.caseNumber}

{de.export.coverNote}

{de.section.heading}

{de.case.timeline}

{entries.length === 0 ? (

{de.entry.empty}

) : ( entries.map((eintrag) => (

{de.entry.types[eintrag.type] ?? eintrag.type} ·{' '} {nameVon.get(eintrag.authorId) ?? eintrag.authorId} ·{' '} {eintrag.createdAt.toLocaleString('de-DE')} {eintrag.editVisible && eintrag.editedAt ? ` · ${de.export.editedAt(eintrag.version, eintrag.editedAt.toLocaleDateString('de-DE'))}` : ''}

{eintrag.retractedAt ? (

{de.entry.retracted( eintrag.retractedAt.toLocaleDateString('de-DE'), nameVon.get(eintrag.authorId) ?? eintrag.authorId, eintrag.retractionReason ?? '', )}

) : (

{eintrag.body}

)}
)) )}

{de.attachment.heading}

{anhaenge.length === 0 ? (

{de.attachment.empty}

) : ( {anhaenge.map((anhang) => ( {/* Der Hash macht nachprüfbar, dass die Datei zum Ausdruck gehört - deshalb steht er im Verzeichnis. */} ))}
{de.export.fileName} {de.export.fileSize} {de.export.fileHash}
{anhang.originalName} {anhang.sizeBytes} {anhang.sha256}
)}
) } function Zeile({ label, value }: { label: string; value: string }) { return ( {label} {value} ) } /** * Die Abschnitte im Druck. Ein gesperrtes Feld wird als Balken ausgegeben, mit * dem Vermerk, ab welcher Stufe es lesbar wäre - sonst merkt der Empfänger * nicht, dass er eine gekürzte Fassung in der Hand hält. */ function PrintSections({ sections, levelName, }: { sections: RenderedSection[] levelName: Map }) { const werte = Object.fromEntries( sections.filter((s) => s.visible).map((s) => [s.fieldKey, s.value]), ) return (
{sections.map((section) => { const field = section.field if (!isInputField(field)) { if (field.type === 'pagebreak') { return
} const text = fillPlaceholders(field.text ?? '', werte) return field.type === 'heading' ? (

{text || field.label}

) : (

{text}

) } return (

{field.label}

{!section.visible ? (

 {' '} {de.export.redactedAt( section.value.requiredLevel, levelName.get(section.value.requiredLevel) ?? '', )}

) : section.segments ? (

{section.segments.map((segment, index) => segment.kind === 'text' ? ( {segment.text} ) : ( // Balken statt Auslassung, mit der Stufe daneben: der // Empfaenger soll sehen, dass hier etwas fehlt.   [{segment.requiredLevel}] ), )}

) : (

{String(section.value ?? '-')}

)}
) })}
) }