import Link from "next/link"; import { notFound } from "next/navigation"; import { ArrowLeft } from "lucide-react"; import type { Metadata } from "next"; import { DruckenKnopf } from "@/components/druck/drucken-knopf"; import { LaufBeleg } from "@/components/druck/lauf-beleg"; import { sitzungErforderlich } from "@/server/auth/sitzungspflicht"; import { laufZumDrucken } from "@/server/queries/druck"; /** * Die Druckansicht eines Laufs (#015). * * Sie ersetzt im MVP den PDF-Export: hell, ohne Navigation, auf A4 — ein * Ausdruck, den jemand abheftet. Wer ein PDF braucht, druckt in eine Datei. * * **Die Seite prueft ihr Recht selbst.** Sie liegt ausserhalb von `(app)`, also * deckt die Huelle dort sie nicht ab. `lesen` genuegt: die Laufseite zeigt * denselben Lauf mit demselben Recht, und UPN und Abteilung der Person stehen * unter `lesen` auch in den Stammdaten (`/mitarbeiter/[id]`). Die Mandantensicht * wendet das Lesemodell an — ein Lauf eines fremden Kunden ist hier nicht * erreichbar, und „gibt es nicht“ ist dieselbe Antwort wie „darfst du nicht“. * * **Es entsteht kein Passwort.** `{{generiertes_passwort}}` in einem * Vorlagentext — Schritt-Titel oder Befehl — wird zu `PASSWORT_IM_DRUCK`, weil * `laufZumDrucken` mit `{ art: "druck" }` aufloest; erzeugt wird keines, und * gespeichert war ohnehin nie eines. Der Beleg dafuer ist ein Vergleich: zwei * Ausdrucke desselben Laufs sind Zeichen fuer Zeichen gleich * (`tests/druckansicht.test.ts`). * * Genau formuliert, weil die Zusage sonst mehr behauptet, als sie haelt: hat * jemand die **Zeichenfolge** `{{generiertes_passwort}}` von Hand in eine Notiz * oder einen Kommentar geschrieben, steht sie dort auch auf dem Blatt. Das ist * kein Geheimnis, sondern ein Platzhalter, den niemand aufgeloest hat — und der * Ausdruck gibt Notizen und Kommentare wortgetreu wieder, wie es ein Beleg tun * muss (Reviewbefund zu #015). */ export const metadata: Metadata = { // Bewusst ohne Lauftitel: ein Titel aus den Daten verlangte eine zweite // Abfrage in `generateMetadata`, also den ganzen Lauf ein zweites Mal. title: "Lauf drucken", }; /** * Das Seitenformat. * * `@page` laesst sich nicht auf eine Klasse einschraenken und kann deshalb nicht * in globals.css stehen — dort galte es fuer jede Seite, auch fuer die * Etikettenbogen mit ihren eigenen Massen (#014). Also schreibt diese Seite ihre * eigene Regel. * * Links mehr Rand als rechts: das Blatt wird gelocht und abgeheftet, und ein * Locher frisst die ersten Millimeter. */ const DRUCKSTIL = ` @page { size: A4; margin: 14mm 14mm 14mm 20mm; } @media print { /* * Der dunkle Grund kommt vom Wurzel-Layout und liegt **hinter** der hellen * Huelle. Am Bildschirm deckt min-h-screen ihn ab; auf Papier gibt es * keinen Bildschirm, und ein Browser, der den Hintergrund des body * mitdruckt, faerbte das ganze Blatt (#015). */ html, body { background: #ffffff; } /* Ein Schritt gehoert zusammen – Zustand, Wert und Notiz auf einer Seite. */ [data-schritt] { break-inside: avoid; } /* Eine Abschnittsueberschrift allein am Seitenfuss zeigt auf nichts. */ h2 { break-after: avoid; } /* * Voreingestellt ist "economy": dort darf der Browser Farben und Flaechen * anpassen, um Toner zu sparen. "exact" verlangt, sie zu drucken. Betroffen * sind auf diesem Blatt die Haarlinien zwischen den Schritten und der * Akzentpunkt der Wortmarke — sonst ist nichts eingefaerbt. */ * { -webkit-print-color-adjust: exact; print-color-adjust: exact; } } `; const GEDRUCKT_AM = new Intl.DateTimeFormat("de-DE", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }); export default async function LaufDruckSeite({ params }: { params: Promise<{ id: string }> }) { const { id } = await params; const { benutzer } = await sitzungErforderlich("lesen", `/drucken/lauf/${id}`); const daten = await laufZumDrucken(id, { id: benutzer.id, rolle: benutzer.rolle }); // Nicht vorhanden und nicht sichtbar sind dieselbe Antwort. if (!daten) notFound(); return ( <>
{/* * „Gedruckt am“ steht bewusst darauf: ein Lauf in Arbeit kann sich nach * dem Ausdruck weiter aendern, und dann sagt dieses Datum, wie alt das * Blatt in der Mappe ist. Die Kennung steht dabei, weil man mit ihr den * Lauf wiederfindet — der Lauftitel ist nicht eindeutig. */}
); }