Aufraeumarbeiten: ESLint, DOM-Verschachtelung, eigene Testwehr

ESLint hatte keine Konfiguration — das Skript "npm run lint" pruefte nichts.
Jetzt Flat Config ohne Formatierungsregeln, dafuer mit den Regeln, die die
Fehler dieses Projekts gemeldet haetten. Er hat gleich zwei gefunden, die
niemand gesucht hatte.

Das Badge rendert ein span statt eines div. Damit ist die Meldung
"div cannot be a descendant of p" weg, die seit Phase 3 auf der Einsatzseite
stand — sie ist der klassische Ausloeser fuer einen Hydration-Fehler.

Die E2E-Tests laufen gegen eine eigene Wehr. Nach fuenf Phasen standen in der
Beispielwehr 120 Atemschutz-Ueberwachungen statt einer und 115 Geraete statt
45; bei jedem Lauf aenderten sich alle Bildschirmfotos. Der Bestand ist einmal
zurueckgesetzt, npm run db:test-reset baut die Testwehr neu auf.

Behoben nebenbei:
- Die Fehlerflaeche der Karte konnte nie etwas anzeigen.
- Zwei useMemo der Lagekarte rechneten bei jedem Rendern neu.
- tests/e2e war vom Typecheck ausgenommen und liess einen doppelt
  deklarierten Bezeichner durch.

Closes #10
Closes #11
Closes #12

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-04 13:24:48 +02:00
co-authored by Claude Opus 5
parent 8081131eb7
commit 7196c7110c
94 changed files with 1447 additions and 428 deletions
+73 -1
View File
@@ -20,7 +20,79 @@ Die aufgewendete Zeit wandert nach jedem Abschnitt in die Spalte
| Phase 6 — Kartenmodule | 2:50 h | 1 | 04.09.2026 |
| Phase 7 — Benachrichtigungen und Termine | 3:10 h | 1 | 04.09.2026 |
| Phase 8 — Lokale KI | 3:40 h | 1 | 04.09.2026 |
| **Summe** | **23:00 h** | **10** | |
| Aufräumarbeiten (Issues #10–#12) | 1:20 h | 1 | 04.09.2026 |
| **Summe** | **24:20 h** | **11** | |
---
## 2026-09-04 · Aufräumarbeiten — Issues #10 bis #12
**11:15 – 12:35 · 1:20 h**
### Was entstanden ist
Drei Punkte, die seit mehreren Phasen im Abschlussbericht standen und in jeder
Phase Zeit gekostet haben. Diesmal als Issues angelegt und abgearbeitet.
| Issue | Kern |
|---|---|
| #11 | ESLint hatte keine Konfiguration — `npm run lint` prüfte nichts |
| #12 | `<div>` in einem `<p>` auf der Einsatzseite, seit Phase 3 |
| #10 | Die E2E-Tests schrieben in die Beispielwehr |
### Entscheidungen
**ESLint zuerst.** Er findet die anderen beiden — und tatsächlich hat er
gleich zwei Fehler gemeldet, die niemand gesucht hatte: eine Fehlerfläche auf
der Karte, die nie etwas anzeigen konnte, und zwei `useMemo` in der Lagekarte,
die bei jedem Rendern neu rechneten.
**Keine Formatierungsregeln.** Die macht Prettier. Zwei Werkzeuge, die sich
über Anführungszeichen streiten, lenken von den Regeln ab, auf die es ankommt.
**Auch keine typgestützten Regeln.** `no-floating-promises` wäre für diesen
Stack die wertvollste Regel überhaupt — sie braucht aber den Compiler bei
jedem Lauf, und dann dauert der Lauf Minuten statt Sekunden. Bei einem
Werkzeug, das vor jedem Commit laufen soll, ist das der falsche Tausch.
**Das Badge wurde ein `<span>`, nicht der Absatz ein `<div>`.** Die Meldung
kam von einer Stelle, aber die Ursache saß im Bauteil: ein Abzeichen ist
Fließtext-Inhalt und gehört in ein Inline-Element. So ist auch jede künftige
Verwendung in einem Absatz richtig, und die Darstellung ändert sich nicht —
`inline-flex` verhält sich in beiden gleich.
**Eine zweite Wehr statt Aufräumen nach dem Lauf.** Ein abgebrochener Lauf
lässt beim Aufräumen einen Bestand zurück, den niemand erwartet. Ein
vollständiger Neuaufbau ist der einzige Zustand, den man kennt — und der Seed
kann ohnehin schon alles, er brauchte nur eine Kennung aus der Umgebung.
**Ein Riegel gegen den eigenen Fehler.** `npm run db:test-reset` bricht ab,
wenn die Testkennung mit der der Beispielwehr übereinstimmt. Ein Skript, das
eine ganze Wehr löscht, braucht eine Stelle, an der es sich weigert.
### Wo es gehakt hat
| Problem | Ursache | Lösung |
|---|---|---|
| Zwei Tests fielen nach der Umstellung durch — und das war richtig so | Sie hatten die Verschmutzung gebraucht. `getByText("B 3 Wohnhausbrand", { exact: true })` traf bisher einen Einsatz aus einem früheren Testlauf, weil der kein Objekt hatte; der Einsatz aus dem Seed hat eines und steht in derselben Zelle | Anker am Zellenanfang statt Prüfung auf Gleichheit |
| Der Kiosk-Test schrieb weiter in die Beispielwehr | `const WEHR = "ff-musterhausen"` stand fest im Test. Das Terminal merkt sich die Wehr und schreibt Mängel und Fahrten genau dorthin | Kennung aus `wehr.ts`, wie überall sonst |
| Ein doppelt deklarierter Bezeichner rutschte durch den Typecheck | `tests/e2e` war in der `tsconfig.json` ausgeschlossen | Ausnahme entfernt. Sie hat nichts gebracht: die Dateien übersetzen sauber |
| Die Anmeldung im Bildschirmfoto-Skript lief ins Leere | Bekannt aus Phase 7 — bei kaltem `.next` ist die Seite noch nicht hydriert | War schon behoben; hier nur wieder aufgetreten und bestätigt |
### Abweichung von der Vorgabe
**Issues angelegt**, obwohl CLAUDE.md das ausdrücklich verbietet („Niemals ein
Issue anlegen"). Auf ausdrückliche Anweisung im Chat — die Regel stammt vom
Auftraggeber, und er hat sie für diesen Vorgang aufgehoben.
### Nachweise
- `npx tsc --noEmit` — ohne Ausgabe, jetzt **einschließlich** `tests/e2e`
- `npm run lint` — ohne Beanstandung
- `npm run test` — 495 Tests grün
- `npx playwright test` — grün gegen die Testwehr
- Die Beispielwehr steht danach unverändert auf dem Seed-Stand: 49 Geräte,
8 Einsätze, 1 Atemschutzüberwachung, 19 Termine, 4 Wiki-Seiten
---
Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 808 KiB

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 830 KiB

After

Width:  |  Height:  |  Size: 764 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 988 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 930 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 946 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 760 KiB

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 766 KiB

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 KiB

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 263 KiB

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 KiB

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 KiB

After

Width:  |  Height:  |  Size: 742 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 991 KiB

After

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 KiB

After

Width:  |  Height:  |  Size: 638 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 KiB

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 KiB

After

Width:  |  Height:  |  Size: 328 KiB