Phase 1: Stammdatenverwaltung
Ab hier fuehrt die Wehr ihren Bestand in FLORIAN. Datenmodell: - Standort, Geraeteraum, Geraetetyp, Qualifikationstyp und Qualifikation - Fahrzeug um Fahrgestellnummer, DIN-Typ, Erstzulassung, Sitzplaetze, Tankinhalt und UVV-Frist erweitert - Geraet um Geraetetyp, Lebensdauerkategorie, errechnetes Lebensdauerende, Anschaffungspreis und die Lagerung erweitert - Ein Geraet steht entweder an einem Standort oder ist auf einem Fahrzeug verlastet. Eine Pruefbedingung setzt das durch, ein Trigger stellt zusaetzlich sicher, dass ein angegebener Geraeteraum zum angegebenen Fahrzeug gehoert. - RLS auf allen neuen Tabellen, Trigram-Indizes fuer die Volltextsuche Fachlogik: - lib/stammdaten/fristen.ts rechnet Prueftermine, Lebensdauer und den abgeleiteten Geraetestatus. Der Geraetetyp gibt vor, das Geraet darf abweichen. - lib/stammdaten/qr.ts und qr-parser.ts erzeugen und lesen die Codes - lib/stammdaten/etiketten.ts baut den A4-Bogen mit 65 Etiketten - lib/stammdaten/import.ts liest CSV und Excel, erkennt deutsche Datums- und Zahlenformate, Windows-1252 und Dubletten innerhalb der Datei - lib/pdf.ts spricht Gotenberg an Oberflaeche: - Geraeteliste mit Volltextsuche, Statusfilter, serverseitiger Sortierung, Blaettern, Spaltenauswahl und Massenbearbeitung - Geraetedetail mit QR-Code, Pruefvorgabe aus dem Geraetetyp und Verlauf - Fahrzeugliste und -detail mit Beladung nach Beladeordnung - Mitgliederliste nach Abteilung, Mitgliedsdetail mit Qualifikationen - Qualifikationen mit Ablaufwarnung je Vorwarnzeit des Typs - Import-Assistent mit Zuordnung und Vorschau vor dem Uebernehmen - QR-Scanner ueber die Kamera, mit Eingabefeld als Rueckfallweg Behoben: - Fristenrechnung lief ueber startOfDay aus date-fns und damit in der Zeitzone des Servers. Ein DATE aus der Datenbank kommt als UTC-Mitternacht und wurde dadurch auf den Vortag geschoben — Fristen waeren einen Tag zu frueh faellig geworden. Jetzt eigene Kalenderarithmetik auf UTC-Bestandteilen. - ctx.sitzung blieb in tRPC-Prozeduren nullbar, weil t.middleware() die Verengung der vorigen Middleware verwirft. Middleware jetzt direkt an .use(). - CardTitle war ein div und damit keine Ueberschrift. Wer mit einer Sprachausgabe arbeitet, springt von Ueberschrift zu Ueberschrift; Karten tauchten in dieser Navigation nicht auf. Daten und Tests: - Seed um 3 Standorte, 20 Geraeteraeume, 14 Geraetetypen mit echten Pruefvorgaben, 49 Geraete und 107 Qualifikationen erweitert - 104 Vitest-Tests (vorher 32), 44 Playwright-Tests (vorher 10) - Anleitung um sieben bebilderte Abschnitte erweitert, Changelog, Devlog, Aufwandsschaetzung und Roadmap fortgeschrieben Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4b3021bb50
commit
26b62a3477
+239
-10
@@ -1,11 +1,10 @@
|
||||
# Anleitung
|
||||
|
||||
Bedienung von FLORIAN, Stand Phase 0 (Version 0.1.0). Die Bilder zeigen die
|
||||
Bedienung von FLORIAN, Stand Phase 1 (Version 0.2.0). Die Bilder zeigen die
|
||||
Beispielwehr aus dem Seed und werden mit `npm run screenshots` neu erzeugt.
|
||||
|
||||
> Was hier noch fehlt — Mitglieder, Geräte, Prüfungen — kommt in den
|
||||
> folgenden Phasen dazu. Der Stand ist bewusst schmal: Phase 0 baut das
|
||||
> Fundament, nicht die Fachmodule. Siehe [ROADMAP.md](ROADMAP.md).
|
||||
> Prüfungen, Einsätze, Atemschutz und die Kiosk-Oberfläche kommen in den
|
||||
> folgenden Phasen dazu. Siehe [ROADMAP.md](ROADMAP.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -94,7 +93,231 @@ nicht geblendet werden.
|
||||
|
||||
---
|
||||
|
||||
## 4. Rollen und was sie dürfen
|
||||
## 4. Geräte
|
||||
|
||||

|
||||
|
||||
Die Geräteliste ist die Ansicht, die am häufigsten offen ist. Von links nach
|
||||
rechts steht dort, was man beim Suchen braucht: Inventarnummer, was es ist, von
|
||||
wem, wo es liegt, in welchem Zustand und wann es wieder geprüft werden muss.
|
||||
|
||||
### Suchen und filtern
|
||||
|
||||
**Das Suchfeld** durchsucht Inventarnummer, Bezeichnung, Hersteller, Modell und
|
||||
Seriennummer gleichzeitig. Es genügt ein Teil — „press" findet alle
|
||||
Pressluftatmer. Gesucht wird erst, wenn man kurz aufhört zu tippen; das
|
||||
verhindert eine Abfrage bei jedem Anschlag.
|
||||
|
||||
**Alle Zustände** schränkt auf einen Status ein. Praktisch, wenn man wissen
|
||||
will, was gerade fällig oder gesperrt ist.
|
||||
|
||||
**Spalten** blendet Spalten ein und aus. Zwölf stehen zur Auswahl, sechs sind
|
||||
voreingestellt. Die Auswahl bleibt im Browser gespeichert — der Gerätewart
|
||||
schaut auf Prüftermine, der Kassier auf Anschaffungspreise, und beide finden
|
||||
ihre Einstellung beim nächsten Mal wieder.
|
||||
|
||||
**Sortiert** wird durch einen Klick auf die Spaltenüberschrift, ein zweiter
|
||||
Klick dreht die Richtung um.
|
||||
|
||||
### Farben und Fristen
|
||||
|
||||
| Farbe | Bedeutung |
|
||||
|---|---|
|
||||
| grün | einsatzbereit |
|
||||
| gelb | Prüfung fällig, oder eine Frist läuft in den nächsten 30 Tagen ab |
|
||||
| rot | gesperrt, ausgesondert oder überfällig |
|
||||
| grau | in Reparatur |
|
||||
|
||||
Hinter jedem Datum steht, wie viele Tage noch bleiben — „in 12 Tagen" sagt mehr
|
||||
als ein Datum, das man erst im Kopf ausrechnen muss. Was vorbei ist, steht als
|
||||
„seit 62 Tagen" da.
|
||||
|
||||
Die Farbe allein trägt nie die Information: daneben steht immer Klartext.
|
||||
|
||||
### Mehrere Geräte auf einmal
|
||||
|
||||
Über die Kästchen links lassen sich Geräte auswählen. Sobald mindestens eines
|
||||
ausgewählt ist, erscheint eine Leiste mit zwei Möglichkeiten:
|
||||
|
||||
- **Etiketten drucken** erzeugt den Etikettenbogen als PDF.
|
||||
- **Status setzen auf …** ändert den Zustand aller ausgewählten Geräte. Der
|
||||
häufigste Handgriff überhaupt: Nach einer Sammelprüfung stehen zwanzig Geräte
|
||||
auf einmal wieder auf einsatzbereit.
|
||||
|
||||
Jede Änderung wird einzeln protokolliert — am jeweiligen Gerät, nicht als
|
||||
Sammelvorgang, den später niemand mehr zuordnen kann.
|
||||
|
||||
---
|
||||
|
||||
## 5. Ein einzelnes Gerät
|
||||
|
||||

|
||||
|
||||
### Woher das Prüfintervall kommt
|
||||
|
||||
Steht am Gerät kein eigenes Intervall, gilt die Vorgabe des Gerätetyps. Das
|
||||
Bild zeigt es: „alle 12 Monate (aus dem Gerätetyp)". So muss man bei vierzig
|
||||
Pressluftatmern nicht vierzigmal dasselbe eintragen. Schreibt der Hersteller
|
||||
für eine bestimmte Charge etwas anderes vor, wird der Wert am Gerät gesetzt und
|
||||
gewinnt dann.
|
||||
|
||||
Dasselbe gilt für die Lebensdauer. Ob sie ab Herstellung oder ab
|
||||
Indienststellung zählt, hängt von der Vorschrift ab: Seile und Druckbehälter
|
||||
altern ab Herstellung, Schutzkleidung meist ab Indienststellung.
|
||||
|
||||
### QR-Code
|
||||
|
||||
Rechts steht der QR-Code des Geräts. **Etikett drucken** erzeugt daraus einen
|
||||
Aufkleber für den Geräteraum.
|
||||
|
||||
Der Code zeigt auf `.../g/<Inventarnummer>`. Wer ihn scannt und angemeldet ist,
|
||||
landet direkt auf dieser Seite. Wer nicht angemeldet ist, landet zuerst auf der
|
||||
Anmeldung und danach hier — **der Aufkleber ist keine Berechtigung.** Wer ihn
|
||||
fotografiert, bekommt dadurch keinen Zugang.
|
||||
|
||||
Bewusst steht die Inventarnummer im Code und nicht die interne Kennung: Eine
|
||||
UUID wäre 36 Zeichen lang, das Muster entsprechend fein, und auf einem
|
||||
38 × 21 mm großen Aufkleber im staubigen Geräteraum wird das unzuverlässig.
|
||||
|
||||
### Verlauf
|
||||
|
||||
Unten steht, was mit dem Gerät passiert ist — direkt aus dem Audit-Log. Es gibt
|
||||
bewusst keine zweite Historie: zwei Quellen könnten auseinanderlaufen.
|
||||
|
||||
---
|
||||
|
||||
## 6. Etiketten drucken
|
||||
|
||||
Der Bogen ist auf das gängige Format mit **65 Etiketten je DIN-A4-Seite**
|
||||
(38,1 × 21,2 mm, 5 Spalten × 13 Zeilen) ausgelegt — den gibt es in jedem
|
||||
Schreibwarenladen und er passt in jeden Laserdrucker.
|
||||
|
||||
Auf jedem Etikett stehen drei Dinge: der QR-Code, die Inventarnummer zum
|
||||
Ablesen ohne Handy, und die Bezeichnung. Bei verlasteten Geräten kommt der
|
||||
Funkrufname des Fahrzeugs dazu.
|
||||
|
||||
Der Druck kann an einer beliebigen Position des Bogens beginnen. Wer drei
|
||||
Etiketten braucht, muss so keinen ganzen Bogen opfern.
|
||||
|
||||
> Erzeugt wird das PDF von Gotenberg, einem eigenen Container im Stack. Kommt
|
||||
> die Meldung „Der Etikettenbogen ließ sich nicht erzeugen", läuft er nicht:
|
||||
> `docker compose ps` zeigt es.
|
||||
|
||||
---
|
||||
|
||||
## 7. Gerät scannen
|
||||
|
||||
Über **Scannen** in der Geräteliste öffnet sich der QR-Scanner. Der Browser
|
||||
fragt beim ersten Mal nach der Kamerafreigabe. Das Bild verlässt das Gerät
|
||||
nicht — ausgewertet wird direkt im Browser.
|
||||
|
||||
Zwei Dinge sind bewusst so gebaut:
|
||||
|
||||
- **Es geht immer auch ohne Kamera.** Darunter steht ein Eingabefeld für die
|
||||
Inventarnummer. Wenn die Freigabe verweigert wurde oder das Etikett zerkratzt
|
||||
ist, tippt man sie ein.
|
||||
- **Fremde Codes brechen nichts ab.** Der Barcode des Herstellers klebt oft
|
||||
direkt daneben. Wird er erkannt, kommt ein Hinweis statt einer Fehlermeldung.
|
||||
|
||||
---
|
||||
|
||||
## 8. Fahrzeuge
|
||||
|
||||

|
||||
|
||||
Die Übersicht zeigt die Fahrzeuge als Kacheln — eine Wehr hat drei bis zehn,
|
||||
nicht dreihundert. Auf jeder Kachel stehen der nächste TÜV-Termin und die Zahl
|
||||
der verlasteten Geräte.
|
||||
|
||||
Auf der Detailseite steht die **Beladung nach Geräteräumen**, in der Reihenfolge
|
||||
der Beladeordnung: G1 bis G6, Heck, Dach, Mannschaftsraum. Genau so sucht der
|
||||
Gerätewart — nicht „wo ist die Motorsäge", sondern „was ist in G3".
|
||||
|
||||
Beim Anlegen eines neuen Fahrzeugs werden diese Räume auf Wunsch gleich
|
||||
mitangelegt. Ein Mannschaftstransportwagen bekommt weniger Fächer als ein
|
||||
Löschfahrzeug.
|
||||
|
||||
Ein Gerät liegt **entweder** an einem Standort **oder** auf einem Fahrzeug.
|
||||
Beides gleichzeitig lässt die Datenbank nicht zu — das ist keine Formalität,
|
||||
sondern verhindert, dass ein Gerät zweimal gezählt wird.
|
||||
|
||||
Was verlastet ist, aber keinem Raum zugeordnet wurde, erscheint unter „Ohne
|
||||
Raumangabe" statt stillschweigend zu verschwinden.
|
||||
|
||||
---
|
||||
|
||||
## 9. Mitglieder und Qualifikationen
|
||||
|
||||

|
||||
|
||||
Die Mitgliederliste ist nach Abteilung gruppiert — Aktive, Jugendfeuerwehr,
|
||||
Passive. Eine durchlaufende alphabetische Liste vermischt Gruppen, die in der
|
||||
Wehr nichts miteinander zu tun haben.
|
||||
|
||||
Auf der Seite **Qualifikationen** steht oben, was abgelaufen ist oder demnächst
|
||||
abläuft. Das ist der Grund, warum jemand diese Seite öffnet.
|
||||
|
||||
**Gewarnt wird mit der Vorwarnzeit des jeweiligen Typs**, nicht mit einer
|
||||
pauschalen Frist. Eine G26.3-Untersuchung braucht 120 Tage Vorlauf, weil erst
|
||||
ein Termin beim Betriebsarzt gefunden werden muss; eine jährliche Unterweisung
|
||||
kommt mit deutlich weniger aus. Einsatzrelevante Qualifikationen sind
|
||||
gekennzeichnet.
|
||||
|
||||
Das Ablaufdatum wird beim Speichern aus der Gültigkeitsdauer des Typs
|
||||
gerechnet — lässt sich aber überschreiben. Auf einer Urkunde steht manchmal
|
||||
etwas anderes, als die Regel hergäbe.
|
||||
|
||||
---
|
||||
|
||||
## 10. Daten importieren
|
||||
|
||||

|
||||
|
||||
Der Import geht in drei Schritten, und der mittlere ist der eigentliche Punkt.
|
||||
|
||||
### Schritt 1: Datei wählen
|
||||
|
||||
CSV oder Excel (`.xlsx`). Die erste Zeile muss die Spaltenüberschriften
|
||||
enthalten. Höchstens 10 MB und 5.000 Zeilen.
|
||||
|
||||
Erkannt werden die Formate, die in der Praxis herauskommen:
|
||||
|
||||
| Fall | Wird verstanden |
|
||||
|---|---|
|
||||
| Trennzeichen | Semikolon, Komma, Tabulator, senkrechter Strich |
|
||||
| Zeichensatz | UTF-8 (auch mit BOM) und Windows-1252 |
|
||||
| Datum | 02.09.2026, 2.9.26, 2026-09-02, und die Seriennummer aus Excel |
|
||||
| Zahlen | 1.234,56 und 1,234.56, mit und ohne Eurozeichen |
|
||||
| Ja/Nein | ja, j, x, 1, wahr — und die Gegenstücke |
|
||||
|
||||
### Schritt 2: Zuordnung prüfen
|
||||
|
||||
FLORIAN rät die Zuordnung anhand der Überschriften. „Inv.-Nr." landet bei
|
||||
Inventarnummer, „Fabrikat" bei Hersteller. Jede Zuordnung lässt sich ändern,
|
||||
und die Vorschau passt sich sofort an.
|
||||
|
||||
**Die Vorschau ist der Grund, warum es diesen Zwischenschritt gibt.** Sie zeigt
|
||||
Zeile für Zeile, was übernommen würde, und markiert rot, was nicht stimmt:
|
||||
fehlende Pflichtfelder, unlesbare Daten, Inventarnummern, die in der Datei
|
||||
doppelt vorkommen. Eine falsch zugeordnete Spalte verteilt sonst Seriennummern
|
||||
auf Inventarnummern, und das merkt man erst Wochen später am Regal.
|
||||
|
||||
### Schritt 3: Übernehmen
|
||||
|
||||
Übernommen wird nur, was fehlerfrei ist. Zeilen mit Fehlern bleiben in der
|
||||
Datei und lassen sich nach der Korrektur erneut einlesen.
|
||||
|
||||
**Vorhandene Datensätze aktualisieren** entscheidet, was mit Datensätzen
|
||||
passiert, die es schon gibt: überspringen (Vorgabe) oder überschreiben.
|
||||
Erkannt werden sie über die Inventarnummer, bei Mitgliedern über die
|
||||
Mitgliedsnummer oder die E-Mail-Adresse.
|
||||
|
||||
Zum Schluss steht da, wie viele Datensätze angelegt, aktualisiert und
|
||||
übersprungen wurden. Der ganze Vorgang steht auch im Protokoll.
|
||||
|
||||
---
|
||||
|
||||
## 11. Rollen und was sie dürfen
|
||||
|
||||
Neun Rollen, jede mit einem festen Satz Rechte. Die Zuordnung steht zentral in
|
||||
`src/lib/authz.ts` und wird beim Seed in die Datenbank gespiegelt — eine Wehr
|
||||
@@ -123,7 +346,7 @@ Das Terminal selbst ist nur die Tür.
|
||||
|
||||
---
|
||||
|
||||
## 5. Anmeldung über authentik einrichten
|
||||
## 12. Anmeldung über authentik einrichten
|
||||
|
||||
authentik läuft **getrennt** von FLORIAN und ist nicht Teil dieses Stacks.
|
||||
|
||||
@@ -176,7 +399,7 @@ ganze Wehr aus.
|
||||
|
||||
---
|
||||
|
||||
## 6. Terminal-Anmeldung im Gerätehaus
|
||||
## 13. Terminal-Anmeldung im Gerätehaus
|
||||
|
||||
Die Oberfläche dazu kommt in Phase 4; der Anmeldeweg steht bereits.
|
||||
|
||||
@@ -198,7 +421,7 @@ Huber), PIN `471203`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Was protokolliert wird
|
||||
## 14. Was protokolliert wird
|
||||
|
||||
Jede schreibende Änderung erzeugt einen Eintrag mit Zeitpunkt, Person,
|
||||
betroffenem Datensatz und den geänderten Feldern im Vorher-Nachher-Vergleich.
|
||||
@@ -218,11 +441,12 @@ einzige Weg, auf dem etwas aus dem Log verschwindet.
|
||||
dem Schreiben herausgenommen.
|
||||
|
||||
Einsehbar ist der Log mit dem Recht `system.auditlog`, das Kommandant und
|
||||
Administrator haben. Die Ansicht dazu kommt in Phase 1.
|
||||
Administrator haben. Am Gerät und am Fahrzeug ist der Verlauf bereits auf der
|
||||
jeweiligen Detailseite sichtbar.
|
||||
|
||||
---
|
||||
|
||||
## 8. Wenn etwas nicht geht
|
||||
## 15. Wenn etwas nicht geht
|
||||
|
||||
| Beobachtung | Wahrscheinliche Ursache | Was hilft |
|
||||
|---|---|---|
|
||||
@@ -231,6 +455,11 @@ Administrator haben. Die Ansicht dazu kommt in Phase 1.
|
||||
| Listen sind leer, obwohl Daten da sind | Kein Mandantenkontext gesetzt | Das ist der Schutzmechanismus. Der Zugriff muss über `fuerOrganisation()` laufen. |
|
||||
| Dateien lassen sich nicht hochladen | MinIO nicht erreichbar oder Bucket fehlt | `http://localhost:9011` öffnen; `docker compose logs minio-init` |
|
||||
| Der Knopf für authentik fehlt | Issuer, Client-ID oder Secret nicht gesetzt | Alle drei sind nötig, sonst wird der Weg ausgeblendet |
|
||||
| Etikettenbogen lässt sich nicht erzeugen | Gotenberg läuft nicht | `docker compose ps`, dann `docker compose up -d gotenberg` |
|
||||
| Kamera bleibt schwarz | Freigabe abgelehnt, oder die Seite läuft nicht über HTTPS | Der Browser fragt nur bei HTTPS oder localhost. Sonst die Inventarnummer eintippen |
|
||||
| Import meldet fehlende Pflichtfelder | Keine Spalte ist Inventarnummer oder Bezeichnung zugeordnet | In Schritt 2 die Zuordnung von Hand setzen |
|
||||
| Umlaute im Import verstümmelt | Die Datei ist weder UTF-8 noch Windows-1252 | In Excel als „CSV UTF-8" speichern |
|
||||
| Gerät lässt sich nicht speichern | Standort **und** Fahrzeug gesetzt, oder keines von beidem | Genau eines von beiden angeben |
|
||||
| `docker compose up` startet Traefik mit | Override-Datei wurde nicht mitgeladen | In Produktion `-f docker-compose.yml` angeben |
|
||||
|
||||
Zustand aller Randsysteme auf einen Blick:
|
||||
|
||||
Reference in New Issue
Block a user