import { Archive } from "lucide-react"; import { ArchivFilter, type ArchivFilterfelder } from "@/components/archiv/archiv-filter"; import { ArchivTreffer } from "@/components/archiv/archiv-treffer"; import { sitzungErforderlich } from "@/server/auth/sitzungspflicht"; import { kundenEbeneSichtbar } from "@/server/mandant"; import { ARCHIV_HOECHSTENS, SUCHE_MINDESTZEICHEN, archivFilterwerte, archivSuche, datumAusText, type ArchivFilter as ArchivFilterwerte, } from "@/server/queries/archiv"; /** * Das Archiv (#015). * * Das Gedaechtnis der Anwendung: durchsucht werden abgeschlossene und * archivierte Laeufe, und zwar in `Lauf.titel`, `Schrittergebnis.notiz`, * `Schrittergebnis.wert` und `Kommentar.rumpf`. Was genau dabei geschieht, steht * in `src/server/queries/archiv.ts`; diese Seite liest die Adresszeile, prueft * ihr Recht und stellt dar. * * **Sie prueft selbst.** Die Huelle unter `(app)/layout.tsx` ist das zweite Netz * und nicht das erste: Next rendert Layouts bei Navigation innerhalb desselben * Layouts nicht neu (#007). */ export default async function ArchivSeite({ searchParams, }: { searchParams: Promise>; }) { const { benutzer } = await sitzungErforderlich("lesen", "/archiv"); const parameter = await searchParams; const einzeln = (name: string): string => (Array.isArray(parameter[name]) ? parameter[name][0] : parameter[name])?.trim() ?? ""; const felder: ArchivFilterfelder = { suche: einzeln("suche"), vorlage: einzeln("vorlage"), mitarbeiter: einzeln("mitarbeiter"), abteilung: einzeln("abteilung"), geraet: einzeln("geraet"), bearbeiter: einzeln("bearbeiter"), kunde: einzeln("kunde"), von: einzeln("von"), bis: einzeln("bis"), laufende: einzeln("laufende") === "1", }; /* * Ein vertippter Parameter filtert nicht, statt eine Fehlerseite zu zeigen – * dieselbe Entscheidung wie bei der Geraeteliste (#011). Eine erfundene * Kennung bleibt dagegen stehen: sie ist kein Formfehler, sondern eine Suche, * die nichts findet. Ueber die Mandantengrenze kommt sie nicht (siehe * archivSuche). */ const von = datumAusText(felder.von); const bis = datumAusText(felder.bis); const filter: ArchivFilterwerte = { ...(felder.suche ? { suche: felder.suche } : {}), ...(felder.vorlage ? { vorlageId: felder.vorlage } : {}), ...(felder.mitarbeiter ? { mitarbeiterId: felder.mitarbeiter } : {}), ...(felder.abteilung ? { abteilungId: felder.abteilung } : {}), ...(felder.geraet ? { geraetId: felder.geraet } : {}), ...(felder.bearbeiter ? { bearbeiterId: felder.bearbeiter } : {}), ...(felder.kunde ? { kundeId: felder.kunde } : {}), ...(von ? { von } : {}), ...(bis ? { bis } : {}), ...(felder.laufende ? { auchLaufende: true } : {}), }; const [ergebnis, werte, kundenEbene] = await Promise.all([ archivSuche(benutzer, filter), archivFilterwerte(benutzer), kundenEbeneSichtbar(), ]); // Hervorgehoben wird nur, wonach auch gesucht wurde: ein verworfener Begriff // hat keine Fundstellen und darf keine markieren. const begriff = ergebnis.sucheZuKurz ? "" : felder.suche; return ( <>

Durchsucht werden abgeschlossene und archivierte Läufe — ihr Titel, die Notizen und Werte der Schritte und die Kommentare. Ein archivierter Lauf ändert sich nicht mehr; zurückholen kann ihn nur ein Administrator.

{ergebnis.sucheZuKurz ? (

„{felder.suche}“ ist als Suchbegriff zu kurz — es braucht mindestens {SUCHE_MINDESTZEICHEN} Zeichen. Gezeigt werden deshalb die Treffer der übrigen Filter.

) : null} {ergebnis.zeilen.length === 0 ? (

Kein Lauf passt dazu. Laufende Läufe stehen erst mit dem Häkchen „auch offene und laufende Läufe“ dabei — bis zum Abschluss gehören sie in die Laufliste.

) : ( <>

{ergebnis.zeilen.length === 1 ? "Ein Lauf gefunden" : `${ergebnis.zeilen.length} Läufe gefunden`} {/* Eine gekappte Liste sagt das – sonst fehlte ein Lauf stillschweigend. */} {ergebnis.gekappt ? `. Es sind mehr als ${ARCHIV_HOECHSTENS}; gezeigt werden die zuletzt abgelegten. Ein engerer Zeitraum oder ein genauerer Begriff holt die übrigen hervor.` : "."}

)} ); }