# Konventionen im Betreuungs-Cockpit Verbindlich für jeden, der Code in dieses Projekt schreibt. Wer davon abweicht, erzeugt Reibung an der nächsten Schnittstelle. ## Sprache - **Alles auf Deutsch**: Bezeichner, Kommentare, Commit-Nachrichten, Oberflächentexte, Fehlermeldungen. Ausnahmen sind Framework-Vorgaben (`page.tsx`, `layout.tsx`, `default`, `props`) und etablierte Fachbegriffe ohne gute Übersetzung (`Token`, `Hash`, `Cookie`). - Umlaute in **Oberflächentexten**: ja, korrekt geschrieben. Umlaute in **Bezeichnern und Dateinamen**: nein, umschrieben (`vermoegen`, `naechsterSchritt`, `zurueck`). - Du-Ansprache. Keine Juristensprache ohne Erklärung — steht ein Paragraph im Text, gehört eine Erklärung daneben. ## Verzeichnisse ``` src/ app/ (auth)/ Anmeldung, Ersteinrichtung, Zweitfaktor (app)/ Alle Fachseiten, hinter Anmeldung + 2FA api/ components/ ui/ Basisbausteine (Button, Input, Card, …) / Fachkomponenten je Modul lib/ crypto/ Feldverschlüsselung, Hashing, TOTP auth/ Sitzung, Rollen, Rate-Limiting validierung/ Zod-Schemata je Entität / Fachlogik ohne React server/ actions/ Server Actions ("use server") prisma/ tests/ Vitest (Logik) e2e/ Playwright (kritische Flows) ``` Ein Modul bringt mit: Seite(n) unter `app/(app)//`, Fachkomponenten unter `components//`, Zod-Schema unter `lib/validierung/.ts`, Server Actions unter `server/actions/.ts`, reine Logik unter `lib//`. ## Datenzugriff ```ts import { db, nichtGeloescht } from '@/lib/db' ``` - **Jede** Abfrage auf Fachdaten filtert `...nichtGeloescht` (Papierkorb). - **Jede** Abfrage ist auf `betreuteId` eingegrenzt. - Zugriff wird immer über `erfordereZugang(betreuteId, modul, art)` geprüft — auch in Server Actions, nicht nur in der Seite. Nie nur im UI ausblenden. ```ts const { betreute, benutzer, darfSchreiben } = await erfordereZugang(id, 'FRISTEN', 'schreiben') ``` ## Geldbeträge Immer `BigInt` in **Cent**, Feldname endet auf `Cent`. Anzeige über `formatCent()`. Nie `number` für Beträge, nie `Decimal`, nie Fließkomma. Auf dem Weg zu Client Components über `centZuZahl()`. ## Datum - Fristen, Stichtage, Bescheiddaten: `@db.Date`, ohne Uhrzeit. - Zeitstempel (`erstelltAm`, Protokolle): volles `DateTime`. - Anzeige über `formatDatum()` / `formatDatumZeit()` aus `@/lib/utils`. - Datumsrechnung immer über `date-fns`, nie mit `setDate` von Hand. ## Verschlüsselte Felder Feldname endet auf `Verschluesselt`. Nie direkt schreiben: ```ts import { sealen, oeffnen } from '@/lib/crypto/feld' const daten = { ...rest, ...sealen('Vorgang', { beschreibung: eingabe.beschreibung }) } const offen = oeffnen('Vorgang', datensatzAusDb) // offen.beschreibung ist Klartext ``` Neues verschlüsseltes Feld? Erst in `VERSCHLUESSELTE_FELDER` in `src/lib/crypto/feld.ts` eintragen, sonst wirft `sealen()`. Verschlüsselte Felder lassen sich **nicht** filtern oder sortieren. ## Audit-Log Jede schreibende Aktion protokolliert: ```ts await protokollieren({ aktion: 'GEAENDERT', entitaet: 'Frist', entitaetId: frist.id, bezeichnung: frist.titel, betreuteId, userId: benutzer.id, userEmail: benutzer.email, userName: benutzer.name, aenderung: aenderungErmitteln(vorher, nachher), }) ``` ## Server Actions - Immer `'use server'` oben, immer Zod-Validierung, immer Zugriffsprüfung. - Rückgabe für Formulare: ein Zustandsobjekt `{ fehler?: string; felder?: Record; werte?: … }`, benutzt mit `useActionState`. Keine geworfenen Fehler ans Formular. - Nach dem Schreiben `revalidatePath()` auf die betroffene Seite. - Löschen heißt: `geloeschtAm` setzen. Endgültiges Löschen nur im Papierkorb und nur nach `erfordereFrischeAnmeldung()`. ## Oberfläche - Basisbausteine aus `@/components/ui/*`. Formularfelder immer in `` einwickeln — das verdrahtet Label, Erklärung und Fehler barrierefrei. - Jede Seite beginnt mit ``. - Leere Listen bekommen `` mit einem konkreten nächsten Schritt, nie nur "keine Daten". - Steht ein Paragraph auf der Seite, steht `` darunter. - Mindestens 44 px Touchziele, Schrift nie unter 16 px in Eingabefeldern. - Farbe ist nie die einzige Information — Ampeln immer mit Text. - Tabellen scrollen im eigenen Rahmen, nie die Seite. - Keine externen Fonts, keine CDNs, keine Tracker. Bilder als Data-URI oder aus dem eigenen Objektspeicher. ## Statusfarben (Ampel) | Lage | Variante | |---|---| | überfällig | `rot` | | diese Woche fällig | `orange` | | dieser Monat fällig | `gelb` | | erledigt / im Plan | `gruen` | ## Tests - Fachlogik (Fristberechnung, Beträge, Bewertungen): Vitest unter `tests/`. - Kritische Abläufe: Playwright unter `e2e/`. - Ein Test beschreibt auf Deutsch, was fachlich gelten soll — nicht, welche Funktion aufgerufen wird. ## Commits - Deutsch, Imperativ, erste Zeile ≤ 72 Zeichen. - Körper erklärt **warum**, nicht was. - Trailer am Ende, ohne Leerzeile dazwischen: ``` Etappe: 3 Phase: Papier ``` Diese Trailer wertet `npm run protokoll` aus — daraus entsteht das Bauprotokoll mit den Zeiten je Etappe.