Files
Dashboard/HANDOVER.md
T
dejanandClaude Opus 5 99be627dad refactor(dashboard): Seiten von der Datenquelle entkoppeln
Die Seiten holten ihre Zahlen selbst aus einem globalen Store. HiveGrid Cloud
verlangt reine Screens nach dem Muster <XPage vm={} actions={} /> — jede Seite
hätte beim Einbau auseinandergenommen werden müssen.

Neue Schichtung:

  view-models/<seite>.vm.ts   baue<Seite>VM(daten, filter) → VMState<…>
                              reine Funktion, kein React, kein Store
  pages/<Seite>.tsx           ({ vm, optionen }) → JSX      wird portiert
  routes/<Seite>Route.tsx     Store + Builder + Seite       wird ersetzt

view-models/index.ts spiegelt VMState wortgleich aus @hivegrid/view-models;
beim Einbau ist es ein Import-Wechsel. Der Loader ruft denselben Builder mit
echten Daten, die Seiten bleiben unberührt.

Dabei abgefallen: die fünf Seitenzustände (idle · loading · ready · empty ·
error) über SeitenZustand — bisher lief der Prototyp nur im Zustand "Daten da",
in der Cloud sind alle fünf DoD-Pflicht. Die Filterleiste bekommt ihre
Auswahllisten jetzt als Prop (in der Software die Facetten aus dem Loader)
statt sie selbst aus dem Datensatz zu ziehen.

Store- und Rohdatenzugriff gibt es damit ausschließlich in routes/. Alle sechs
Seiten rendern pixelgleich; typecheck, lint und build sind grün.

Nicht geändert: Next.js bleibt. Im gesamten Projekt gibt es vier
Next-spezifische Importe, alle in AppShell und app/layout — beide Dateien
entfallen beim Einbau ohnehin, weil die Cloud Shell und Root-Layout mitbringt.
Ein Framework-Wechsel wäre Aufwand ohne Ertrag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
2026-08-29 17:39:41 +02:00

129 lines
6.6 KiB
Markdown

