From 1744403620c04303cfca893d3ae90e38c931c8ba Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Thu, 3 Sep 2026 09:20:30 +0200 Subject: [PATCH] Geraeteformular zum Anlegen und Bearbeiten ergaenzen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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//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 --- CHANGELOG.md | 30 +- docs/ANLEITUNG.md | 49 ++ docs/AUFWANDSSCHAETZUNG.md | 11 +- docs/DEVLOG.md | 60 +- docs/ROADMAP.md | 2 + docs/screenshots/01-anmeldung-dark.png | Bin 93549 -> 59572 bytes docs/screenshots/02-uebersicht-dark.png | Bin 205393 -> 205704 bytes docs/screenshots/02-uebersicht-light.png | Bin 205898 -> 206047 bytes docs/screenshots/03-geraeteliste-dark.png | Bin 1133805 -> 1137521 bytes docs/screenshots/03-geraeteliste-light.png | Bin 1166483 -> 1169991 bytes docs/screenshots/04-geraet-dark.png | Bin 232272 -> 236481 bytes docs/screenshots/04-geraet-light.png | Bin 240949 -> 245667 bytes docs/screenshots/05-fahrzeuge-dark.png | Bin 156515 -> 156567 bytes docs/screenshots/05-fahrzeuge-light.png | Bin 156566 -> 156609 bytes docs/screenshots/10-geraet-neu-dark.png | Bin 0 -> 307015 bytes docs/screenshots/10-geraet-neu-light.png | Bin 0 -> 314564 bytes scripts/screenshots.ts | 10 +- .../(app)/geraete/[id]/bearbeiten/page.tsx | 77 +++ src/app/(app)/geraete/[id]/page.tsx | 12 +- src/app/(app)/geraete/geraete-tabelle.tsx | 41 +- src/app/(app)/geraete/neu/page.tsx | 55 ++ src/components/stammdaten/anzeige.tsx | 17 +- .../stammdaten/geraet-formular-werte.ts | 63 ++ src/components/stammdaten/geraet-formular.tsx | 609 ++++++++++++++++++ src/components/ui/textarea.tsx | 21 + tests/e2e/geraet-formular.spec.ts | 202 ++++++ 26 files changed, 1237 insertions(+), 22 deletions(-) create mode 100644 docs/screenshots/10-geraet-neu-dark.png create mode 100644 docs/screenshots/10-geraet-neu-light.png create mode 100644 src/app/(app)/geraete/[id]/bearbeiten/page.tsx create mode 100644 src/app/(app)/geraete/neu/page.tsx create mode 100644 src/components/stammdaten/geraet-formular-werte.ts create mode 100644 src/components/stammdaten/geraet-formular.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 tests/e2e/geraet-formular.spec.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a6b583..b478ed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,35 @@ Die Regeln dazu stehen in [docs/VERSIONIERUNG.md](docs/VERSIONIERUNG.md). ## [Unveröffentlicht] -Noch nichts. Nächster Schritt ist Phase 2 (Wartungs- und Prüfmanagement). +Nächster Schritt ist Phase 2 (Wartungs- und Prüfmanagement). + +### Hinzugefügt + +- **Formular zum Anlegen und Bearbeiten eines Geräts** (`/geraete/neu` und + `/geraete//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 diff --git a/docs/ANLEITUNG.md b/docs/ANLEITUNG.md index 7606e9f..507bdcd 100644 --- a/docs/ANLEITUNG.md +++ b/docs/ANLEITUNG.md @@ -186,6 +186,55 @@ bewusst keine zweite Historie: zwei Quellen könnten auseinanderlaufen. --- +## 5a. Ein Gerät anlegen oder ändern + +![Formular für ein neues Gerät](screenshots/10-geraet-neu-light.png) + +Über **Neues Gerät** in der Geräteliste, oder über **Bearbeiten** auf der +Detailseite eines vorhandenen Geräts. Für größere Bestände ist der Import der +schnellere Weg; dieses Formular ist für den Neuzugang zwischendurch. + +Pflicht sind nur zwei Angaben: **Inventarnummer** und **Bezeichnung**. Alles +andere kann später nachgetragen werden. + +### Der Gerätetyp nimmt Arbeit ab + +Wird oben ein Gerätetyp gewählt, gelten dessen Prüfintervall, Prüfvorschrift +und Lebensdauer — man muss sie nicht eintragen. Die Felder darunter bleiben +trotzdem änderbar: Was dort steht, überschreibt die Vorgabe für dieses eine +Gerät. Das braucht man, wenn der Hersteller für eine bestimmte Charge etwas +anderes vorschreibt. + +Ist die Bezeichnung noch leer, wird sie aus dem Typ übernommen. + +### „Daraus ergibt sich" + +Der graue Kasten zeigt, was aus den Angaben folgt: wann die nächste Prüfung +fällig wird und wann die Nutzungsdauer endet. Er rechnet beim Tippen mit, +sodass man vor dem Speichern sieht, was herauskommt. Gerechnet wird mit +denselben Regeln wie auf dem Server — beim Speichern rechnet dieser erneut und +behält das letzte Wort. + +Steht dort „keine", fehlt entweder das Prüfintervall oder die Indienststellung. +Das ist kein Fehler: Für Geräte ohne Prüfpflicht ist es der Normalfall. + +### Wo liegt es? + +Entweder an einem Standort **oder** auf einem Fahrzeug. Der Umschalter lässt +nur eines von beidem zu — deshalb kann hier gar nicht erst der Fehler +entstehen, den die Datenbank sonst abweisen würde. + +Bei „Auf einem Fahrzeug" lässt sich zusätzlich der Geräteraum angeben. Er darf +leer bleiben: Manches liegt lose in der Kabine. Wird das Fahrzeug gewechselt, +wird die Raumangabe zurückgesetzt — der Raum gehört zum Fahrzeug. + +Gibt es in der Wehr nur einen Standort, ist er beim Anlegen schon ausgewählt. + +> Jede Änderung landet im Verlauf des Geräts. Wer wissen will, wann ein +> Prüftermin verschoben wurde und von wem, findet es dort. + +--- + ## 6. Etiketten drucken Der Bogen ist auf das gängige Format mit **65 Etiketten je DIN-A4-Seite** diff --git a/docs/AUFWANDSSCHAETZUNG.md b/docs/AUFWANDSSCHAETZUNG.md index d97a11e..7724ba6 100644 --- a/docs/AUFWANDSSCHAETZUNG.md +++ b/docs/AUFWANDSSCHAETZUNG.md @@ -31,7 +31,7 @@ Faktoren, die in die Schätzung eingehen: | Phase | Inhalt | Vergleichsaufwand | Sitzungszeit geschätzt | Sitzungszeit tatsächlich | |---|---|---:|---:|---:| | 0 | Fundament | 12–16 PT | — | **1:00 h** | -| 1 | Stammdaten | 10–14 PT | 2,5–3,5 h | **1:15 h** | +| 1 | Stammdaten | 10–14 PT | 2,5–3,5 h | **2:05 h** | | 2 | Wartung und Prüfung | 18–24 PT | 4,5–6 h | offen | | 3 | Einsätze und Übungen | 12–16 PT | 3–4 h | offen | | 4 | Kiosk-PWA mit Offline | 16–22 PT | 4–5,5 h | offen | @@ -72,7 +72,7 @@ das nachträglich einbaut, zahlt ein Vielfaches. ## Phase 1 — Stammdaten -**Vergleichsaufwand: 10–14 PT** · **Tatsächlich: 1:15 h Sitzungszeit** +**Vergleichsaufwand: 10–14 PT** · **Tatsächlich: 2:05 h Sitzungszeit** (1:15 h Hauptteil, 0:50 h Nachtrag) | Baustein | PT | |---|---:| @@ -97,8 +97,11 @@ Tabellenbibliothek nötig war. Nicht eingeplant war die Zeitzonen-Falle in der Fristenrechnung; sie hätte in Phase 2 deutlich mehr gekostet, wenn sie erst dort aufgefallen wäre. -Offen geblieben ist ein Formular zum einzelnen Anlegen von Geräten -(geschätzt 0,5 PT), siehe [DEVLOG.md](DEVLOG.md). +Das zunächst offen gebliebene Formular zum einzelnen Anlegen von Geräten wurde +als Nachtrag ergänzt — geschätzt waren 0,5 PT, tatsächlich 0:50 h Sitzungszeit. +Die Hälfte davon ging für zwei Dinge drauf, die nichts mit dem Formular zu tun +hatten: eine Server/Client-Grenze, die stillschweigend `undefined` liefert, und +Datumsfelder, die sich in Chromium nicht automatisiert füllen lassen. ## Phase 2 — Wartung und Prüfung diff --git a/docs/DEVLOG.md b/docs/DEVLOG.md index 05dd5e0..791008d 100644 --- a/docs/DEVLOG.md +++ b/docs/DEVLOG.md @@ -12,7 +12,65 @@ Die aufgewendete Zeit wandert nach jedem Abschnitt in die Spalte |---|---:|---:|---| | Phase 0 — Fundament | 1:00 h | 1 | 03.09.2026 | | Phase 1 — Stammdaten | 1:15 h | 1 | 03.09.2026 | -| **Summe** | **2:15 h** | **2** | | +| Phase 1 — Nachtrag Geräteformular | 0:50 h | 1 | 03.09.2026 | +| **Summe** | **3:05 h** | **3** | | + +--- + +## 2026-09-03 · Phase 1 — Nachtrag: Geräteformular + +**08:30 – 09:20 · 0:50 h** + +### Was entstanden ist + +Die letzte offene Lücke aus Phase 1: ein Formular zum Anlegen und Bearbeiten +eines einzelnen Geräts. Bis dahin konnte man Geräte nur über den Import +erfassen — für einen Bestand richtig, für den einen nachbeschafften Gerät +umständlich. + +| Datei | Inhalt | +|---|---| +| `components/stammdaten/geraet-formular.tsx` | das Formular, geteilt von Anlegen und Bearbeiten | +| `components/stammdaten/geraet-formular-werte.ts` | Typ und Vorgabewerte, bewusst ohne `"use client"` | +| `components/ui/textarea.tsx` | fehlender Baustein | +| `app/(app)/geraete/neu/page.tsx` | Anlegen | +| `app/(app)/geraete/[id]/bearbeiten/page.tsx` | Bearbeiten | +| `tests/e2e/geraet-formular.spec.ts` | 7 Tests | + +### Entscheidungen + +**Die Lagerung ist ein Umschalter, keine zwei Auswahlfelder.** Standort und +Fahrzeug schließen sich aus. Zwei nebeneinanderstehende Felder laden dazu ein, +beide zu füllen; die Datenbank weist das ab, aber eine Fehlermeldung nach dem +Absenden ist schlechter als ein Formular, in dem der Fehler gar nicht möglich +ist. Beim Wechsel des Fahrzeugs wird der Geräteraum geleert — er gehört zum +Fahrzeug. + +**Die Fristen werden schon beim Tippen vorgerechnet.** Wer ein Prüfintervall +einträgt, sieht sofort, wann die nächste Prüfung fällig wird. Gerechnet wird +mit denselben Funktionen wie auf dem Server; beim Speichern rechnet der Server +erneut und behält das letzte Wort. Die Vorschau ist Anzeige, keine Quelle. + +**Bei nur einem Standort wird er vorbelegt.** Das ist bei den meisten Wehren +der Fall, und ein Pflichtfeld weniger ist ein Klick weniger. + +### Wo es gehakt hat + +| Problem | Ursache | Lösung | +|---|---|---| +| Formular stürzte beim ersten Rechnen ab | `LEERES_GERAET` lag im `"use client"`-Modul; eine Server-Komponente bekommt beim Import nur eine Client-Referenz, das Spreaden ergibt `undefined` | Werte in ein eigenes Modul ohne `"use client"` | +| E2E fand die Felder nicht | `getByLabel` vergleicht den Text des `