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 `