import Link from "next/link"; import { Archive, CalendarDays, CheckCircle2, FileDown } from "lucide-react"; import type { LaufStatus } from "@/generated/prisma/enums"; import type { ArchivZeile, TrefferFeld } from "@/server/queries/archiv"; /** * Eine Zeile der Trefferliste (#015). * * Sie zeigt **warum** sie da ist: bei jeder Fundstelle steht, in welchem Feld * der Begriff steht, und der Ausschnitt zeigt ihn im Satz. Ein Treffer ohne * sichtbaren Grund laesst den Suchenden jeden Lauf einzeln aufklappen. * * Karte statt Tabellenzeile: ein Treffer bringt bis zu vier Ausschnitte mit, die * am Handy umbrechen muessen — in einer Tabelle waere davon nichts lesbar. */ const STATUS_TEXT: Readonly> = { OFFEN: "offen", IN_ARBEIT: "in Arbeit", ABGESCHLOSSEN: "abgeschlossen", ARCHIVIERT: "archiviert", }; const FELD_TEXT: Readonly> = { TITEL: "Titel", NOTIZ: "Notiz", WERT: "Wert", KOMMENTAR: "Kommentar", }; const PILLE = "rounded-pille bg-flaeche-hoch px-2.5 py-1 text-xs text-nebentext"; const DATUM = new Intl.DateTimeFormat("de-DE", { day: "2-digit", month: "2-digit", year: "numeric" }); /** * Hebt den Suchbegriff im Ausschnitt hervor. * * Verglichen wird ueber `indexOf` und nicht ueber einen regulaeren Ausdruck: der * Begriff kommt aus der Adresszeile, und `.` oder `(` waeren dort sonst * Sonderzeichen. Ohne Begriff — die Liste zeigt dann ohnehin keine Fundstellen — * bleibt der Text unveraendert. */ function Hervorgehoben({ text, begriff }: { text: string; begriff: string }) { if (!begriff) return <>{text}; const teile: React.ReactNode[] = []; const klein = text.toLowerCase(); const gesucht = begriff.toLowerCase(); let stelle = 0; for (let fund = klein.indexOf(gesucht); fund !== -1; fund = klein.indexOf(gesucht, stelle)) { if (fund > stelle) teile.push(text.slice(stelle, fund)); teile.push( {text.slice(fund, fund + begriff.length)} , ); stelle = fund + begriff.length; } if (stelle < text.length) teile.push(text.slice(stelle)); return <>{teile}; } export function ArchivTreffer({ zeile, begriff, kundenEbene, mitPdf, }: { zeile: ArchivZeile; begriff: string; kundenEbene: boolean; mitPdf: boolean; }) { const angaben = [ `${zeile.vorlagenName} v${zeile.version}`, zeile.geraetName, zeile.mitarbeiterName, zeile.abteilungName, zeile.ticket, zeile.abgeschlossenVon ? `abgeschlossen von ${zeile.abgeschlossenVon}` : null, zeile.abgeschlossenVon === null && zeile.zugewiesenAn ? `zugewiesen an ${zeile.zugewiesenAn}` : null, ].filter((angabe): angabe is string => Boolean(angabe)); return (
  • {zeile.archiviertAm ? (

    {angaben.join(" · ")}

    {/* * **Das Blatt direkt aus der Liste**, ohne Umweg ueber den Lauf: wer im * Archiv sucht, sucht meist genau dieses eine Protokoll — fuer die * Ablage, fuer den Kunden, fuer die Pruefung. Ein `a` und kein `Link`, * weil es ein PDF ist und kein Navigationsziel; sonst erzeugte Next * beim Vorabladen eines je Zeile (#041). */} {mitPdf ? ( ) : null} {zeile.fundstellen.length > 0 ? (
      {zeile.fundstellen.map((stelle, nummer) => (
    • {FELD_TEXT[stelle.feld]}
    • ))}
    ) : null} {zeile.weitereFundstellen > 0 ? (

    {zeile.weitereFundstellen === 1 ? "eine weitere Fundstelle in diesem Lauf" : `${zeile.weitereFundstellen} weitere Fundstellen in diesem Lauf`}

    ) : null}
  • ); }