feat(protokoll): Protokoll ansehen, mit Filtern und Mandantensicht (#45)
This commit was merged in pull request #45.
This commit is contained in:
@@ -14,6 +14,7 @@ const REITER = [
|
||||
{ pfad: "/admin/benutzer", beschriftung: "Benutzer" },
|
||||
{ pfad: "/admin/abteilungen", beschriftung: "Abteilungen" },
|
||||
{ pfad: "/admin/schnellstart", beschriftung: "Schnellstart" },
|
||||
{ pfad: "/admin/protokoll", beschriftung: "Protokoll" },
|
||||
/*
|
||||
* Das Verzeichnis gibt es nur im Modus INTERN: dort hat das Haus **ein** AD,
|
||||
* aus dem seine Leute kommen. Im Modus DIENSTLEISTUNG waere es ein LDAP zu
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import Link from "next/link";
|
||||
import { ScrollText } from "lucide-react";
|
||||
import { ProtokollFilter, type ProtokollFilterfelder } from "@/components/protokoll/protokoll-filter";
|
||||
import { ProtokollListe } from "@/components/protokoll/protokoll-liste";
|
||||
import { sitzungErforderlich } from "@/server/auth/sitzungspflicht";
|
||||
import { kundenEbeneSichtbar } from "@/server/mandant";
|
||||
import { PRO_SEITE, protokollSeite, protokollUrheber } from "@/server/queries/protokoll";
|
||||
|
||||
/**
|
||||
* Das Protokoll ansehen (#023).
|
||||
*
|
||||
* Geschrieben wurde es seit #006 — Rollenänderungen, veröffentlichte Vorlagen,
|
||||
* archivierte und zurückgeholte Läufe, der Moduswechsel. Ansehen konnte es
|
||||
* niemand: wer wissen wollte, wer wann eine Rolle geändert hat, brauchte `psql`.
|
||||
*
|
||||
* `verwaltung` und nicht `lesen`: das Protokoll sagt, wer was getan hat, und ist
|
||||
* damit selbst eine Auskunft über Menschen.
|
||||
*
|
||||
* **Die Seite prüft selbst.** Das Admin-Layout darüber ist das zweite Netz und
|
||||
* nicht das erste (siehe CLAUDE.md).
|
||||
*/
|
||||
|
||||
export default async function ProtokollSeiteAnsicht({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
||||
}) {
|
||||
const { benutzer } = await sitzungErforderlich("verwaltung", "/admin/protokoll");
|
||||
const parameter = await searchParams;
|
||||
const einzeln = (name: string): string =>
|
||||
(Array.isArray(parameter[name]) ? parameter[name][0] : parameter[name])?.trim() ?? "";
|
||||
|
||||
const felder: ProtokollFilterfelder = {
|
||||
suche: einzeln("suche"),
|
||||
urheber: einzeln("urheber"),
|
||||
von: einzeln("von"),
|
||||
bis: einzeln("bis"),
|
||||
};
|
||||
const gewuenschteSeite = Number.parseInt(einzeln("seite"), 10);
|
||||
|
||||
const [ergebnis, urheber, kundenEbene] = await Promise.all([
|
||||
protokollSeite(
|
||||
{
|
||||
suche: felder.suche,
|
||||
...(felder.urheber ? { urheberId: felder.urheber } : {}),
|
||||
...(felder.von ? { vonDatum: felder.von } : {}),
|
||||
...(felder.bis ? { bisDatum: felder.bis } : {}),
|
||||
seite: Number.isNaN(gewuenschteSeite) ? 1 : gewuenschteSeite,
|
||||
},
|
||||
benutzer,
|
||||
),
|
||||
protokollUrheber(benutzer),
|
||||
kundenEbeneSichtbar(),
|
||||
]);
|
||||
|
||||
/** Dieselben Filter mitnehmen, nur die Seite wechseln. */
|
||||
function blaetternZu(seite: number): string {
|
||||
const suchteil = new URLSearchParams();
|
||||
if (felder.suche) suchteil.set("suche", felder.suche);
|
||||
if (felder.urheber) suchteil.set("urheber", felder.urheber);
|
||||
if (felder.von) suchteil.set("von", felder.von);
|
||||
if (felder.bis) suchteil.set("bis", felder.bis);
|
||||
suchteil.set("seite", String(seite));
|
||||
return `/admin/protokoll?${suchteil.toString()}`;
|
||||
}
|
||||
|
||||
const erster = (ergebnis.seite - 1) * PRO_SEITE + 1;
|
||||
const letzter = Math.min(ergebnis.seite * PRO_SEITE, ergebnis.gesamt);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="flex items-center gap-2 text-2xl font-bold">
|
||||
<ScrollText className="size-6 text-nebentext" strokeWidth={1.75} aria-hidden="true" />
|
||||
Protokoll
|
||||
</h1>
|
||||
<p className="mt-2 max-w-prose text-nebentext">
|
||||
Wer hat was wann getan. Festgehalten wird, was nachvollziehbar bleiben muss — Rollenänderungen,
|
||||
veröffentlichte Vorlagen, archivierte und zurückgeholte Läufe, Verzeichnisquellen, der Moduswechsel.
|
||||
Einträge werden nie geändert und nie gelöscht.
|
||||
</p>
|
||||
|
||||
<ProtokollFilter felder={felder} urheber={urheber} />
|
||||
|
||||
<p className="mt-4 text-sm text-nebentext">
|
||||
{ergebnis.gesamt === 0 ? "Keine Einträge." : `${erster}–${letzter} von ${ergebnis.gesamt} Einträgen`}
|
||||
</p>
|
||||
|
||||
<ProtokollListe zeilen={ergebnis.zeilen} kundenEbene={kundenEbene} />
|
||||
|
||||
{ergebnis.seiten > 1 ? (
|
||||
<nav aria-label="Seiten" className="mt-6 flex flex-wrap items-center gap-3">
|
||||
{ergebnis.seite > 1 ? (
|
||||
<Link
|
||||
href={blaetternZu(ergebnis.seite - 1)}
|
||||
className="flex min-h-11 items-center rounded-pille border border-linie px-4 text-sm text-nebentext hover:bg-flaeche-hoch hover:text-text md:min-h-10"
|
||||
>
|
||||
zurück
|
||||
</Link>
|
||||
) : null}
|
||||
<span className="text-sm text-nebentext">
|
||||
Seite {ergebnis.seite} von {ergebnis.seiten}
|
||||
</span>
|
||||
{ergebnis.seite < ergebnis.seiten ? (
|
||||
<Link
|
||||
href={blaetternZu(ergebnis.seite + 1)}
|
||||
className="flex min-h-11 items-center rounded-pille border border-linie px-4 text-sm text-nebentext hover:bg-flaeche-hoch hover:text-text md:min-h-10"
|
||||
>
|
||||
weiter
|
||||
</Link>
|
||||
) : null}
|
||||
</nav>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user