# Übergabe an die Entwicklung
Dieser Prototyp liefert **Gestaltung und Interaktion**, nicht die Anbindung.
Was echt ist, was Attrappe ist und was beim Einbau zu tun bleibt:
---
## 1. Was fertig ist und übernommen werden kann
| Bereich | Datei(en) | Zustand |
|---|---|---|
| Kennzahlen-Berechnung | `features/dashboard/data/calculations.ts` | **Reine Funktionen** über typisierten Eingangsdaten — direkt übernehmbar |
| Chart-Theme | `features/dashboard/utils/chartTheme.ts` | Carbon-Tokens, Palette, Achsen-Props, Margins |
| Formatierung | `features/dashboard/utils/formatters.ts` | Deutsche Zahlen-, Währungs- und Datumsformate |
| Chart-Komponenten | `features/dashboard/components/charts/` | Vollständig, mit Leerzuständen und Tooltips |
| Karten & Tabellen | `features/dashboard/components/cards/`, `tables/` | Vollständig |
| Filtersystem | `features/dashboard/context/FilterContext.tsx`, `data/filtering.ts` | State und Relevanzlogik fertig |
| Seitenaufbau | `features/dashboard/pages/` | **Reine Komponenten** `({ vm })` — kein Store-Zugriff, direkt übernehmbar |
| View-Model-Builder | `features/dashboard/view-models/*.vm.ts` | Reine Funktionen `(daten, filter) → VMState<…>`; im Loader wiederverwendbar |
| Seitenzustände | `components/layout/SeitenZustand.tsx` | idle · loading · empty · error ausgestaltet |
---
## 2. Was echte Logik braucht
### 2.1 Datenquelle austauschen
Alles hängt an **einer** Stelle: `features/dashboard/data/seed.ts` exportiert
`DATENSATZ` vom Typ `Datensatz`. Diesen Export durch echte Daten ersetzen —
Loader, Server-Query oder Service, je nach Architektur. Alle Berechnungen und
Komponenten arbeiten unverändert weiter.
Feldzuordnung zu den HiveGrid-Entities steht in [`docs/DATENMODELL.md`](docs/DATENMODELL.md).
**Achtung Zeitbezug:** `features/dashboard/utils/dateUtils.ts` definiert
`HEUTE` als festen Stichtag (30.07.2026), damit der Prototyp reproduzierbar
bleibt. Beim Einbau durch `new Date()` ersetzen — die Konstante wird an
elf Stellen verwendet, alle über den Import.
### 2.2 Filter serverseitig anwenden
Aktuell wird komplett clientseitig gefiltert (`data/filtering.ts`). Bei echten
Datenmengen gehört das in die Query. Der State im `FilterContext` lässt sich
dafür in URL-Parameter serialisieren; die Relevanzlogik (`FILTER_RELEVANZ`)
bleibt clientseitig, weil sie nur die Anzeige steuert.
### 2.3 Aktionen ohne Wirkung
Diese Elemente lösen bewusst nur einen Hinweis-Toast aus:
| Element | Wo | Erwartetes Verhalten |
|---|---|---|
| Export (Seitenkopf) | `components/layout/PageHeader.tsx` | PDF-/CSV-Export der Ansicht |
| Teilen | `components/layout/PageHeader.tsx` | Link mit eingefrorenen Filtern |
| Aktualisieren | `components/layout/PageHeader.tsx` | Daten neu laden |
| „Als Bild exportieren" | `components/cards/ChartCard.tsx` | PNG-Export des Charts |
| Einträge in „Handlungsbedarf" | `components/cards/AlertList.tsx` | Absprung in die jeweilige Arbeitsliste — die Handlung je Zeile (`aktion`) beschreibt, was dort passieren soll |
| Tabellenzeilen | `components/tables/DataTable.tsx` | Absprung auf den Datensatz |
| Suche und Glocke (Kopfzeile) | `components/layout/AppShell.tsx` | Attrappe, gehört zur bestehenden Shell |
### 2.4 Was funktioniert und erhalten bleiben sollte
- **Zeitraum-Umschaltung** rechnet alle Kennzahlen und Charts neu, inklusive Trendvergleich zur Vorperiode
- **Mehrfachfilter** (Gewerk, Status, Quelle, Region, Mitarbeiter) mit entfernbaren Chips
- **Seitenabhängige Filterrelevanz** — irrelevante Filter werden gar nicht erst angeboten
- **Widgets ausblenden** über das ⋯-Menü jeder Karte, Wiedereinblenden über die Filterleiste, persistiert
- **Tabellen** sortieren, durchsuchen, blättern
- **Hover** auf Balken, Ringsegmenten, Trichterstufen und Verlaufspunkten
- **Legenden im Ring** heben beim Klick das jeweilige Segment hervor
- **Tastaturbedienung**: sichtbarer Fokus auf allen interaktiven Elementen, Escape schließt Dropdown und Karten-Menü
Persistiert wird in `localStorage` unter `hivegrid.analytics.*`. In der echten
Software gehört das an das Benutzerprofil.
---
## 3. Bewusste Entscheidungen
**Recharts statt der Subframe-Chart-Komponenten.** Die Subframe-Wrapper
(`ui/components/AreaChart` etc.) geben zu wenig Kontrolle über Achsen, Tooltips
und Ringproportionen, um die Carbon-Regeln umzusetzen. Die übrige Oberfläche
nutzt weiterhin die Subframe-Bibliothek und deren Design-Tokens.
**Deterministische Dummy-Daten.** Fester Seed statt `Math.random()`: Die Zahlen
bleiben über Reloads und Screenshots identisch, sonst ist keine Abstimmung über
konkrete Werte möglich.
**Team-Seite enthalten,** obwohl die Planung sie für v2 vorgesehen hatte.
Auslastung ist im Handwerk die Kennzahl, nach der disponiert wird — ohne sie
fehlt dem Dashboard der Bezug zum Tagesgeschäft.
**Kein Dark Mode.** Die Carbon-Palette ist hier im Light Theme hinterlegt.
Ein Dark Theme braucht die zweite Palettenvariante (in `@carbon/charts`
enthalten) und eigene Kontrastprüfung — bewusst nicht angefangen, statt es
halb zu machen.
---
## 4. Empfohlene Reihenfolge beim Einbau
1. `data/types.ts` gegen die echten Entity-Typen abgleichen
2. `data/seed.ts` durch die echte Datenquelle ersetzen
3. `HEUTE` in `utils/dateUtils.ts` auf `new Date()` umstellen
4. `view-models/index.ts` gegen `@hivegrid/view-models` tauschen — der Vertrag
(`VMState`) ist wortgleich, es ist ein Import-Wechsel
5. Eine Seite vollständig durchziehen (Übersicht): Loader ruft
`baueUebersichtVM()`, die Route rendert `<UebersichtSeite vm={…} />`. Der
Container unter `routes/` entfällt dabei, die Seite bleibt unverändert.
Dabei zeigt sich, welche Felder in den echten Daten fehlen
6. Texte auf i18n-Keys ziehen, `data-ui-id` ergänzen, `mobile:`-Verhalten
nachrüsten (siehe `docs/LEITPLANKEN.md`)
7. Filter serverseitig nachziehen, sobald die Datenmengen es erfordern
8. Export, Absprünge und Persistenz am Benutzerprofil ergänzen
---
## 5. Offene Punkte für die Fachabstimmung
- **Auslastung** rechnet produktive Stunden gegen Vertragsstunden. Ob Rüst- und
Fahrzeiten als produktiv zählen, ist eine betriebliche Festlegung — die
Schwellen (70 % / 95 %) stehen in `components/charts/AuslastungsBalken.tsx`.
- **Umsatz** ist hier Zahlungseingang, nicht Rechnungsstellung. Für die
betriebswirtschaftliche Auswertung kann die andere Definition gewünscht sein;
beide Varianten sind aus denselben Daten berechenbar.
- **Deckungsbeitrag** basiert auf einer geschätzten Marge je Auftrag. Ob echte
Kostendaten vorliegen, ist offen.
- **Förderquote BAFA/KfW** ist als eigener Block angelegt, weil sie im
Wärmepumpengeschäft entscheidend ist. Sie braucht ein Feld am Auftrag.