Files
AfiS-Aktensystem/web/app/(print)/dokumente/[documentNumber]/druck/page.tsx
T
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

181 lines
5.9 KiB
TypeScript

import type { Metadata } from 'next'
import { db } from '@/lib/db'
import { loadDocumentSections } from '@/lib/documents/service'
import { loadVisibleDocument } from '@/lib/documents/service'
import { resolvePhotoUrls } from '@/lib/files/service'
import { permissionsForPrint } from '@/lib/export/printAccess'
import { Sheet } from '@/components/documents/Sheet'
import { de } from '@/lib/i18n/de'
import { NotAvailable } from '../../../NotAvailable'
import { isInputField, fillPlaceholders } from '@/lib/templates/fields'
export const dynamic = 'force-dynamic'
export async function generateMetadata({
params,
}: {
params: Promise<{ documentNumber: string }>
}): Promise<Metadata> {
const { documentNumber } = await params
return { title: documentNumber, robots: { index: false, follow: false } }
}
/**
* Druckansicht eines einzelnen Dokuments.
*
* Kein Verlauf, keine Kopfdatentabelle einer Akte - das Dokument selbst, so
* wie seine Vorlage es vorsieht. Ein Aktenblatt kommt als Aktenblatt heraus,
* ein Vertrag als Vertrag. Siehe docs/08-suche-und-export.md.
*/
export default async function PrintDocumentPage({
params,
searchParams,
}: {
params: Promise<{ documentNumber: string }>
searchParams: Promise<{ token?: string }>
}) {
const { documentNumber } = await params
const { token } = await searchParams
const zugang = await permissionsForPrint(token)
if (!zugang) return <NotAvailable />
const { permissions, displayName } = zugang
const doc = await loadVisibleDocument(permissions, documentNumber)
if (!doc) return <NotAvailable />
const [sections, template, foundationLevels, levels] = await Promise.all([
loadDocumentSections(permissions, doc),
db.documentTemplate.findUnique({
where: { id: doc.templateId },
select: { sheetTitle: true, sheetSubtitle: true, name: true },
}),
db.foundationClearance.findMany({ orderBy: { sortOrder: 'asc' } }),
db.clearanceLevelConfig.findMany({ orderBy: { level: 'asc' } }),
])
const photoUrls = await resolvePhotoUrls(
permissions,
sections.map((section) => ({
fieldKey: section.fieldKey,
visible: section.visible,
value: section.visible ? section.value : null,
type: section.field.type,
})),
)
const levelName = new Map(levels.map((l) => [l.level, l.name]))
const werte = Object.fromEntries(
sections.filter((s) => s.visible).map((s) => [s.fieldKey, s.value]),
)
// Das Aktenblatt bringt sein eigenes Aussehen mit und braucht keinen
// Berichtsrahmen darum.
if (doc.layout === 'DOSSIER') {
return (
<Sheet
sections={sections}
title={template?.sheetTitle ?? de.app.name}
subtitle={template?.sheetSubtitle ?? template?.name ?? doc.title}
documentNumber={doc.documentNumber}
issuedAt={doc.createdAt}
foundationLevels={foundationLevels}
footerNote={de.document.footerNote}
photoUrls={photoUrls}
/>
)
}
return (
<main className="print-sheet">
<section className="print-cover">
<span className="print-stamp">
{doc.classification} · {levelName.get(doc.classification) ?? de.common.unknown}
</span>
<h1 style={{ marginTop: '4mm' }}>{doc.title}</h1>
<p style={{ fontFamily: 'var(--font-mono, monospace)', margin: 0 }}>
{doc.documentNumber}
</p>
<table className="print-meta">
<tbody>
<tr>
<th>{de.case.status}</th>
<td>{de.document.statusNames[doc.status] ?? doc.status}</td>
</tr>
<tr>
<th>{de.document.kind}</th>
<td>{doc.templateKey}</td>
</tr>
<tr>
<th>{de.export.retrievedBy}</th>
<td>{displayName}</td>
</tr>
<tr>
<th>{de.export.asOf}</th>
<td>
{new Date().toLocaleString('de-DE')} · {de.clearance.label}{' '}
{permissions.clearanceLevel}
</td>
</tr>
</tbody>
</table>
</section>
{doc.retractedAt ? (
<p className="print-stamp" style={{ marginBottom: '4mm' }}>
{de.document.statusNames.RETRACTED}
</p>
) : null}
{sections.map((section) => {
const field = section.field
if (!isInputField(field)) {
if (field.type === 'pagebreak') {
return <div key={field.key} style={{ breakAfter: 'page' }} />
}
const text = fillPlaceholders(field.text ?? '', werte)
return field.type === 'heading' ? (
<h3 key={field.key}>{text || field.label}</h3>
) : (
<p key={field.key} style={{ whiteSpace: 'pre-wrap' }}>
{text}
</p>
)
}
if (field.type === 'signature') {
return (
<div key={field.key} style={{ marginTop: '10mm', breakInside: 'avoid' }}>
<div style={{ borderTop: '1pt solid #000', width: '70mm' }} />
<p className="print-note">{fillPlaceholders(field.label, werte)}</p>
</div>
)
}
return (
<div key={field.key} style={{ marginBottom: '3mm', breakInside: 'avoid' }}>
<p className="print-label">{field.label}</p>
{!section.visible ? (
<p style={{ margin: '1mm 0' }}>
<span className="print-redacted">&nbsp;</span>{' '}
<span className="print-note">
{de.export.redactedAt(
section.value.requiredLevel,
levelName.get(section.value.requiredLevel) ?? '',
)}
</span>
</p>
) : (
<p style={{ margin: '1mm 0', whiteSpace: 'pre-wrap' }}>
{String(section.value ?? '-')}
</p>
)}
</div>
)
})}
</main>
)
}