Dashboard-Raster von 12 auf 24 Spalten - bei 12 waren Reihen sofort randvoll (3 Kacheln à w:4), kein Spielraum zum Breiter-Ziehen ohne Nachbarkachel zu verkleinern. Layout-localStorage-Key auf v3 gehoben (alte v2-Koordinaten passen nicht mehr zum neuen Raster). Sidebar links jetzt einklappbar (Toggle-Button bleibt sichtbar, Zustand in localStorage) - mehr Platz für breite Dashboard-Raster auf Desktop-Monitoren.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Vorheriger Fix (--mabea-* Zwischen-Variablen) hat den Zirkel nur eine Ebene verschoben statt aufgelöst: da @theme weiterhin --color-primary selbst neu definierte (höherer Layer als global.css), zeigte --mabea-primary auf --color-primary, welches wiederum auf --mabea-primary zeigte - derselbe Zirkel, nur indirekt. Coming from CSS-Spec: eine zirkuläre var()-Kette macht die Property zur "guaranteed-invalid value", unabhängig davon über wie viele Zwischenschritte sie läuft.
Echter Fix: kein @theme-Mapping auf --color-* mehr, damit keine Namenskollision mit global.css überhaupt erst entstehen kann. Alle betroffenen Tailwind-Klassen (bg-primary, text-danger, border-l-warning, bg-surface-sunken, text-muted-foreground, ring-primary, etc.) auf Arbitrary-Value-Syntax umgestellt (z.B. bg-[var(--color-primary)]) - referenziert global.css-Variablen direkt, ohne je einen neuen Property-Namen im Theme-Layer zu belegen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
@theme-Mapping in tailwind.css hatte Einträge wie "--color-primary: var(--color-primary)" - gleicher Name wie in global.css, also Selbstreferenz auf dieselbe Custom Property. Laut CSS-Spec wird eine zirkuläre var()-Referenz zur "guaranteed-invalid value" - die Property bricht dadurch komplett weg, nicht nur für neue Tailwind-Utilities, sondern für JEDE Regel im ganzen Projekt, die dieselbe Property liest (z.B. .btn-primary in global.css, .badge-danger, etc.). Betraf: primary, primary-hover, primary-soft, danger, danger-bg, warning, warning-bg, success, success-bg, surface, surface-sunken, border, border-strong - praktisch die gesamte Farbpalette der App, nicht auf Dashboard beschränkt. Sichtbar u.a. als unsichtbarer weißer Text auf weißem "Trotzdem übernehmen"-Button (Kontrolle-Seite bei gesperrtem Objekt). Fix: Zwischen-Variablen mit eigenem Namen (--mabea-*) in tailwind.css, keine Namenskollision mehr.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Race Condition: bei bereits eingeloggter Session (Direktaufruf/Reload von "/" oder /dashboard/etc.) ist "eingeloggt" sofort true (Token im Storage), "rollen" aber noch [] bis /auth/me durch ist. Startseite/AdminRoute haben in genau diesem kurzen Fenster mit istVerantwortlich=false entschieden und per Navigate replace auf /objekte umgeleitet, bevor die echten Rollen (z.B. "administration") eintrafen - Admin landete dadurch nie auf dem Dashboard, sondern immer auf Objekte. Neues rollenGeladen-Flag im AuthContext, Startseite/AdminRoute warten jetzt darauf statt vorschnell mit rollen=[] zu entscheiden.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
react-grid-layout/react-resizable hängt den Resize-Griff per children-Injection an (cloneElement(child, {}, ...child.props.children, handle)) - erwartet einen DIREKTEN Kindknoten der Grid-Item-Wurzel (CSS ".react-grid-item > .react-resizable-handle"). TileFrame rendere "children" bisher nur verschachtelt in CardContent, die CSS-Regel griff dort nicht - Griff blieb unsichtbar/nicht greifbar. Fix: eigener Kachel-Inhalt läuft jetzt über eine separate "inhalt"-Prop, "children" (das react-grid-layout für den Griff nutzt) wird direkt unter Card gerendert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Root Cause (Screenshot-Analyse): .page-wide hatte kein width:100%/flex:1 wie .page - als Flex-Kind von .app-shell schrumpfte es auf reine Content-Breite. react-grid-layout positioniert Kacheln absolut (kein Beitrag zur intrinsischen Breite), dadurch kollabierte der ganze Grid-Container auf ~380px statt volle verfügbare Breite - Kacheln dadurch winzig, Text lief mitten im Wort ab. Fix: .page-wide bekommt flex:1/width:100%. Zusätzlich CardHeader/CardTitle mit min-w-0/truncate gegen abgeschnittene Titel, Einsatzbereitschaft-Vierer-Zeile von sm:grid-cols-4 (reagiert auf Viewport-, nicht Kachel-Breite) auf festes grid-cols-2 - lief in schmalen Kacheln trotz breitem Viewport ineinander.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Card: 14px Radius, weicherer Schatten, Hover-Lift über box-shadow (nicht transform, würde von react-grid-layouts Inline-Positionierung überschrieben). Einsatzbereitschaft-Kachel: große Zahl "X/Y einsatzbereit", proportionaler gestapelter Statusbalken, Vierer-Zeile mit Farbpunkten+Zahl, Detail-Liste bleibt aufklappbar. Prüftermine/Ablaufdaten/Qualifikationsablauf: auf 5 Zeilen begrenzt mit "Alle anzeigen", Status-Badge links (überfällig gefüllt rot, bald fällig Outline amber, FristBadge zeigt nur noch Status), Countdown rechts tabular-nums. Neue --status-ok/warn/unknown/critical CSS-Variablen als Alias auf bestehende Farb-Tokens. Skeleton-Platzhalter statt "Lade…"-Text während des initialen Ladens. Keine API-/Datenänderungen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
@dnd-kit (nur Reihenfolge tauschen) durch react-grid-layout ersetzt - Kacheln lassen sich jetzt frei auf einem 12-Spalten-Raster positionieren (nicht nur umsortieren) UND per Ecke ziehen dynamisch in der Größe ändern (nicht nur zwischen 4 festen Stufen wechseln). Layout-Modell in layout.ts auf x/y/w/h pro Kachel umgestellt (vorher Reihenfolge-Array + Größen-Enum). TileFrame vereinfacht (kein Drag-Griff/Größen-Button mehr nötig, das übernimmt die Grid-Lib direkt an der Kachel).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Root Cause fürs "sieht noch alt aus": global.css wurde separat unlayered importiert, Tailwind-Utilities liefen in layer(utilities). Unlayertes CSS gewinnt in der CSS-Kaskade IMMER gegen jeden Layer, unabhängig von Spezifität - die meisten neuen Tailwind-Klassen (text-4xl, rounded-lg, border-l-*, etc.) wurden dadurch von alten global.css-Regeln überstimmt, ohne sichtbaren Effekt. Fix: global.css läuft jetzt selbst in einem eigenen Layer ("global-css"), Layer-Reihenfolge explizit deklariert (global-css < theme < utilities) - Tailwind gewinnt jetzt wie vorgesehen. main.tsx importiert global.css nicht mehr separat, das übernimmt tailwind.css per @import layer(global-css).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
React.memo auf allen 8 Kachel-Komponenten + TileFrame, damit Interaktionen (Anpassen/Monitor/Größe/Ausblenden) nicht mehr alle Kacheln unnötig neu rendern. Voraussetzung dafür: onAusblendenUmschalten/onGroesseAendern als stabile useCallback-Referenzen mit id-Parameter statt pro Kachel neu erzeugter Closures. Die vier Layout-Mutationsfunktionen (dragEnde/ausblendenUmschalten/groesseAendern/layoutZuruecksetzen) über eine gemeinsame aktualisiereLayout-Helper-Funktion mit funktionalem setState dedupliziert (vermeidet stale-closure-Risiko bei schnellen Interaktionen). Instabile Index-Keys in PrueftermineTile/AufgabenTile durch stabile Keys ersetzt. maengelOffenListe in MaengelTile mit useMemo. Fehler-Anzeige bekommt "Erneut versuchen"-Button. Drag-Griff in TileFrame: type="button" + aria-roledescription für Screenreader.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Dashboard immer Startseite für Verantwortliche (Mitarbeiter -> Objekte), kaputte Startseiten-Auswahl entfernt. Neu gemeldete Mängel als eigener Hinweis. Kacheln per Anpassen-Modus verschieb-/ausblendbar (localStorage), Status-Monitor-Modus mit Vollbild und Auto-Refresh (60s). DashboardPage dabei in kleine Dateien pro Kachel gesplittet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Nutzer-Fund (Readiness-Backlog-Review): einsatzbereitschaft() zählte ein
Objekt ohne jede Kontrolle bisher als "einsatzbereit", weil keine Kriterien
verletzt waren - fachlich falsch, "nie geprüft" ist nicht dasselbe wie
"geprüft und in Ordnung".
Neuer vierter Zustand "unbekannt": Objekte ohne abgeschlossene Kontrolle UND
ohne sonstige Blocker fallen jetzt hierunter statt unter "einsatzbereit".
Objekte mit anderen Gründen (Fehlbestand/Mangel/Prüfung/...) bleiben davon
unberührt, die zählten schon vorher korrekt.
Tests angepasst: zwei bestehende Tests nutzten ungeprüfte Objekte und
erwarteten fälschlich "einsatzbereit" - jetzt entweder explizit kontrolliert
(Ist=Soll) oder auf "unbekannt" korrigiert, plus neuer Test für den Kernfall.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Nutzer-Fund: funkkenner (z.B. OPTA-Nummer) fehlte in fahrzeugdetails komplett
- eigenes Feld, nicht dasselbe wie funkrufname (gesprochener Rufname vs.
formale Funk-Identifikationsnummer im Digitalfunknetz).
Migration 0018, Model/Schema/Frontend-Formular ergänzt. Auch im Kachel-
Backlog (arbeitskacheln/05_fleet.md, FLEET-002) nachgezogen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Nutzer-Vorgabe: Design in Richtung OpenFleet (schlicht) - Schatten auf none
gesetzt, Radius 12px->8px, ruhigere Palette. Da Schatten/Radius komplett über
CSS-Variablen liefen, genügt die Token-Änderung, kein Component-Code
angefasst.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
4 Admin-Sektionen (ObjektSection, MaterialSection, ZustaendigkeitSection,
ObjektPositionenPanel) hatten identisches fest-20rem/12rem-Sidebar-neben-
Liste-Layout ohne Umbruch - auf Mobile lagen beide Spalten nebeneinander
breiter als der Bildschirm. Neue global.css-Klassen .split-layout/
-sidebar/-sidebar-sm/-content lösen das inline-Style-Duplikat auf und
klappen unter 768px automatisch auf eine Spalte.
Zusätzlich: .page-wide/.topbar/.card-grid/.row bekommen eigene 768px-
Anpassungen (kleinere Paddings/Schrift, Kacheln volle Breite, automatischer
Zeilenumbruch). Tabellen (Historie/Lagerbewegungen) waren schon per
overflow-x:auto abgesichert, keine Änderung nötig dort.
Mitarbeiter-Mobilflow (ObjektListPage/KontrollPage, 640px-Spalte) unverändert
- war laut Nutzer-Feedback bereits gut.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Farbpalette/Radius/Schatten überarbeitet (global.css), alle bestehenden
Klassennamen bleiben unverändert - keine Seite muss angepasst werden. Neue
Klassen .card-grid/.stat-number für einheitliche KPI-Kacheln (Dashboard,
Mängel-Übersicht) - Muster aus allen drei Referenzprojekten (Resgrid/
OpenFleet/Emergency_Management_and_Coordination_System): Statistik-Karten
sind dort durchgängig das zentrale Dashboard-Element.
Dashboard/Mängel/Fehlbestände-Seiten von page (640px, für Mitarbeiter-
Mobilflow gedacht) auf page-wide umgestellt - das sind Verantwortliche-
Screens mit Karten/Listen, die auf Desktop-Breite gehören (Analog AdminPage).
Hartcodierte Statusfarben (#b00020/#b8860b) durch CSS-Variablen ersetzt.
Mobile/responsive Feinschliff folgt in einem eigenen Durchgang (Nutzer-
Entscheidung: erst Desktop normal bauen).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Additiv neben dem bestehenden 4-Rollen-System (mitarbeiter/material-
verantwortlicher/leitungsverantwortlicher/administration bleiben unverändert,
kein Breaking Change): Admin kann jetzt eigene Rollen (z.B. "Materialwart",
"Helfer" aus der Ursprungs-Anforderung) mit frei wählbaren Einzelrechten aus
einem Berechtigungs-Katalog anlegen und Benutzern zuweisen - auch Benutzern
ganz ohne feste RolleTyp-Zuordnung.
Neue Tabellen: berechtigung (Katalog), rolle (custom, admin-anlegbar),
rolle_berechtigung (M:N), benutzer_rolle_zuordnung (M:N, eigene Tabelle statt
Wiederverwendung des ENUM-basierten benutzer_rolle).
require_roles_or_permission() kombiniert beide Systeme: bestehende feste
Rollen ODER eine passende granulare Berechtigung. Auf die vom Nutzer genannten
Beispiel-Endpunkte angewendet: Material anlegen/bearbeiten, Lagerbewegung
durchführen, Mangel melden/lesen/bearbeiten, Prüfung durchführen - weitere
Endpunkte folgen bei Bedarf nach demselben Muster (require_permission()/
require_roles_or_permission() stehen jetzt als Bausteine bereit).
Neue Endpunkte: GET /berechtigungen, CRUD /rollen, PUT/DELETE
/rollen/{id}/berechtigungen/{id}, PUT/DELETE /benutzer/{id}/rollen/{id}.
Frontend: neuer Admin-Tab "Rollen & Rechte" (Rolle anlegen, Rechte togglen,
Benutzer zuweisen/entfernen).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Bisher waren nur Kontrolle/Fehlbestand/Nachfüllung/Mindermenge/Geräteinstanz
im append-only Audit-Log (historie.log() war seit Prompt 13 nie flächendeckend
verdrahtet, docstring sagte das bereits so). Drei neue Ereignisse ergänzt:
- mangel_gemeldet / mangel_status_geaendert (Mangel-Modul)
- qualifikation_erfasst (Personal-Modul, sicherheitsrelevant: "wer hat wem
wann eine Qualifikation bestätigt" hängt fachlich direkt an "wer darf
fahren")
- objekt_geaendert (Status-/Fahrzeug-Zuordnungsänderungen via PATCH /objekte)
Lagerbewegung bewusst NICHT zusätzlich in historie dupliziert - hat bereits
eigenes vollständiges Audit-Trail (Wer/Wann/Von/Nach/Grund in eigener
Tabelle).
Frontend: Änderungslog-Filter um geraet_instanz/mangel/benutzer_qualifikation/
objekt ergänzt (geraet_instanz fehlte dort zuvor ebenfalls).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Objekt bleibt der Materialträger (Rucksack/Gerät/Fahrzeug, Karte 11) -
"Material verschieben" heißt: das Objekt bekommt einen neuen Standort, jede
Bewegung wird append-only protokolliert (Wer/Wann/Von/Nach/Grund, Nutzer-
Vorgabe Modul 5).
POST /objekte/{id}/lagerbewegungen setzt Standort + protokolliert in einem
Zug, GET /lagerbewegungen (Filter objekt_id/standort_id) für die Historie.
Rollen wie beim Mangel-Modul: Materialverantwortliche+Leitung+Admin dürfen
verschieben, alle Eingeloggten dürfen lesen.
Frontend: "Verschieben"-Aktion in ObjektSection, neuer Admin-Tab
"Lagerbewegungen" (reine Anzeige, analog HistorieSection).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Kennzahlen: bestehender Endpoint /dashboard/kennzahlen war seit Prompt 12.1
nie im Frontend verdrahtet - jetzt eigene Kachel (offene Fehlbestände,
genehmigte Mindermengen, kürzlich erledigt, problematische Objekte, Objekte
gesamt).
Mängel: eigene Übersicht (offen/kritisch-Zahl + aufklappbare Liste) zusätzlich
zur bisherigen Erwähnung als Grund in Kachel 1 - nutzt bestehendes GET
/maengel, kein neuer Backend-Code.
Qualifikationsablauf: neuer Endpoint /dashboard/qualifikationsablaeufe
(bevorstehende_qualifikationsablaeufe(), 60 Tage Vorlauf) - analog
bevorstehende_prueftermine, aber für BenutzerQualifikation.gueltig_bis
(Nutzer-Vorgabe: "wer verliert bald Führerschein/Lehrgang-Gültigkeit").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Neue Tabelle fahrzeugdetails (1:1 zu Objekt, additiv/optional): Kennzeichen,
Funkrufname, Baujahr, Kilometerstand, Betriebsstunden, nächste HU/UVV.
objekt_status um 'in_wartung' erweitert (Nutzer-Vorgabe: einsatzbereit/
eingeschränkt/nicht einsatzbereit/in Wartung/außer Dienst - die ersten drei
bleiben weiterhin rein rechnerisch über dashboard.einsatzbereitschaft(),
in_wartung/ausser_dienst sind die beiden manuell gesetzten Zustände).
dashboard.einsatzbereitschaft(): ausser_dienst-Objekte zählen nicht mehr mit,
in_wartung und überfällige HU/UVV sind neue Gründe für "nicht einsatzbereit".
dashboard.bevorstehende_prueftermine() (Kachel 2) zeigt jetzt Geräte- UND
Fahrzeugprüfungen (HU/UVV) in einer Liste (Nutzer-Vorgabe: ein Konzept für
"welche Prüfungen bald fällig sind") - Schema dafür verallgemeinert
(typ/bezeichnung/faelligkeitsdatum statt geräte-spezifischer Felder).
Frontend: Status-Dropdown + Fahrzeugdetails-Formular in ObjektSection
(nur für Objekttypen mit ist_zugfahrzeug=True), Dashboard-Kachel 2 zeigt
gemischte Liste.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Eigener Workflow für Schäden/Defekte, getrennt von Fehlbestand (Mengen-
Abweichung Soll/Ist). Status: neu → in_bearbeitung → ersatzteil_bestellt/
reparatur_geplant → erledigt. entitaet_typ/entitaet_id optional (Objektposition
oder Geräteinstanz), analog zum bestehenden historie.entitaet_typ-Muster,
objekt_id immer gesetzt für Dashboard-Abfragen.
Ein offener Mangel mit prioritaet=hoch macht das Objekt "nicht einsatzbereit"
(neuer Grund mangel_kritisch_offen in dashboard.einsatzbereitschaft) -
niedrige/normale Mängel blockieren nicht.
Melden/Lesen: alle Mitarbeiter+. Status ändern: Materialverantwortliche+.
Neue Seite /maengel für alle eingeloggten Nutzer (Nav-Link "Mängel").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Bisher nirgends vermerkt/abrufbar, wann ein Objekt zuletzt komplett
kontrolliert wurde (nur roh in kontrolle.beendet_am ableitbar). Neues Feld
ObjektRead.letzte_kontrolle_am (jüngste Kontrolle mit status=abgeschlossen),
angezeigt in Objektliste und Admin-Objektsektion ("noch nie kontrolliert"
falls keine).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
Objekttyp.ist_zugfahrzeug (Migration 0011) grenzt die Fahrzeug-Zuordnung
serverseitig auf tatsächliche Zugfahrzeuge ein (422 bei Verstoß), Frontend
filtert die Fahrzeug-Dropdowns entsprechend.
Neue Dashboard-Endpunkte /dashboard/einsatzbereitschaft und
/dashboard/prueftermine plus DashboardPage mit drei Kacheln
(Einsatzbereitschaft, Prüftermine, Ablaufdaten). Aktive
Mindermengen-Genehmigung zählt als "eingeschränkt einsatzbereit", nicht als
voll einsatzbereit (U2/Karte 08).
Neue FehlbestandListePage: Fehlbestände eigenständig nachfüllen/Mindermenge
genehmigen, ohne komplette Kontrolle des Objekts zu starten (Backend
existierte bereits, nur das Frontend fehlte).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KC8HYvv6UkCVYheYiTw9DD
89 "Blutdruckmanschette" <- 91 "Blutdruckmanschette ALT" zusammengelegt
(152 Materialien). Echter Konflikt an 7 Objekten geprüft und dokumentiert -
kein Datenverlust (Konfliktzeilen alle im Default-Zustand, einziger echter
Historie-Treffer korrekt umgehängt statt gelöscht).
ZustaendigkeitSection: gleiches zweispaltiges Layout mit Suche wie Material-/
Objektliste. screenshots/ ins .gitignore (lokale Debug-Bilder, nicht für
den Remote gedacht).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
ObjektPositionenPanel: Fach-Sidebar (Vorlagen-Feature "feste Fächer-Liste")
links, damit die Bestückung übersichtlich bleibt und auch Dritte (Vertretung
ohne die Kontrollperson) sehen, wo welches Material im Rucksack hingehört.
Gruppierungslogik aus KontrollPage in gemeinsames Util (utils/fachGruppierung)
ausgelagert, da jetzt zweimal gebraucht.
.page-wide: margin:0 statt zentriert (Nutzer-Vorgabe: bei 1200px sonst viel
verschenkter Platz links/rechts auf breiten Monitoren).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
ObjektPositionenPanel zeigte bisher nur ablauf_charge/geraet_sn-Positionen
und konnte weder Sollmenge-Override noch Status noch neues Material ändern
- Backend-Endpunkte (PATCH/POST .../positionen) existierten schon, waren im
Frontend nicht angebunden.
Jetzt: alle Positionen sichtbar, Sollmenge-Override je Position, Entfernen/
Reaktivieren (ist_status), zusätzliches Material hinzufügen (Dropdown mit
noch nicht vorhandenen Materialien + Sollmenge).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
.page-wide (max-width 1200px statt 640px) für die AdminPage - Tabellen
(Änderungslog) und mehrspaltige Formulare brauchen am Desktop mehr Platz
als die mobile Kontroll-Ansicht der Mitarbeiter. Objektliste/Kontrolle/
Login bleiben bei .page (640px, mobile-optimiert).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Bug: Frontend prüfte nur istAdmin, Materialverantwortliche und Leitungs-
verantwortliche sahen exakt dieselbe UI wie Mitarbeiter - obwohl das Backend
längst granular unterscheidet (require_roles je Endpunkt).
- AuthContext gibt volle Rollenliste + istVerantwortlich/istMaterial-
verantwortlich/istLeitungsverantwortlich weiter.
- /admin-Route für alle drei Rollen offen, AdminPage filtert Tabs nach
Rolle (Material/Leitung: Objekte-Pflege, Kontrollverantwortung,
Änderungslog; nur Administration: Stammdaten/Benutzer/Eskalation/
Zuständigkeit).
- ObjektSection blendet Anlegen/Duplizieren/Kopplung für Nicht-Admins aus
(waren admin-only im Backend, hätten 403 geworfen).
- Promise.allSettled statt Promise.all beim Laden, damit ein admin-only
Endpunkt (z.B. /benutzer) nicht die ganze Seite für niedrigere Rollen
blockiert.
- Backend: GET /benutzer jetzt auch für Material-/Leitungsverantwortliche
lesbar (für Kontrollverantwortung-Auswahl im Frontend nötig), Schreiben
bleibt admin-only.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Excel-Kontext (Rettungsrucksack.xlsx Zeile 50, direkt nach "Beatmungsbeutel
Kind") + Nutzer-Bestätigung: "Beatmunsmaske" (102) war die S-Größe, andere
Rucksäcke führen Beatmungsmaske S/M/L bereits einzeln. Zusammengelegt mit
161 "Beatmungsmasken S" (154 Materialien).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Live-Datenbereinigung (172 -> 155 Materialien): Excel-Import-Tippfehler und
Leerzeichen-Varianten zusammengelegt, Material 148 "Manometer SN 1308409"
korrigiert (Name -> "Manometer", materialtyp -> geraet_sn, SN als erste
geraet_instanz-Zeile). Skript und Entscheidungsprotokoll (inkl. bewusst
unverändert gelassener Fälle) dokumentiert für Nachvollziehbarkeit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Backend: geraet_instanz-CRUD (Liste/Anlegen/Status ändern inkl. optionalem
Fehlbestand pro Gerät/Löschen), pruefintervall_monate in Objektposition-
Schemas verdrahtet (Prüfdatum -> naechste_pruefung wird automatisch
berechnet, Monatsrechnung ohne neue Dependency).
Frontend: GeraeteInstanzenListe ersetzt das Einzel-SN-Feld im Admin-Portal
(ObjektPositionenPanel) durch eine Instanz-Liste mit Status/Prüfdatum/
Bemerkung je Exemplar - löst das Karte-14-Kernproblem (mehrere Geräte pro
Position). ObjektSection: Anhänger-Kopplung bei Anlage und nachträglich
änderbar.
Kontroll-Erfassung (PositionCard/KontrollPage) bleibt unverändert - die
komplett neue Erfassungs-UI für geraet_sn (Umsetzungsschritt 4, Karte 14)
ist bewusst nicht Teil dieses Commits.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Neuer Admin-Tab "Zuständigkeit": Benutzer materialverantwortlich für Standort
(vererbt sich auf alle Objekte dort) oder einzelnes Objekt zuordnen, Liste,
Entfernen. Backend-Endpunkte (inkl. DELETE) waren bereits vorhanden.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
useKontrolle lädt zusätzlich die Vorlage und mappt Fach je Material.
KontrollPage gruppiert die Erfassung nach Fach (Vorlagen-Reihenfolge,
"Ohne Fach" zuletzt, keine Überschrift falls alles ohne Fach) statt flacher
Liste - näher am Regal-/Fahrzeug-Layout. E2E-Mocks um Vorlage-Route ergänzt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
historie-Tabelle (append-only Audit-Trail über Kontrolle/Fehlbestand/
Nachfüllung/Mindermenge) war bisher nur intern befüllt, ohne API/UI. Neu:
GET /api/v1/historie (gefiltert nach Typ/ID/Benutzer/Zeitraum, paginiert,
Benutzername aufgelöst), Admin-Portal-Tab "Änderungslog" mit Typ-Filter.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
ApiClient nimmt jetzt einen injizierbaren http.Client entgegen (Testbarkeit,
kein Verhaltensunterschied im Betrieb). Tests mocken ihn mit mocktail statt
echter Netzwerkaufrufe. offline_queue_test.dart nutzt sqflite_common_ffi
(reines Dart) für die Queue-Logik ohne Gerät/Emulator: Erfolg entfernt
Eintrag, Netzwerkfehler bleibt "wartet", 4xx setzt "fehler" ohne Auto-Retry.
Dazu Widget-Tests für Login-Flow und Objektliste-Suchfilter.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
lib/offline/queue.dart (sqflite) analog zur React-PWA-Queue: enqueue statt
Direkt-Senden, Status wartet/wird_uebertragen/fehler, Idempotenz, Sync bei
Reconnect (connectivity_plus) + periodisch alle 15s. KontrollScreen gleicht
per 2s-Reconciliation-Loop den Anzeigezustand ab, "Kein Netz"-Banner.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
scripts/eskalation_pruefen.py ruft den Service direkt gegen die DB auf (kein
HTTP/Admin-Token nötig, gleiches Muster wie scripts/create_admin.py).
mabea-eskalation.timer läuft täglich 06:00 Uhr. Zeitschwellen bleiben in der
DB (eskalation_konfiguration), nicht im Timer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
Läuft komplett gegen gemockten Backend (e2e/mockApi.ts, page.route) - kein
echtes Backend/DB nötig, playwright.config.ts startet npm run dev automatisch.
Deckt Login-Erfolg/-Fehler, 409-Objektsperre mit Übernahme, hochzählende
Fortschrittsanzeige und Offline-Queue-Verhalten (kein Request während offline,
Sync bei Reconnect) ab.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV