Schliesst die letzte offene Luecke aus Phase 1. Bis dahin liessen sich Geraete
nur ueber den Import erfassen — fuer einen Bestand richtig, fuer ein einzelnes
nachbeschafftes Geraet umstaendlich.
Neu:
- /geraete/neu und /geraete/<id>/bearbeiten, beide ueber dieselbe Komponente
- Die Lagerung ist ein Umschalter statt zweier Auswahlfelder. Standort und
Fahrzeug schliessen sich aus; so kann der Fehler gar nicht erst entstehen,
den die Datenbank sonst abweisen wuerde. Beim Wechsel des Fahrzeugs wird die
Raumangabe zurueckgesetzt.
- Prueftermin und Lebensdauerende werden beim Tippen vorgerechnet, mit
denselben Funktionen wie auf dem Server. Beim Speichern rechnet der Server
erneut und behaelt das letzte Wort.
- Gibt es nur einen Standort, ist er beim Anlegen vorbelegt.
- Knopf "Neues Geraet" in der Liste, "Bearbeiten" auf der Detailseite
Behoben:
- Das Formular stuerzte beim ersten Rechnen ab. Die Vorgabewerte lagen in einem
"use client"-Modul und wurden von einer Server-Komponente importiert; dort
kommt nur eine Client-Referenz an, kein Objekt, und das Spreaden ergibt lauter
undefined. Die Werte liegen jetzt in geraet-formular-werte.ts ohne
"use client".
- Das Sternchen an Pflichtfeldern wurde von Sprachausgaben mitgelesen
("Bezeichnung Stern"). Es traegt jetzt aria-hidden; dass ein Feld ausgefuellt
werden muss, sagt bereits das required-Attribut.
- Fehlermeldungen in Formularen sind eindeutig ansteuerbar. Next.js haengt
einen eigenen Routen-Ansager mit der Rolle "alert" in jede Seite.
Tests:
- 7 neue Playwright-Tests zum Formular, darunter die Lagerungsregel und die
Fristenvorschau
- Zwei davon laufen nicht unter Touch-Emulation: Chromium nimmt dort keine
zeichenweise Eingabe in Datumsfelder an. Begruendet uebersprungen, die
Rechnung selbst deckt qr-und-fristen.test.ts ab.
- Insgesamt 104 Unit-Tests und 56 E2E-Tests gruen
Anleitung, Changelog, Devlog, Aufwandsschaetzung und Roadmap fortgeschrieben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
197 lines
9.1 KiB
Markdown
197 lines
9.1 KiB
Markdown
# Changelog
|
||
|
||
Alle nennenswerten Änderungen an FLORIAN. Format nach
|
||
[Keep a Changelog](https://keepachangelog.com/de/1.1.0/), Versionierung nach
|
||
[Semantic Versioning](https://semver.org/lang/de/).
|
||
|
||
Die Regeln dazu stehen in [docs/VERSIONIERUNG.md](docs/VERSIONIERUNG.md).
|
||
|
||
## [Unveröffentlicht]
|
||
|
||
Nächster Schritt ist Phase 2 (Wartungs- und Prüfmanagement).
|
||
|
||
### Hinzugefügt
|
||
|
||
- **Formular zum Anlegen und Bearbeiten eines Geräts** (`/geraete/neu` und
|
||
`/geraete/<id>/bearbeiten`). Damit ist die letzte offene Lücke aus Phase 1
|
||
geschlossen: Ein Neuzugang lässt sich einzeln erfassen, ohne den Umweg über
|
||
eine Importdatei.
|
||
- Die Lagerung wird als Umschalter geführt statt als zwei Auswahlfelder, die
|
||
man versehentlich beide füllen kann.
|
||
- Prüftermin und Lebensdauerende werden schon beim Tippen ausgerechnet und
|
||
angezeigt — mit denselben Funktionen, mit denen der Server anschließend
|
||
rechnet.
|
||
- Ist nur ein Standort angelegt, wird er vorbelegt.
|
||
- 7 weitere Playwright-Tests zum Formular (nun 58 statt 44, zwei davon unter
|
||
Touch-Emulation begründet übersprungen).
|
||
|
||
### Behoben
|
||
|
||
- **Das Formular stürzte beim ersten Rechnen ab.** Die Vorgabewerte lagen in
|
||
einem `"use client"`-Modul und wurden von einer Server-Komponente importiert;
|
||
dort kommt nur eine Client-Referenz an, kein Objekt. Das Spreaden davon ergab
|
||
lauter `undefined`. Die Werte liegen jetzt in einem eigenen Modul ohne
|
||
`"use client"`.
|
||
- **Das Sternchen an Pflichtfeldern wurde vorgelesen.** Eine Sprachausgabe
|
||
nannte das Feld „Bezeichnung Stern". Das Sternchen trägt jetzt `aria-hidden`;
|
||
dass ein Feld ausgefüllt werden muss, sagt das `required`-Attribut.
|
||
- Fehlermeldungen in Formularen sind eindeutig ansteuerbar (`data-testid`) —
|
||
Next.js hängt einen eigenen Routen-Ansager mit derselben Rolle in jede Seite.
|
||
|
||
## [0.2.0] — 2026-09-03
|
||
|
||
Phase 1: Stammdaten. Ab hier führt die Wehr ihren Bestand in FLORIAN — mit
|
||
Standorten, Fahrzeugen samt Beladeordnung, Gerätetypen als Vorgabe und
|
||
Geräten, die per QR-Code am Regal auffindbar sind.
|
||
|
||
### Hinzugefügt
|
||
|
||
**Datenmodell**
|
||
|
||
- `Standort` (Gerätehaus, Lager, Außenstelle, Werkstatt) mit PostGIS-Punkt für
|
||
die Karte ab Phase 6.
|
||
- `Geraeteraum` — die Fächer am Fahrzeug (G1 bis G6, Heck, Dach,
|
||
Mannschaftsraum), in Beladereihenfolge sortierbar.
|
||
- `Geraetetyp` als Vorgabe für Prüfintervall, Prüfvorschrift und Lebensdauer.
|
||
Ein einzelnes Gerät darf davon abweichen; sonst gilt der Typ.
|
||
- `Qualifikationstyp` und `Qualifikation` mit Gültigkeitsdauer und eigener
|
||
Vorwarnzeit je Typ.
|
||
- `Fahrzeug` erweitert um Fahrgestellnummer, DIN-Typkurzzeichen, Erstzulassung,
|
||
Sitzplätze, Tankinhalt, Gesamtmasse und die UVV-Frist.
|
||
- `Geraet` erweitert um Gerätetyp, Lebensdauerkategorie, errechnetes
|
||
Lebensdauerende, Anschaffungspreis und die Lagerung.
|
||
|
||
**Regeln in der Datenbank**
|
||
|
||
- Ein Gerät steht entweder an einem Standort oder ist auf einem Fahrzeug
|
||
verlastet — eine Prüfbedingung setzt das durch, ein Trigger stellt zusätzlich
|
||
sicher, dass ein angegebener Geräteraum auch zum angegebenen Fahrzeug gehört.
|
||
- Row Level Security auf allen neuen Tabellen.
|
||
- Trigram-Indizes für die Volltextsuche über Geräte, Fahrzeuge und Personen.
|
||
|
||
**Funktionen**
|
||
|
||
- Geräteliste mit Volltextsuche, Statusfilter, serverseitiger Sortierung,
|
||
Blättern und Spaltenauswahl. Die Spaltenauswahl bleibt je Browser gespeichert.
|
||
- Massenbearbeitung: Status mehrerer Geräte auf einmal setzen, Geräte umlagern.
|
||
Jede Änderung erzeugt einen eigenen Protokolleintrag am jeweiligen Gerät.
|
||
- QR-Code je Gerät. Der Code zeigt auf `/g/<inventarnummer>` und führt nach der
|
||
Anmeldung direkt zur Gerätedetailseite.
|
||
- Etikettenbogen als PDF über Gotenberg: DIN A4 mit 65 Etiketten
|
||
(38,1 × 21,2 mm). Der Druck kann an einer beliebigen Position beginnen, damit
|
||
angebrochene Bögen weiterverwendet werden können.
|
||
- QR-Scan über die Kamera im Browser, mit Eingabefeld als Rückfallweg.
|
||
- Import aus CSV und Excel: Spalten werden nach den Überschriften automatisch
|
||
zugeordnet, die Vorschau zeigt Zeile für Zeile, was übernommen würde und was
|
||
daran nicht stimmt. Geschrieben wird erst nach Bestätigung.
|
||
- Gerätehistorie und Fahrzeughistorie, gerendert aus dem Audit-Log.
|
||
- Qualifikationen mit Ablaufwarnung, gerechnet mit der Vorwarnzeit des
|
||
jeweiligen Typs.
|
||
- Fahrzeugdetail zeigt die Beladung nach Geräteräumen statt alphabetisch.
|
||
|
||
**Daten und Tests**
|
||
|
||
- Der Seed legt jetzt zusätzlich 3 Standorte, 20 Geräteräume, 14 Gerätetypen
|
||
mit echten Prüfvorgaben (DGUV, DIN, vfdb), 49 Geräte und 107 Qualifikationen
|
||
an. Fristen sind bewusst gestreut, damit die Ampeln etwas zeigen.
|
||
- 104 Vitest-Tests (vorher 32), davon 40 zum Import und 32 zu QR und Fristen.
|
||
- 44 Playwright-Tests (vorher 10) über Desktop und Tablet.
|
||
|
||
### Geändert
|
||
|
||
- **Fristenrechnung auf UTC-Kalenderarithmetik umgestellt.** Vorher wurde über
|
||
`startOfDay` aus date-fns gerechnet, das die Zeitzone des Servers verwendet.
|
||
Ein Prüfdatum kommt aus der Datenbank als UTC-Mitternacht und wurde dabei auf
|
||
den Vortag zurückgeschoben — Fristen wären einen Tag zu früh fällig geworden.
|
||
- `CardTitle` rendert eine echte Überschrift statt eines `div`. Wer mit einer
|
||
Sprachausgabe arbeitet, springt von Überschrift zu Überschrift; Karten
|
||
tauchten in dieser Navigation vorher gar nicht auf.
|
||
- Die Entwicklungsanzeige von Next.js ist abgeschaltet, damit sie nicht in den
|
||
Bildern der Anleitung mitläuft.
|
||
|
||
### Sicherheit
|
||
|
||
- Der Etikettendruck wird protokolliert (`EXPORTIERT`) — damit ist
|
||
nachvollziehbar, wann welche Aufkleber erzeugt wurden.
|
||
- Der Import übernimmt keine Werte, die der Browser geschickt hat: die Datei
|
||
wird beim Übernehmen erneut serverseitig eingelesen und geprüft. Sonst ließe
|
||
sich die Vorschau umgehen.
|
||
|
||
## [0.1.0] — 2026-09-03
|
||
|
||
Phase 0: das Fundament. Ab hier steht ein lauffähiger Stack mit
|
||
Mandantentrennung, Anmeldung, Dateiablage und lückenloser Protokollierung.
|
||
|
||
### Hinzugefügt
|
||
|
||
**Betrieb**
|
||
|
||
- `docker-compose.yml` mit Traefik, App, Worker, PostgreSQL 17 + PostGIS,
|
||
Redis, MinIO und Gotenberg.
|
||
- `docker-compose.override.yml` für die Entwicklung: ohne Traefik, mit
|
||
Mailpit als SMTP-Auffangkorb und Adminer für den Blick in die Datenbank.
|
||
Host-Ports bewusst abseits der Standardwerte (3100, 5442, 6389, 9010/9011),
|
||
damit FLORIAN neben anderen lokalen Stacks laufen kann.
|
||
- Mehrstufiges `docker/Dockerfile`: eigener Benutzer `florian` (UID 1001,
|
||
kein root), `tini` als PID 1, Healthcheck gegen `/api/health`,
|
||
Standalone-Build von Next.js.
|
||
- `docker/entrypoint.sh` wendet ausstehende Migrationen an — nur im
|
||
App-Container, nicht im Worker.
|
||
- `docker/postgres/10-init-app-role.sh` legt beim ersten Start die Rolle
|
||
`florian_app` (`NOBYPASSRLS`) samt Vorgabeprivilegien an.
|
||
- `.env.example` mit einem Kommentar zu jeder einzelnen Variable.
|
||
|
||
**Datenmodell**
|
||
|
||
- Prisma-Schema mit `Organisation`, `Person`, `Rolle`, `Berechtigung`,
|
||
`RolleBerechtigung`, `PersonRolle`, `AusweisToken`, `AuditLog`, `Datei`.
|
||
- `Fahrzeug` und `Geraet` bereits schmal angelegt, damit der Seed eine
|
||
realistische Wehr aufbauen kann. Phase 1 erweitert beide.
|
||
- Migration `20260902000100_rls_policies`: Row Level Security auf allen
|
||
mandantenpflichtigen Tabellen, Funktion `aktuelle_organisation()`,
|
||
Append-only-Trigger auf `audit_log`.
|
||
- Migration `20260902000200_audit_bereinigung`: `UPDATE` auf `audit_log`
|
||
bleibt dauerhaft gesperrt, `DELETE` ist nur im gekennzeichneten
|
||
Aufbewahrungslauf möglich.
|
||
|
||
**Anwendungskern**
|
||
|
||
- `lib/env.ts` — Zod-Validierung der gesamten Umgebung beim Start.
|
||
- `lib/db.ts` — getrennte Verbindungen für Anwendung (RLS aktiv) und System
|
||
(RLS-frei), `fuerOrganisation()` als einziger Weg zu Fachdaten.
|
||
- `lib/authz.ts` — 9 Rollen, 38 Einzelrechte, zentrale Prüffunktionen.
|
||
- `lib/storage.ts` — MinIO, presignte Up- und Downloads, SHA-256-Prüfsumme.
|
||
- `lib/audit.ts` — Protokolleinträge mit Vorher/Nachher-Vergleich, entfernt
|
||
Geheimnisse vor dem Schreiben.
|
||
- `lib/auth.ts` — Auth.js mit OIDC gegen authentik, lokalem Passwort-Login
|
||
und Terminal-Anmeldung über PIN oder RFID.
|
||
- `lib/warteschlange.ts` und `src/worker/` — BullMQ-Gerüst mit den
|
||
Auftragsarten für Fristen, Erinnerungen, PDF und Aufbewahrung.
|
||
- `lib/integrations/` — Mail, Telegram und LLM hinter abschaltbaren
|
||
Schnittstellen.
|
||
- tRPC v11 mit den Prozedurarten `oeffentlich`, `angemeldet` und
|
||
`mitRecht(...)`.
|
||
|
||
**Oberfläche**
|
||
|
||
- Basis-Layout mit Seitenleiste, heller und dunkler Darstellung
|
||
(Systemvorgabe als Standard) und durchgängig deutschen Texten.
|
||
- Anmeldeseite, die genau die eingerichteten Anmeldewege zeigt.
|
||
- Übersicht mit Kennzahlen und dem Sicherheitshinweis zur analogen
|
||
Atemschutzüberwachung.
|
||
- Farbwelt in OKLCH mit festen Statusfarben für Gerätezustände.
|
||
|
||
**Daten und Tests**
|
||
|
||
- Seed mit einer vollständigen Beispielwehr: 15 Personen mit Rollen und
|
||
Terminal-Ausweisen, 3 Fahrzeuge, 40 Geräte mit gemischten Prüffristen.
|
||
- Vitest: 32 Tests, darunter acht, die die Mandantentrennung gegen eine echte
|
||
Datenbank prüfen.
|
||
- Playwright: 10 Tests über Desktop und Tablet.
|
||
- `npm run screenshots` erzeugt die Bilder für die Anleitung.
|
||
|
||
**Dokumentation**
|
||
|
||
- README mit Schnellstart in sechs Zeilen.
|
||
- Anleitung, Roadmap, Aufwandsschätzung, Devlog und Versionierungsregeln.
|