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
6.6 KiB
Ü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.
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
data/types.tsgegen die echten Entity-Typen abgleichendata/seed.tsdurch die echte Datenquelle ersetzenHEUTEinutils/dateUtils.tsaufnew Date()umstellenview-models/index.tsgegen@hivegrid/view-modelstauschen — der Vertrag (VMState) ist wortgleich, es ist ein Import-Wechsel- Eine Seite vollständig durchziehen (Übersicht): Loader ruft
baueUebersichtVM(), die Route rendert<UebersichtSeite vm={…} />. Der Container unterroutes/entfällt dabei, die Seite bleibt unverändert. Dabei zeigt sich, welche Felder in den echten Daten fehlen - Texte auf i18n-Keys ziehen,
data-ui-idergänzen,mobile:-Verhalten nachrüsten (siehedocs/LEITPLANKEN.md) - Filter serverseitig nachziehen, sobald die Datenmengen es erfordern
- 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.