import { notFound, redirect } from "next/navigation"; import Link from "next/link"; import Image from "next/image"; import { ArrowLeft, Printer } from "lucide-react"; import { aktuelleSitzung } from "@/lib/auth"; import { fuerOrganisation } from "@/lib/db"; import { qrAlsDataUri, qrZiel } from "@/lib/stammdaten/qr"; import { alsDatum, alsDatumZeit, alsEuro } from "@/lib/utils"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { StatusAbzeichen, FristAnzeige } from "@/components/stammdaten/anzeige"; export const metadata = { title: "Gerät" }; /** * Detailseite eines Geräts. * * Serverseitig gerendert: hier wird nichts gefiltert und nichts sortiert, also * braucht es auch keinen Zustand im Browser. Der QR-Code wird auf dem Server * erzeugt und als Data-URI eingebettet — so lädt die Seite nichts nach. */ export default async function GeraetSeite({ params }: { params: Promise<{ id: string }> }) { const sitzung = await aktuelleSitzung(); if (!sitzung) redirect("/anmelden"); const { id } = await params; const daten = await fuerOrganisation(sitzung.organisationId, async (db) => { const geraet = await db.geraet.findUnique({ where: { id }, include: { geraetetyp: true, standort: { select: { id: true, name: true } }, fahrzeug: { select: { id: true, funkrufname: true } }, geraeteraum: { select: { id: true, bezeichnung: true } }, }, }); if (!geraet) return null; const verlauf = await db.auditLog.findMany({ where: { entitaet: "Geraet", entitaetId: id }, orderBy: { zeitpunkt: "desc" }, take: 30, select: { id: true, zeitpunkt: true, aktion: true, beschreibung: true, akteurName: true, }, }); return { geraet, verlauf }; }); if (!daten) notFound(); const { geraet, verlauf } = daten; const qr = await qrAlsDataUri(geraet.inventarnummer, 320); const angaben: Array<[string, React.ReactNode]> = [ ["Inventarnummer", {geraet.inventarnummer}], ["Gerätetyp", geraet.geraetetyp?.name ?? "—"], ["Hersteller", geraet.hersteller ?? "—"], ["Modell", geraet.modell ?? "—"], ["Seriennummer", {geraet.seriennummer ?? "—"}], ["Herstellungsdatum", alsDatum(geraet.herstellungsdatum)], ["Indienststellung", alsDatum(geraet.indienststellung)], [ "Anschaffungspreis", geraet.anschaffungspreis ? alsEuro(Number(geraet.anschaffungspreis)) : "—", ], [ "Prüfintervall", geraet.pruefintervallMonate ? `alle ${geraet.pruefintervallMonate} Monate` : (geraet.geraetetyp?.pruefintervallMonate ? `alle ${geraet.geraetetyp.pruefintervallMonate} Monate (aus dem Gerätetyp)` : "keines hinterlegt"), ], ["Letzte Prüfung", alsDatum(geraet.letztePruefung)], ["Nächste Prüfung", ], ["Lebensdauer bis", ], ["Prüfvorschrift", geraet.geraetetyp?.pruefvorschrift ?? "—"], ]; return (

{geraet.bezeichnung}

{geraet.inventarnummer}

Angaben
{angaben.map(([bezeichnung, wert]) => (
{bezeichnung}
{wert}
))}

Lagerung

{geraet.fahrzeug ? ( <> Verlastet auf{" "} {geraet.fahrzeug.funkrufname} {geraet.geraeteraum && `, ${geraet.geraeteraum.bezeichnung}`} ) : ( `Standort ${geraet.standort?.name ?? "unbekannt"}` )} {geraet.lagerort && ` — ${geraet.lagerort}`}

{geraet.bemerkung && (

Bemerkung

{geraet.bemerkung}

)}
QR-Code Aufkleber im Geräteraum. Wer ihn scannt, landet direkt auf dieser Seite. {/* Data-URI: die Grafik kommt aus dem Server-Rendering, nicht aus einem Nachladen. */} {`QR-Code

{qrZiel(geraet.inventarnummer)}

Verlauf Gerendert aus dem Audit-Log. Es gibt bewusst keine zweite Historie — zwei Quellen könnten auseinanderlaufen. {verlauf.length === 0 ? (

Zu diesem Gerät ist noch nichts protokolliert.

) : (
    {verlauf.map((eintrag) => (
  1. {alsDatumZeit(eintrag.zeitpunkt)} {eintrag.beschreibung ?? eintrag.aktion} {eintrag.akteurName && ( — {eintrag.akteurName} )}
  2. ))}
)}
); }