feat: Oberflaechenbausteine, Anwendungsrahmen und Konventionen

Basisbausteine im shadcn/ui-Stil, aber mit drei bewussten Abweichungen:

- Bedienelemente sind 44px hoch statt 36px. Ich bediene die App oft am
  Handy im Heim oder auf dem Amt; kleiner ist dort nicht treffsicher.
- Auswahlfelder benutzen das native <select>. Android und iOS oeffnen
  dafuer ihre eigene Liste - schneller als jeder Nachbau, und mit
  Screenreader und Tastatur funktioniert es ohne Zutun.
- Eingabefelder haben mindestens 16px Schrift, sonst zoomt iOS-Safari
  beim Hineintippen das Formular heran.

<Feld> verdrahtet Beschriftung, Erklaerung und Fehler ueber
aria-describedby. Der Erklaerungstext ist kein Beiwerk: die Anwendung
benutzt Begriffe aus dem Betreuungsrecht, die niemand nachschlagen soll.

<Rechtshinweis> ist der stehende Vermerk, dass hier keine Rechtsberatung
stattfindet. Er steht an jeder Stelle mit Paragraphenbezug.

Der Anwendungsrahmen fuehrt drei Bedingungen zusammen, ohne die niemand
an Fachdaten kommt: angemeldet, zweiter Faktor eingerichtet, mindestens
eine betreute Person zugeordnet. Die Navigation zeigt Zaehler fuer
ueberfaellige Fristen und neuen Posteingang - die Frage "wo muss ich
hin?" wird schon im Menue beantwortet.

Das Farbschema wird per Inline-Skript mit CSP-Nonce gesetzt, bevor die
Seite gezeichnet wird - sonst blitzt beim Laden das helle Thema auf.

docs/KONVENTIONEN.md haelt fest, was in allen weiteren Etappen gilt:
Verzeichnisaufbau, Cent-Betraege, Umgang mit verschluesselten Feldern,
Zugriffspruefung, Audit-Pflicht, Ampelfarben, Commit-Trailer.

Etappe: 1
Phase: Fundament
This commit is contained in:
Florian Wilde
2026-08-22 01:54:56 +02:00
parent bd1c7b801f
commit dcd3a8ce71
30 changed files with 2151 additions and 12 deletions
+17 -12
View File
@@ -1,4 +1,4 @@
import NextAuth from 'next-auth'
import NextAuth, { CredentialsSignin } from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import type { Rolle } from '@prisma/client'
import { db } from '@/lib/db'
@@ -25,18 +25,23 @@ import { protokollieren } from '@/lib/audit'
* Benutzer nur bis zur Einrichtungsseite - erzwungen im App-Layout.
*/
export class AnmeldeFehler extends Error {
constructor(
public readonly grund:
| 'daten-falsch'
| 'gesperrt'
| 'konto-inaktiv'
| 'zweiter-faktor-fehlt'
| 'code-falsch',
public readonly nochSekunden?: number,
) {
export type AnmeldeGrund =
| 'daten-falsch'
| 'gesperrt'
| 'konto-inaktiv'
| 'code-falsch'
/**
* Auth.js transportiert Fehler aus authorize() nur ueber das Feld "code".
* Deshalb erbt der Fehler von CredentialsSignin und kodiert eine etwaige
* Restsperrzeit in den Code hinein: "gesperrt:120".
*/
export class AnmeldeFehler extends CredentialsSignin {
code: string
constructor(grund: AnmeldeGrund, nochSekunden?: number) {
super(grund)
this.name = 'AnmeldeFehler'
this.code = nochSekunden ? `${grund}:${nochSekunden}` : grund
}
}