diff --git a/docs/schema-wuensche/fristen.md b/docs/schema-wuensche/fristen.md new file mode 100644 index 0000000..bcfa2af --- /dev/null +++ b/docs/schema-wuensche/fristen.md @@ -0,0 +1,86 @@ +# Schema-Wünsche aus dem Modul „Fristen & Kalender" + +Das Modul ist vollständig mit dem bestehenden `model Frist` umgesetzt — +nichts davon blockiert. Die folgenden Punkte sind Verbesserungen, die +jeweils eine Behelfslösung ersetzen würden. `prisma/schema.prisma` wurde +nicht angefasst. + +--- + +## 1. Getrennte Felder für Erledigungsnotiz, Verschiebungs- und Entfallgrund + +**Behelf heute:** Beim Erledigen, Verschieben und Entfallenlassen hängen die +Server Actions den Grund als Textzeile an `beschreibung` an +(`"Verschoben vom 14.03.2026: Unterlagen fehlten noch."`). + +**Warum das stört:** `beschreibung` ist der Text, der sagt, *was zu tun +ist* — er wandert unverändert in den ICS-Feed und in die Vorwarnungen. Mit +angehängter Historie wird er über die Jahre unlesbar, und die Ableitung der +Pflichtfristen überschreibt ihn bei offenen Fristen ohnehin wieder (dann +ist die Notiz weg). Ausserdem lässt sich nicht auswerten, wie oft eine +Frist verschoben wurde — genau die Zahl, die im Jahresbericht auffällt. + +**Wunsch:** + +```prisma +/// Freitext beim Abhaken: wie wurde erledigt, was ging raus, an wen. +erledigtNotiz String? +/// Grund der letzten Verschiebung bzw. des Entfallens. +statusGrund String? +/// Wie oft wurde diese Frist schon verschoben? Fuer die Selbstkontrolle. +verschobenAnzahl Int @default(0) +``` + +--- + +## 2. Wer hat die Frist erledigt? + +**Behelf heute:** Steht nur im Audit-Log (`AuditLog.userId`). + +**Warum das stört:** Für die Anzeige („erledigt am 14.03. von Florian +Wilde") müsste die Liste für jede Frist im Audit-Log nachschlagen. Bei +mehreren Betreuern oder einem mitlesenden Geschwisterteil ist das die erste +Frage, die gestellt wird. + +**Wunsch:** analog zu `Vorgang.angelegtVonId` + +```prisma +erledigtVonId String? +erledigtVon User? @relation("FristErledigtVon", fields: [erledigtVonId], references: [id], onDelete: SetNull) +``` + +--- + +## 3. `vorgaengerFristId` ist `@unique` — die Kette ist einspurig + +**Behelf heute:** Funktioniert für den Normalfall (eine erledigte Frist → +genau eine Nachfolgefrist). + +**Warum das stört:** Beim Verschieben *und* bei der Wiederholung zeigt die +neue Frist auf dieselbe Vorgängerin. Wird eine verschobene Frist später +irrtümlich noch einmal verschoben, läuft das in eine Verletzung der +Eindeutigkeit statt in eine verständliche Meldung. Ein einfacher `@@index` +statt `@unique` würde reichen; die Kette bliebe über `vorgaengerFristId` +weiterhin rückwärts lesbar. + +--- + +## 4. Kein Feld für den Kalender-Token — bewusst so + +Zur Klarstellung, damit niemand später eines nachrüstet: Der abonnierbare +ICS-Feed kommt **ohne** Schemafeld aus. Die Adresse trägt eine Signatur +`base64url(..)`, berechnet mit +`AUTH_SECRET` (siehe `src/lib/fristen/ics.ts`). + +Vorteil: Es liegt kein dauerhaft gültiges Geheimnis in der Datenbank, das +bei einem Datenbank-Leck mitgelesen werden könnte. Die Route prüft bei +jedem Abruf zusätzlich, ob der Benutzer noch aktiv ist und noch Lesezugriff +auf das Modul `FRISTEN` hat — ein entzogener Zugang leert den Kalender +sofort. + +Nachteil, der in Kauf genommen wird: Eine einzelne Adresse lässt sich nicht +gezielt widerrufen, nur durch Wechsel von `AUTH_SECRET` (was alle +Anmeldungen beendet). Falls das je gebraucht wird, wäre ein +`kalenderGeneration Int @default(0)` an `BetreuungsZugang` die kleinste +Lösung: Sie geht in den HMAC ein, und ein Hochzählen entwertet nur die +Adressen dieses einen Zugangs. diff --git a/src/components/fristen/erledigen-knopf.tsx b/src/components/fristen/erledigen-knopf.tsx index 63ad53a..8fdaed2 100644 --- a/src/components/fristen/erledigen-knopf.tsx +++ b/src/components/fristen/erledigen-knopf.tsx @@ -52,7 +52,7 @@ export function ErledigenKnopf({ size={groesse} onClick={abhaken} disabled={laeuft} - aria-label={`Frist „${titel}" als erledigt abhaken`} + aria-label={`Frist „${titel}“ als erledigt abhaken`} > {laeuft ? (