feat(dashboard): Analytics-Dashboard als Gestaltungsvorlage zur Übergabe
UI/UX-Prototyp der Analytics-Dashboards in HiveGrid, fachlich auf einen SHK-/Handwerksbetrieb zugeschnitten. Ziel ist die Übergabe an die Entwicklung, nicht der Produktivbetrieb. Inhalt: sechs Seiten (Übersicht, Finanzen, Aufträge, Kunden, Team, Komponenten), 26 KPIs, neun Chart-Bausteine, Filtersystem mit seitenabhängiger Relevanz und ein deterministischer Demo-Datensatz (420 Kunden, 1.900 Aufträge, Stichtag 30.07.2026). Drei Quellen sind zusammengeführt: die fachliche Planung mit dem Branchenpartner (Januar 2026), die Subframe-Komponentenbibliothek aus dem HiveGrid-Pitch und IBM Carbon Data Visualization für die Diagrammregeln. Herkunft und bewusste Abweichungen stehen in docs/HERKUNFT.md. Dokumentation: README, HANDOVER (was echte Logik braucht), docs/DESIGN-SYSTEM, KPI-KATALOG, DATENMODELL, NAVIGATION, HERKUNFT, ZIELSYSTEM (Einbauort in HiveGrid Cloud) und LEITPLANKEN (welche Cloud-Screens als Vorbild taugen). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
This commit is contained in:
commit
b8bf987d0e
250 files changed
+33153
No files matched your search
@@ -0,0 +1,99 @@
|
||||
# Datenmodell
|
||||
|
||||
Definiert in `features/dashboard/data/types.ts`, erzeugt in `data/seed.ts`.
|
||||
Die Struktur ist bewusst nah an den HiveGrid-Entities gehalten, damit beim
|
||||
Einbau nur die Datenquelle getauscht werden muss.
|
||||
|
||||
---
|
||||
|
||||
## Zuordnung zu HiveGrid
|
||||
|
||||
| Prototyp | HiveGrid-Entity | Verwendete Felder |
|
||||
|---|---|---|
|
||||
| `Rechnung` | `invoice` | `amount`, `status`, `issuedAt`, `dueDate`, bezahlt-am |
|
||||
| `Auftrag` | `order` | `totalValue`, `status`, `startDate`, `endDate`, Gewerk, Marge, Förderung |
|
||||
| `Kunde` | `company` | `name`, `source`, `createdAt`, Typ, Region |
|
||||
| `Mitarbeiter` | `TenantUser` + `User` | Name, Rolle, Gewerk, Status, Wochenstunden |
|
||||
| `Zeiteintrag` | `timeEntry` | `hours`, `date`, Auftragsbezug, Abwesenheitsgrund |
|
||||
|
||||
**Felder, die es so noch nicht gibt** und beim Einbau ergänzt werden müssen:
|
||||
|
||||
| Feld | Wo | Wofür |
|
||||
|---|---|---|
|
||||
| `gewerk` | Auftrag, Mitarbeiter | Alle Gewerke-Auswertungen und der Gewerk-Filter |
|
||||
| `margeProzent` | Auftrag | Deckungsbeitrag |
|
||||
| `foerderung` / `foerderbetrag` | Auftrag | Förderquote BAFA/KfW |
|
||||
| `verlorenAb` | Auftrag | Ehrlicher Vertriebstrichter (siehe unten) |
|
||||
| `ueberAngebot` | Auftrag | Trennt Angebotsgeschäft von Direktaufträgen |
|
||||
| `wochenstunden` | Mitarbeiter | Auslastungsberechnung |
|
||||
| `zahlungsmoralTage` | Kunde | Auffällige Zahlungsmoral (auch aus Rechnungen ableitbar) |
|
||||
|
||||
---
|
||||
|
||||
## Statuswerte
|
||||
|
||||
**Auftrag:** `anfrage` → `aufmass` → `angebot` → `beauftragt` → `in_arbeit` →
|
||||
`abgeschlossen`, dazu `verloren` als Endzustand.
|
||||
|
||||
`verlorenAb` hält fest, welche Stufe ein verlorener Vorgang zuletzt erreicht
|
||||
hat. Ohne dieses Feld müsste der Trichter raten und würde die oberen Stufen
|
||||
systematisch zu klein zeigen.
|
||||
|
||||
`ueberAngebot` trennt das Angebotsgeschäft (ab etwa 6.000 €) von Aufträgen, die
|
||||
direkt beauftragt werden — Wartung, Notdienst, E-Check. Trichter und
|
||||
Abschlussquote beziehen sich ausschließlich auf das Angebotsgeschäft.
|
||||
|
||||
**Rechnung:** `entwurf` · `gestellt` · `bezahlt` · `ueberfaellig`.
|
||||
|
||||
---
|
||||
|
||||
## Gewerke und Leistungsarten
|
||||
|
||||
| Gewerk | Leistungsarten |
|
||||
|---|---|
|
||||
| Heizung | Wärmepumpe, Gastherme, Heizkörper, Wartung |
|
||||
| Sanitär | Bad-Sanierung, Rohrleitungen, Armaturen, Notdienst |
|
||||
| Klima & Lüftung | Splitgerät, Lüftungsanlage, Kanalreinigung, Wartung |
|
||||
| Photovoltaik | PV-Anlage, Batteriespeicher, Wallbox, Wartung |
|
||||
| Elektro | Elektroinstallation, Zählerschrank, Smart Home, E-Check |
|
||||
|
||||
Förderfähig (BAFA/KfW) sind Wärmepumpe, PV-Anlage, Batteriespeicher und
|
||||
Lüftungsanlage.
|
||||
|
||||
---
|
||||
|
||||
## Der Demo-Datensatz
|
||||
|
||||
Erzeugt mit festem Seed (`mulberry32`, Seed `20260730`) — die Zahlen bleiben
|
||||
über Reloads und Screenshots identisch. Bezugsdatum ist der **30.07.2026**
|
||||
(`HEUTE` in `utils/dateUtils.ts`).
|
||||
|
||||
Fiktiver Betrieb: **Meister Krause GmbH**, Raum Rhein-Main, 24 Beschäftigte
|
||||
(3 Meister, 14 Monteure, 3 Auszubildende, 4 Büro).
|
||||
|
||||
| Größe | Wert |
|
||||
|---|---|
|
||||
| Kunden | 420 |
|
||||
| Aufträge (24 Monate) | 1.900 |
|
||||
| Rechnungen | ~1.270 |
|
||||
| Zeiteinträge (12 Wochen) | ~1.500 |
|
||||
| Jahresumsatz | ~2,4 Mio. € |
|
||||
| Offene Forderungen | ~230 T€ |
|
||||
| Abschlussquote (Angebotsgeschäft) | ~38 % |
|
||||
| Ø Auslastung | ~73 % |
|
||||
|
||||
**Eingebaute Effekte**, damit die Charts nicht künstlich flach aussehen:
|
||||
|
||||
- **Saisonalität** — Heizung und Lüftung ziehen von September bis Februar an,
|
||||
der Hochsommer ist schwächer (`SAISON_INDEX`)
|
||||
- **Wachstum** — rund 12 % gegenüber dem Vorjahr
|
||||
- **Größenabhängige Abschlussquote** — Wartung und Notdienst werden fast immer
|
||||
ausgeführt, große Anlagen dagegen verglichen und häufiger verloren
|
||||
- **Zahlungsverhalten** — die Mehrheit zahlt nahe am Zahlungsziel (21 Tage),
|
||||
ein kleiner Rest wird zum Mahnfall; Forderungen älter als sieben Monate sind
|
||||
eingebracht oder ausgebucht und stehen nicht mehr in der offenen Liste
|
||||
- **Förderanträge** — nur bei Vorgängen der letzten rund 100 Tage offen; ein
|
||||
Bescheid dauert Wochen, nicht Jahre
|
||||
|
||||
Diese Effekte sind fachlich begründet, nicht kosmetisch: Ein gleichverteilter
|
||||
Zufallsdatensatz erzeugt Charts, an denen sich keine Gestaltung prüfen lässt.
|
||||
@@ -0,0 +1,248 @@
|
||||
# Design-System: Datenvisualisierung
|
||||
|
||||
Grundlage ist **IBM Carbon Data Visualization**, ergänzt um die Spacing- und
|
||||
Zahlenregeln aus der eigenen Planung (`UI_UX_GUIDELINES.md`, Januar 2026).
|
||||
|
||||
Die konkreten Farb- und Linienwerte sind aus dem ausgelieferten Stylesheet von
|
||||
`@carbon/charts` (`dist/styles.css`, Light Theme) übernommen — nicht aus der
|
||||
Dokumentation abgeschrieben. Alles Folgende ist in
|
||||
`features/dashboard/utils/chartTheme.ts` als Code hinterlegt.
|
||||
|
||||
---
|
||||
|
||||
## 1. Farben
|
||||
|
||||
### Kategoriale Palette (14 Farben, Reihenfolge verbindlich)
|
||||
|
||||
```
|
||||
1 #6929c4 2 #1192e8 3 #005d5d 4 #9f1853 5 #fa4d56
|
||||
6 #520408 7 #198038 8 #002d9c 9 #ee5396 10 #b28600
|
||||
11 #009d9a 12 #012749 13 #8a3800 14 #a56eff
|
||||
```
|
||||
|
||||
Die Reihenfolge ist auf Unterscheidbarkeit optimiert — auch bei
|
||||
Farbfehlsichtigkeit und in Graustufen. Farben deshalb **immer von vorne
|
||||
vergeben**, nie nach Geschmack auswählen und nie umsortieren.
|
||||
|
||||
**Maximal fünf Kategorien je Chart.** Alles darüber fasst
|
||||
`gruppiereRest()` zu „Sonstige" zusammen. Ein Ring mit zwölf Segmenten ist
|
||||
keine Information mehr, sondern Dekoration.
|
||||
|
||||
### Statusfarben (nur für Aussagen, nie für Kategorien)
|
||||
|
||||
| Bedeutung | Wert |
|
||||
|---|---|
|
||||
| positiv | `#198038` |
|
||||
| Warnung | `#b28600` |
|
||||
| negativ | `#da1e28` |
|
||||
| neutral | `#8d8d8d` |
|
||||
| Information | `#1192e8` |
|
||||
|
||||
Kategorie- und Statusfarben nie im selben Chart mischen — eine grüne Kategorie
|
||||
neben einem grünen Positiv-Status ist nicht mehr eindeutig lesbar.
|
||||
|
||||
**Rangfolgen einfarbig.** Top-Kunden oder Aufträge je Mitarbeiter sind keine
|
||||
Kategorien: Dort trägt die Farbe keine Information, und eine bunte Palette
|
||||
erzeugt nur Unruhe. Palette nur, wenn die Farbe etwas bedeutet.
|
||||
|
||||
### Feste Farbzuordnung
|
||||
|
||||
Wiederkehrende Kategorien behalten ihre Farbe über alle Charts und Seiten
|
||||
hinweg (`GEWERK_FARBEN`, `STATUS_KATEGORIE_FARBEN` in `chartTheme.ts`). Sonst
|
||||
ist Heizung im Ring lila und im Balkendiagramm blau, und jede Legende muss neu
|
||||
gelesen werden.
|
||||
|
||||
| Gewerk | Farbe | | Status | Farbe |
|
||||
|---|---|---|---|---|
|
||||
| Heizung | `#6929c4` | | Verloren / Überfällig | `#fa4d56` / `#da1e28` |
|
||||
| Sanitär | `#1192e8` | | In Arbeit / Bezahlt | `#198038` |
|
||||
| Klima & Lüftung | `#005d5d` | | Beauftragt | `#009d9a` |
|
||||
| Photovoltaik | `#9f1853` | | Angebot | `#6929c4` |
|
||||
| Elektro | `#198038` | | Entwurf / Sonstige | `#8d8d8d` |
|
||||
|
||||
Bei Statuswerten trägt die Farbe Bedeutung: „Verloren" und „Überfällig" sind
|
||||
rot, „Bezahlt" grün. Eine zufällige Palettenfarbe für „Verloren" würde die
|
||||
Aussage der Grafik verwässern.
|
||||
|
||||
### Geordnete Dimensionen
|
||||
|
||||
Altersklassen, Risikostufen und Ähnliches sind **keine Kategorien**, sondern eine
|
||||
Reihenfolge mit wachsendem Risiko. Dafür gilt die vierstufige Risikorampe
|
||||
(`RISIKO_RAMPE` in `chartTheme.ts`) — durchgehend Statusfarben, keine
|
||||
Palettenfarbe dazwischen:
|
||||
|
||||
| Stufe | Wert | Bedeutung |
|
||||
|---|---|---|
|
||||
| 1 | `#198038` | im Zahlungsziel |
|
||||
| 2 | `#b28600` | beobachten |
|
||||
| 3 | `#ba4e00` | mahnen |
|
||||
| 4 | `#da1e28` | Abschreibungsrisiko |
|
||||
|
||||
### Einzelserien
|
||||
|
||||
| Rolle | Wert |
|
||||
|---|---|
|
||||
| Hauptreihe (Umsatzverlauf o. Ä.) | `#1192e8` |
|
||||
| Vergleichsreihe (Vorjahr) | `#8d8d8d`, gestrichelt |
|
||||
|
||||
Die Vergleichsreihe ist bewusst neutral: Sie soll einordnen, nicht konkurrieren.
|
||||
|
||||
---
|
||||
|
||||
## 2. Achsen und Gitter
|
||||
|
||||
| Element | Wert | Begründung |
|
||||
|---|---|---|
|
||||
| Gitterlinien | `#e0e0e0`, **nur horizontal** | Vertikale Linien zerschneiden den Verlauf |
|
||||
| Achsenlinie | `#8d8d8d`, 1 px | Nur an der X-Achse; die Y-Achse braucht keine |
|
||||
| Tick-Marks | keine | Die Beschriftung reicht als Orientierung |
|
||||
| Beschriftung | 12 px, `#525252` | Carbon-Standard |
|
||||
| Y-Achsen-Breite | 58 px | Platz für „245 T€" ohne Umbruch |
|
||||
|
||||
Bei mehr als 12 Datenpunkten wird nur jede n-te Beschriftung gezeigt, sonst
|
||||
überlappen sie (`MAX_ACHSENLABELS` in `chartTheme.ts` — eine Konstante für
|
||||
alle Diagrammtypen).
|
||||
|
||||
**Achsenwerte sind rund.** Recharts leitet Ticks aus `max/(n-1)` ab und rundet
|
||||
den Rohschritt auf; bei 219 T€ ergibt das 0/55/110/165/220. Krumme Achsenwerte
|
||||
kosten den Leser bei jedem Blick eine Rechenoperation. `nettesRaster()` erzwingt
|
||||
deshalb Schrittweiten aus der 1-2-2,5-5-Reihe.
|
||||
|
||||
**Die Einheit gehört zur Achse, nicht zum Wert.** `achsenFormat(max)` wählt sie
|
||||
einmal aus dem Achsenmaximum. Sonst steht „0 €" neben „10 T€" und die Skala
|
||||
wechselt mitten im Lesen die Einheit; die Nulllinie trägt gar keine Einheit.
|
||||
|
||||
---
|
||||
|
||||
## 3. Tooltip
|
||||
|
||||
Einheitlich für alle Charts (`components/charts/ChartTooltip.tsx`):
|
||||
|
||||
- Fläche `#393939`, Text `#ffffff`
|
||||
- Label 12 px mit 80 % Deckkraft, Wert 13 px fett in Tabellenziffern
|
||||
- kein Rahmen, kein Doppelpunkt zwischen Name und Wert
|
||||
- Farbmarker je Reihe, 10 × 10 px
|
||||
|
||||
Ein gemeinsamer Tooltip verhindert, dass jedes Chart eigene Regeln entwickelt.
|
||||
|
||||
---
|
||||
|
||||
## 4. Balken, Flächen, Ringe
|
||||
|
||||
| Element | Regel |
|
||||
|---|---|
|
||||
| Balkenbreite | maximal 48 px |
|
||||
| Balkenabstand | mindestens halbe Balkenbreite (`barCategoryGap: 28 %`) |
|
||||
| Balkenecken | 2 px, nur an der Werteseite |
|
||||
| Linienstärke Fläche | 2 px, Verlaufsfüllung 24 % → 2 % |
|
||||
| Interpolation | **linear** — eine Monotone-Kurve erfindet Zwischenwerte zwischen zwei Messpunkten |
|
||||
| Punkte | nur beim Hovern, Radius 4, weißer Rand |
|
||||
| Ring innen/außen | 62 % / 88 %, 2° Segmentabstand |
|
||||
| Ringmitte | Gesamtwert — sonst muss man die Segmente addieren |
|
||||
| Ringlegende | rechts, einzeilig, mit Wert und Prozentanteil |
|
||||
|
||||
Ringsegmente starten oben (12 Uhr) mit dem größten Wert und laufen im
|
||||
Uhrzeigersinn absteigend. Auch „Sonstige" wird nach Größe einsortiert, nicht
|
||||
ans Ende gehängt.
|
||||
|
||||
**Ein Ring braucht ein echtes Ganzes.** Beträgt ein Segment über 80 %, trägt die
|
||||
Grafik nichts mehr bei — dann ist die Bezugsgröße falsch gewählt. Beispiel: Der
|
||||
Rechnungsbestand aus zwei Jahren gegen den heutigen Forderungsstand ergibt
|
||||
92 % „Bezahlt". Richtig ist der offene Bestand allein; dann zeigt der Ring, wie
|
||||
viel davon bereits überfällig ist.
|
||||
|
||||
### Werte in einem engen Band: Punkt statt Balken
|
||||
|
||||
Auslastung liegt betrieblich zwischen etwa 70 und 100 %. Ein Balken **muss** bei
|
||||
Null beginnen, sonst verzerrt seine Länge — und dann sind 74 % und 96 % optisch
|
||||
fast gleich lang. Ein Punkt kodiert über die **Position**, nicht über die Länge,
|
||||
und darf deshalb einen ausgeschnittenen Bereich zeigen.
|
||||
|
||||
Der Zielkorridor liegt als graues Band hinter den Punkten, die 100-%-Marke als
|
||||
beschriftete Linie, darunter eine Achse. Damit erklärt sich die Grafik ohne
|
||||
Ampel-Legende — und tote Legendeneinträge („Luft frei", wenn niemand darunter
|
||||
liegt) entfallen.
|
||||
|
||||
---
|
||||
|
||||
## 5. Abstände (8-px-System)
|
||||
|
||||
| Bereich | Wert |
|
||||
|---|---|
|
||||
| Seitenrand | 24 px |
|
||||
| Abstand zwischen Karten | 16 px |
|
||||
| Innenabstand Karte | 16 px |
|
||||
| Kopf zu Inhalt in der Karte | 12 px |
|
||||
| Label zu Wert in der KPI-Karte | 4 px |
|
||||
| Icon zu Text | 8 px |
|
||||
|
||||
**Grundregel: innen ≤ außen.** Ist der Innenabstand größer als der Abstand
|
||||
zwischen den Karten, zerfällt das Raster optisch.
|
||||
|
||||
---
|
||||
|
||||
## 6. Typografie
|
||||
|
||||
| Rolle | Größe | Gewicht |
|
||||
|---|---|---|
|
||||
| Seitentitel | 22 px | 600 |
|
||||
| KPI-Wert | 28 px | 600 |
|
||||
| Kartentitel | 14 px | 600 |
|
||||
| Fließtext / Tabelle | 13 px | 400 |
|
||||
| Label, Achsen, Legende | 12 px | 400–500 |
|
||||
| Kleinstschrift (Meta) | 11 px | 400 |
|
||||
|
||||
Eine Schriftfamilie (Inter). Zahlen immer in Tabellenziffern
|
||||
(`font-variant-numeric: tabular-nums`, global gesetzt), damit Spalten
|
||||
untereinander stehen.
|
||||
|
||||
---
|
||||
|
||||
## 7. Zahlenformate (deutsch)
|
||||
|
||||
| Fall | Ausgabe |
|
||||
|---|---|
|
||||
| Währung, KPI | `238.412 €` |
|
||||
| Währung, kompakt (Achsen, Balken) | `8,4 T€` · `245 T€` · `1,25 Mio. €` |
|
||||
| Prozent | `58,2 %` |
|
||||
| Tage / Stunden | `34,2 Tage` · `7,5 h` |
|
||||
| Datum | `30.07.2026`, kurz `30.07.`, Monat `Jul 26` |
|
||||
|
||||
Punkt als Tausendertrenner, Komma als Dezimaltrenner. Innerhalb einer Ansicht
|
||||
immer dasselbe Format — nicht in einer Zeile `245.000 €` und in der nächsten
|
||||
`245 T€`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Zustände
|
||||
|
||||
Jedes Chart braucht einen **Leerzustand** — Filter können jede Ansicht leer
|
||||
laufen lassen. `ChartLeer` zeigt dann eine gestrichelte Fläche mit Hinweistext
|
||||
statt eines leeren Rechtecks.
|
||||
|
||||
Hover-Zustände: Karten erhalten einen stärkeren Schatten, Balken und
|
||||
Ringsegmente werden hervorgehoben (die übrigen auf 35–45 % Deckkraft).
|
||||
Tabellenzeilen bekommen **keinen** Hover, solange sie nicht anklickbar sind —
|
||||
ein Hover ohne Ziel verspricht eine Interaktion, die es nicht gibt.
|
||||
|
||||
## 9. Fokus und Kontrast
|
||||
|
||||
**Fokus** ist eine einzige Regel in `app/globals.css` statt `focus:`-Klassen an
|
||||
jedem Element:
|
||||
|
||||
```css
|
||||
:where(a, button, [role="button"], input, select, textarea, [tabindex]):focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--shadow-focus);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
```
|
||||
|
||||
`:focus-visible` zeigt den Ring nur bei Tastaturbedienung. Overlays (Filter-
|
||||
Dropdown, Karten-Menü) schließen mit **Escape** und geben den Fokus an den
|
||||
auslösenden Knopf zurück.
|
||||
|
||||
**Kontrast:** `--color-text-neutral-tertiary` liegt auf `neutral-700`
|
||||
(5,15:1 auf Weiß), nicht auf `neutral-600` (3,68:1 — unter WCAG AA). Das
|
||||
entspricht dem a11y-Override im Zielsystem. `text-neutral-subtle` ist ein
|
||||
Placeholder-Token und für lesbaren Text nicht geeignet.
|
||||
@@ -0,0 +1,173 @@
|
||||
# Herkunft
|
||||
|
||||
Woher die Gestaltung dieses Prototyps stammt, was aus welcher Quelle übernommen
|
||||
wurde und wo bewusst abgewichen wurde.
|
||||
|
||||
Der Zweck: Wer beim Einbau eine Entscheidung anzweifelt, soll nachlesen können,
|
||||
ob sie begründet ist oder nur mitgeschleppt wurde. Vieles hier sieht auf den
|
||||
ersten Blick wie eine Abweichung von der Januar-Planung aus und ist eine
|
||||
absichtliche Korrektur.
|
||||
|
||||
---
|
||||
|
||||
## Die drei Quellen
|
||||
|
||||
| Quelle | Was daraus stammt | Stand |
|
||||
|---|---|---|
|
||||
| **Fachliche Planung** mit dem Branchenpartner | Seitenschnitt, KPI-Auswahl, Filtersystem, Spacing- und Zahlenregeln | Januar 2026 |
|
||||
| **HiveGrid-Pitch-Prototyp** | Komponentenbibliothek `ui/`, Design-Tokens, Icon-Set, Gestaltungssprache der Oberfläche | Juli/August 2026 |
|
||||
| **IBM Carbon Data Visualization** | Chart-Regeln, Farbpalette, Achsen, Tooltips, Kategorien-Obergrenze | laufend |
|
||||
|
||||
Die ersten beiden liegen als lokale Ordner außerhalb dieses Repos und sind
|
||||
**nicht Teil der Übergabe** — alles fachlich Relevante daraus ist in `docs/`
|
||||
überführt. Die Zuordnung unten ist die Brücke zurück, falls jemand die
|
||||
Originale doch braucht.
|
||||
|
||||
---
|
||||
|
||||
## 1. Fachliche Planung (Januar 2026)
|
||||
|
||||
Ordner `dashboard-planung/`, entstanden mit dem Branchenpartner. Drei Teile
|
||||
sind hier eingeflossen:
|
||||
|
||||
| Teil der Planung | Wirkung in diesem Repo |
|
||||
|---|---|
|
||||
| `01_CRM-Analytics-Clickdummy/` — React/Vite-Click-Dummy | Seitenschnitt, Komponentenzuschnitt, Filterarchitektur |
|
||||
| `UI_UX_GUIDELINES.md` | 8-px-Raster, Regel „innen ≤ außen", Zahlenformate, KPI-Karten-Aufbau |
|
||||
| `03_Planung-und-Konzept/Analytics/REQUIREMENTS-analytics.md` | Anforderungs-IDs (INFRA, DASH, FIN, ORD, CUST, COMP, FILT) |
|
||||
|
||||
Die Anforderungs-IDs stehen an mehreren Stellen als Kommentar im Code
|
||||
(`FilterContext.tsx` → INFRA-01/02, `filtering.ts` → FILT-05,
|
||||
`AnalyticsToolbar.tsx` → FILT-06). Sie sind der Faden zurück in die Planung.
|
||||
|
||||
### Übernommen
|
||||
|
||||
- **Fünf Analyse-Seiten** in derselben Reihenfolge: Übersicht, Finanzen,
|
||||
Aufträge, Kunden, Team
|
||||
- **Komponentenzuschnitt**: KPICard, ChartCard, AlertList, DataTable,
|
||||
Filterleiste, Mehrfachauswahl mit Suche — dieselbe Aufteilung, neu geschrieben
|
||||
- **Filterarchitektur**: globaler Filter-State über alle Seiten, in
|
||||
`localStorage` persistiert, aktive Einschränkungen als entfernbare Chips
|
||||
- **Seitenabhängige Filterrelevanz** (FILT-05) als Konzept
|
||||
- **Recharts** als Chart-Bibliothek — dieselbe Wahl wie im Click-Dummy
|
||||
- **8-px-Raster und deutsche Zahlenformate** aus `UI_UX_GUIDELINES.md`,
|
||||
siehe [DESIGN-SYSTEM.md](DESIGN-SYSTEM.md) Abschnitt 5 und 7
|
||||
|
||||
### Bewusst anders
|
||||
|
||||
| Thema | Click-Dummy (Januar) | Hier | Grund |
|
||||
|---|---|---|---|
|
||||
| **Chart-Farben** | 12 kräftige Farben, Regel „keine Grautöne, wirken traurig" | Carbon-Palette, Grau ausdrücklich für Vergleichsreihen und Neutrales | Die Januar-Palette ist auf Freundlichkeit optimiert, nicht auf Unterscheidbarkeit. Carbon ist auf Farbfehlsichtigkeit und Graustufen geprüft. Grau ist kein Makel, sondern das Mittel, mit dem eine Vergleichsreihe zurücktritt |
|
||||
| **Irrelevante Filter** | rechts ausgegraut angezeigt | werden gar nicht erst angeboten | Ein sichtbarer Filter, der nichts bewirkt, kostet Vertrauen in die Zahlen |
|
||||
| **Schriftgrößen** | zur Laufzeit verstellbar (`TypographyContext` + Dev-Panel), KPI-Wert 37 px | feste Werte, KPI-Wert 28 px | Das Panel war ein Abstimmungswerkzeug. Die Abstimmung ist erfolgt; die Werte gehören jetzt fest ins Design-System |
|
||||
| **Chart-Elemente** | Drop-Shadow statt Rahmen | flach, Trennung über Weißraum und Segmentabstand | Schatten auf Datenflächen erzeugen eine Tiefe, die keine Bedeutung trägt |
|
||||
| **Fachlichkeit** | generisches CRM: Abteilungen, Bewertungen, Kundenkontakte | SHK-Handwerk: Gewerke, Förderung, Auslastung, Angebotsgeschäft | Auftrag dieses Prototyps |
|
||||
| **Bezeichner** | englisch (`revenue`, `orders`) | deutsch (`umsatz`, `auftraege`) | Die Oberfläche ist deutsch. Ein Bruch in der Mitte erzwingt beim Einbau ständiges Übersetzen |
|
||||
| **Datensatz** | 25 Mitarbeiter, 200 Kunden, 800 Aufträge, gleichverteilt | 24, 420, 1.900 — mit Saisonalität, Wachstum, Zahlungsverhalten | An einem gleichverteilten Datensatz lässt sich keine Gestaltung prüfen, siehe [DATENMODELL.md](DATENMODELL.md) |
|
||||
|
||||
### Anforderungen, die hier nicht umgesetzt sind
|
||||
|
||||
Die Planung führt 47 v1-Anforderungen als vollständig. In diesem Prototyp
|
||||
fehlen vier davon bewusst:
|
||||
|
||||
| ID | Anforderung | Warum nicht |
|
||||
|---|---|---|
|
||||
| INFRA-03 | Filter-Presets speichern und laden | Bedienkomfort ohne Gestaltungsfrage. Gehört in die echte Software ans Benutzerprofil, nicht in die Vorlage |
|
||||
| INFRA-04 (teilweise) | Zeitraum mit benutzerdefiniertem Bereich | Die sechs Presets decken die Abstimmung ab; ein Datumsbereichs-Picker ist eine Umsetzungs-, keine Gestaltungsfrage |
|
||||
| FILT-02 / FILT-04 | Filter für Rechnungsstatus und Betragsbereich | Rechnungsstatus ist auf der Finanzseite bereits als Chart-Dimension sichtbar; ein Betragsfilter verzerrt Summen-KPIs, ohne dass es an der Karte steht |
|
||||
| CUST-03 | KPI „Kontakte gesamt" | Der Prototyp führt keine Kontakt-Entität. In HiveGrid existiert sie — die Karte lässt sich beim Einbau ergänzen |
|
||||
|
||||
Umgekehrt ist **Team** enthalten, obwohl die Planung es unter v2 führt
|
||||
(TEAM-01 bis TEAM-03): Auslastung ist im Handwerk die Kennzahl, nach der
|
||||
disponiert wird.
|
||||
|
||||
---
|
||||
|
||||
## 2. HiveGrid-Pitch-Prototyp
|
||||
|
||||
Der Next.js-Prototyp aus dem Kundentermin (Juli 2026). Von dort stammt die
|
||||
gesamte Oberfläche außerhalb der Diagramme.
|
||||
|
||||
| Übernommen | Wo |
|
||||
|---|---|
|
||||
| Komponentenbibliothek — 86 Komponenten, 4 Layouts | `ui/components/`, `ui/layouts/` |
|
||||
| Design-Tokens (Farben, Radien, Schatten, Typo-Skala) | `ui/theme.css`, eingebunden in `app/globals.css` |
|
||||
| Icon-Set (Feather über `@subframe/core`) | überall, aufgelöst in `components/layout/FeatherIcon.tsx` |
|
||||
| Stack | Next.js 16 · React 19 · Tailwind v4 — gleiche Versionen |
|
||||
|
||||
`ui/` ist **byteidentisch** mit dem Stand im Pitch-Repo und wurde nicht
|
||||
angefasst. Die Bibliothek wird dort über die Subframe-CLI aus dem Projekt
|
||||
*Basedesign* synchronisiert:
|
||||
|
||||
```bash
|
||||
npx @subframe/cli@latest sync --all
|
||||
```
|
||||
|
||||
Wer die Bibliothek aktualisiert, sollte das im Pitch-Repo tun und den Ordner
|
||||
hierher kopieren — sonst laufen die beiden Stände auseinander.
|
||||
|
||||
### Was aus der Bibliothek nicht verwendet wird
|
||||
|
||||
`ui/components/` enthält eigene Chart-Wrapper (`AreaChart`, `BarChart`,
|
||||
`LineChart`, `PieChart`). Sie sind hier **nicht** im Einsatz. Sie geben zu
|
||||
wenig Kontrolle über Achsen, Tooltips und Ringproportionen, um die
|
||||
Carbon-Regeln umzusetzen. Die Diagramme unter
|
||||
`features/dashboard/components/charts/` setzen deshalb direkt auf Recharts auf.
|
||||
|
||||
Das ist die einzige Stelle, an der dieser Prototyp von der Pitch-Bibliothek
|
||||
abweicht — überall sonst gilt sie.
|
||||
|
||||
---
|
||||
|
||||
## 3. IBM Carbon Data Visualization
|
||||
|
||||
Grundlage aller Diagramm-Entscheidungen. Ausformuliert in
|
||||
[DESIGN-SYSTEM.md](DESIGN-SYSTEM.md), als Code hinterlegt in
|
||||
`features/dashboard/utils/chartTheme.ts`.
|
||||
|
||||
Die Farb- und Linienwerte sind aus dem ausgelieferten Stylesheet von
|
||||
`@carbon/charts` (`dist/styles.css`, Light Theme) übernommen — nicht aus der
|
||||
Dokumentation abgeschrieben. Das Paket selbst ist **keine Abhängigkeit** dieses
|
||||
Repos; gezogen wurden nur die Werte.
|
||||
|
||||
| Von Carbon | Konkret |
|
||||
|---|---|
|
||||
| Kategoriale Palette | 14 Farben in verbindlicher Reihenfolge, immer von vorne vergeben |
|
||||
| Obergrenze | maximal 5 Kategorien je Chart, Rest über `gruppiereRest()` zu „Sonstige" |
|
||||
| Achsen | nur horizontale Gitterlinien, keine Tick-Marks, Achsenlinie nur an X |
|
||||
| Tooltip | dunkle Fläche `#393939`, weißer Text, kein Rahmen |
|
||||
| Neutrale Werte | Grid `#e0e0e0`, Achse `#8d8d8d`, Text `#161616` / `#525252` |
|
||||
|
||||
**Ergänzt, nicht aus Carbon:** die festen Farbzuordnungen je Gewerk und je
|
||||
Status (`GEWERK_FARBEN`, `STATUS_KATEGORIE_FARBEN`). Carbon regelt die Palette,
|
||||
nicht welche Kategorie welche Farbe bekommt. Eine Kategorie behält ihre Farbe
|
||||
über alle Seiten hinweg, damit man Legenden nicht ständig neu liest.
|
||||
|
||||
---
|
||||
|
||||
## 4. Was aus keiner der drei Quellen stammt
|
||||
|
||||
Die SHK-Fachlogik ist für diesen Prototyp entstanden und beim Einbau der Teil,
|
||||
der mit dem Betrieb abzustimmen ist:
|
||||
|
||||
| Thema | Wo | Offene Fachfrage |
|
||||
|---|---|---|
|
||||
| Gewerke und Leistungsarten | `data/types.ts` | Schnitt deckungsgleich mit dem Betrieb? |
|
||||
| Förderquote BAFA/KfW | `calculations.ts` → `foerderQuote()` | Braucht ein Feld am Auftrag |
|
||||
| Vertriebstrichter nur über Angebotsgeschäft | `vertriebsTrichter()`, Feld `ueberAngebot` | Ab welchem Wert wird schriftlich angeboten? |
|
||||
| Absprungstufe verlorener Vorgänge | Feld `verlorenAb` | Wird das heute erfasst? |
|
||||
| Auslastung und ihre Schwellen | `AuslastungsBalken.tsx` | Zählen Rüst- und Fahrzeiten als produktiv? |
|
||||
|
||||
Diese Punkte stehen mit Begründung in [HANDOVER.md](../HANDOVER.md) Abschnitt 5
|
||||
und in [KPI-KATALOG.md](KPI-KATALOG.md).
|
||||
|
||||
---
|
||||
|
||||
## Zeitleiste
|
||||
|
||||
| Wann | Was |
|
||||
|---|---|
|
||||
| 18.–22.01.2026 | CRM-Analytics-Click-Dummy mit dem Branchenpartner, UI/UX-Regeln, Anforderungskatalog |
|
||||
| 22.01.2026 | Dashboard- und Analytics-Bausteine ins HiveGrid-Projekt übernommen |
|
||||
| Juli 2026 | Pitch-Prototyp mit der Subframe-Bibliothek *Basedesign* |
|
||||
| 27.08.2026 | Dieses Repo: Planung, Pitch-Bibliothek und Carbon zusammengeführt, fachlich auf SHK zugeschnitten |
|
||||
@@ -0,0 +1,147 @@
|
||||
# KPI-Katalog
|
||||
|
||||
Jede Kennzahl mit Definition, Berechnung und Fundstelle im Code.
|
||||
Alle Funktionen liegen in `features/dashboard/data/calculations.ts`.
|
||||
|
||||
**Zwei Zeitbezüge sind zu unterscheiden:**
|
||||
|
||||
- **zeitraumbezogen** — reagiert auf die Zeitraum-Umschaltung (Umsatz, neue Kunden)
|
||||
- **stichtagsbezogen** — gilt zum heutigen Tag, unabhängig vom Zeitraum
|
||||
(offene Forderungen, aktive Aufträge, Auslastung des Bestands)
|
||||
|
||||
Das ist Absicht: „Offene Forderungen der letzten 7 Tage" wäre keine sinnvolle
|
||||
Zahl.
|
||||
|
||||
In der Oberfläche steht der Zeitbezug in der **Fußzeile jeder KPI-Karte**
|
||||
(„im gewählten Zeitraum" / „zum Stichtag" / „über alle Zeiträume"). Karten mit
|
||||
Vorperiodenvergleich zeigen dort stattdessen den Trend — die Anatomie bleibt
|
||||
gleich, damit die Reihe eine gemeinsame Grundlinie hat. Diagrammkarten, die
|
||||
den Zeitraumfilter ignorieren, sagen es im Untertitel.
|
||||
|
||||
---
|
||||
|
||||
## Übersicht
|
||||
|
||||
| KPI | Definition | Bezug | Funktion |
|
||||
|---|---|---|---|
|
||||
| Umsatz (Zahlungseingang) | Summe bezahlter Ausgangsrechnungen, gezählt am Zahlungsdatum | Zeitraum | `umsatz()` |
|
||||
| Trend Umsatz | Vergleich mit dem gleich langen Zeitraum davor | Zeitraum | `umsatzVorperiode()` + `berechneTrend()` |
|
||||
| Offene Forderungen | Summe aller nicht bezahlten Ausgangsrechnungen | Stichtag | `offenePosten().gesamt` |
|
||||
| Aktive Aufträge | Vorgänge im Status „beauftragt" oder „in Arbeit" | Stichtag | `aktiveAuftraege()` |
|
||||
| Ø Auslastung Monteure | Mittel der produktiven Stunden je verfügbarer Stunde | Zeitraum | `teamKennzahlen()` |
|
||||
| Neue Kunden | Kundendatensätze mit Anlagedatum im Zeitraum | Zeitraum | `neueKunden()` |
|
||||
| Abschlussquote | Gewonnene Aufträge im Angebotsgeschäft, bezogen auf alle Angebote | Stichtag | `abschlussquote()` |
|
||||
| Deckungsbeitrag | Nach Auftragswert gewichtete Marge abgeschlossener Aufträge | Zeitraum | `marge()` |
|
||||
|
||||
**Umsatzentwicklung** (`umsatzVerlauf()`) bildet zusätzlich den
|
||||
Vorjahreszeitraum ab: Jede Zahlung wird um zwölf Monate verschoben in denselben
|
||||
Zeitraster einsortiert. Die Auflösung wechselt automatisch — bis 30 Tage
|
||||
tageweise, darüber monatsweise.
|
||||
|
||||
---
|
||||
|
||||
## Finanzen
|
||||
|
||||
| KPI | Definition | Bezug | Funktion |
|
||||
|---|---|---|---|
|
||||
| Umsatz (bezahlt) | wie oben | Zeitraum | `umsatz()` |
|
||||
| Offene Forderungen | Status „gestellt" oder „überfällig" | Stichtag | `offenePosten()` |
|
||||
| Überfällig | Nur Status „überfällig" (Zahlungsziel deutlich überschritten) | Stichtag | `offenePosten().ueberfaellig*` |
|
||||
| Ø Zahlungsdauer | Mittel aus Rechnungsdatum bis Zahlungseingang | Zeitraum | `zahlungsdauer()` |
|
||||
| Deckungsbeitrag | siehe oben | Zeitraum | `marge()` |
|
||||
| Forderungen nach Alter | Buckets ab Fälligkeit: 0–30 / 31–60 / 61–90 / > 90 Tage | Stichtag | `offenePosten().nachAlter` |
|
||||
| Rechnungsvolumen nach Status | Betragssumme je Rechnungsstatus | Stichtag | `rechnungenNachStatus()` |
|
||||
| Umsatz nach Gewerk | Bezahlte Rechnungen, zugeordnet über den Auftrag | Zeitraum | `umsatzNachGewerk()` |
|
||||
| Top-Kunden | Kunden nach bezahltem Gesamtumsatz | gesamt | `topKunden()` |
|
||||
|
||||
Die Altersstruktur ist die wichtigste Karte der Seite: Ab etwa 60 Tagen sinkt
|
||||
die Einbringungsquote erfahrungsgemäß deutlich. Die Balken sind deshalb von
|
||||
grün nach rot eingefärbt.
|
||||
|
||||
---
|
||||
|
||||
## Aufträge
|
||||
|
||||
| KPI | Definition | Bezug | Funktion |
|
||||
|---|---|---|---|
|
||||
| Neue Aufträge | Vorgänge mit Anlagedatum im Zeitraum | Zeitraum | `auftraegeImZeitraum()` |
|
||||
| Aktive Aufträge | „beauftragt" oder „in Arbeit" | Stichtag | `aktiveAuftraege()` |
|
||||
| Auftragsbestand | Summe der Werte aktiver Aufträge | Stichtag | abgeleitet |
|
||||
| Über Termin | Beauftragt oder in Arbeit und Fälligkeit überschritten | Stichtag | `ueberfaelligeAuftraege()` |
|
||||
| Abschlussquote | siehe Übersicht | Stichtag | `abschlussquote()` |
|
||||
| Ø Durchlaufzeit | Anlagedatum bis Abschluss, nur abgeschlossene Vorgänge | Stichtag | `durchlaufzeit()` |
|
||||
| Vertriebstrichter | Vorgänge je erreichter Stufe | Stichtag | `vertriebsTrichter()` |
|
||||
| Förderquote BAFA/KfW | Anteil **förderfähiger** Aufträge mit beantragter oder bewilligter Förderung | Stichtag | `foerderQuote()` |
|
||||
|
||||
**Zum Trichter:** Er zeigt ausschließlich das **Angebotsgeschäft** — Vorhaben
|
||||
ab etwa 6.000 €, für die schriftlich angeboten wird (Feld `ueberAngebot`).
|
||||
Wartung, Notdienst und E-Check gehen ohne Angebot direkt in die Ausführung;
|
||||
zählte man sie mit, würde der Trichter unten breiter als oben.
|
||||
|
||||
Verlorene Vorgänge zählen bis zu der Stufe, auf der sie abgesprungen sind
|
||||
(Feld `verlorenAb`). Ohne diese Unterscheidung wären die oberen Stufen
|
||||
systematisch zu klein und die Übergangsquoten wertlos.
|
||||
|
||||
**Zur Abschlussquote:** gleiche Bezugsgröße wie der Trichter. Sie entspricht
|
||||
deshalb exakt der Übergangsquote von „Angebot" auf „Beauftragt" — im
|
||||
Demo-Datensatz rund 38 %.
|
||||
|
||||
**Zur Förderquote:** Bezugsgröße sind nur förderfähige Leistungen (Wärmepumpe,
|
||||
PV-Anlage, Batteriespeicher, Lüftungsanlage). Über alle Aufträge gerechnet läge
|
||||
sie im einstelligen Bereich und wäre wertlos — eine Badsanierung war nie
|
||||
förderfähig.
|
||||
|
||||
---
|
||||
|
||||
## Kunden
|
||||
|
||||
| KPI | Definition | Bezug | Funktion |
|
||||
|---|---|---|---|
|
||||
| Kunden gesamt | Alle Kundendatensätze | Stichtag | — |
|
||||
| Neue Kunden | Anlagedatum im Zeitraum | Zeitraum | `neueKunden()` |
|
||||
| Wiederkehrende Kunden | Anteil mit mehr als einem Auftrag | Stichtag | `wiederkehrquote()` |
|
||||
| Ø Kundenwert | Bezahlter Umsatz je Kunde mit mindestens einem Auftrag | gesamt | abgeleitet |
|
||||
| Reaktivierbar | Kunden mit Historie, über 12 Monate ohne Auftrag | Stichtag | `inaktiveKunden()` |
|
||||
| Kunden nach Quelle / Typ / Region | Verteilungen | Stichtag | `kundenNach()` |
|
||||
| Auffällige Zahlungsmoral | Ø Zahlungsdauer über 30 Tage | Stichtag | `zahlungsauffaellig()` |
|
||||
|
||||
Empfehlung und Bestandskunden zusammen zeigen, wie stark der Betrieb ohne
|
||||
Werbebudget wächst — im Handwerk die aussagekräftigste Quellenkennzahl.
|
||||
|
||||
---
|
||||
|
||||
## Team
|
||||
|
||||
| KPI | Definition | Bezug | Funktion |
|
||||
|---|---|---|---|
|
||||
| Im Einsatz | Gewerblich Beschäftigte mit Status „aktiv" | Stichtag | — |
|
||||
| Ø Auslastung | Produktive Stunden / verfügbare Stunden (aus Wochenstunden hochgerechnet) | Zeitraum | `teamKennzahlen()` |
|
||||
| Produktivquote | Auftragsbezogene Stunden / erfasste Stunden | Zeitraum | abgeleitet |
|
||||
| Erfasste Stunden | Summe aller Zeiteinträge | Zeitraum | abgeleitet |
|
||||
| Abwesenheit | Stunden für Urlaub, Krankheit, Fortbildung | Zeitraum | abgeleitet |
|
||||
| Abgeschlossene Aufträge je Mitarbeiter | Anzahl im Zeitraum abgeschlossener Aufträge | Zeitraum | `teamKennzahlen()` |
|
||||
| Auftragswert je Mitarbeiter | Summe der Werte dieser Aufträge | Zeitraum | `teamKennzahlen()` |
|
||||
|
||||
**Auslastungsschwellen** (in `AuslastungsBalken.tsx`, betrieblich festzulegen):
|
||||
|
||||
| Bereich | Bedeutung |
|
||||
|---|---|
|
||||
| < 70 % | Luft frei — Kapazität ungenutzt |
|
||||
| 70–95 % | Zielkorridor |
|
||||
| > 95 % | Ausgebucht — Terminrisiko |
|
||||
|
||||
Der Auftragswert je Mitarbeiter ist ausdrücklich **kein Leistungsvergleich**:
|
||||
Ein Wärmepumpen-Auftrag ist um ein Vielfaches größer als eine Wartung. Der
|
||||
Hinweis steht als Tooltip an der Karte.
|
||||
|
||||
---
|
||||
|
||||
## Bewusst nicht enthalten
|
||||
|
||||
| Kennzahl | Grund |
|
||||
|---|---|
|
||||
| Live-Aktualisierung | Für die Gestaltungsabstimmung ohne Nutzen |
|
||||
| CSV-/Excel-Export | Reine Umsetzungsfrage, kein Gestaltungsthema |
|
||||
| Drilldown auf Einzeldatensätze | Der Absprung in die bestehenden Listen genügt |
|
||||
| E-Mail-Marketing-Kennzahlen | Eigenes Modul (siehe Planungsordner 04) |
|
||||
| Workflow-Kennzahlen | Eigenes Modul |
|
||||
@@ -0,0 +1,211 @@
|
||||
# Leitplanken aus HiveGrid Cloud
|
||||
|
||||
Welche Bereiche der Zielanwendung bereits Davids finale Subframe-Screens tragen
|
||||
und welche noch auf Interim-UI laufen — und was daraus für dieses Dashboard
|
||||
verbindlich ist.
|
||||
|
||||
Warum die Unterscheidung zählt: Ein nachgezogener Screen ist eine
|
||||
**Design-Entscheidung**, an der man sich ausrichten kann. Eine Interim-UI ist
|
||||
eine **Notlösung des Devs** — sie kopieren hieße, einen Platzhalter zum Standard
|
||||
zu erklären.
|
||||
|
||||
Stand der Auswertung: 2026-08-29 gegen den lokalen Repo-Stand.
|
||||
|
||||
---
|
||||
|
||||
## 1. Woran man es im Code erkennt
|
||||
|
||||
Der Repo pflegt dafür bewusste Marker. In absteigender Verlässlichkeit:
|
||||
|
||||
| Muster | Bedeutung |
|
||||
|---|---|
|
||||
| `Interim-UI (AD-28)` im Kopfkommentar | Optik ist ausdrücklich vorläufig |
|
||||
| `design-nachzug` | offener Punkt — steht auch in sonst fertigen Screens für einzelne Reste |
|
||||
| Kopfkommentar mit Subframe-Screen-ID (`WF-P8 S03`, `S02/S02L/S03/S05`), Page-UUID oder „gegen Davids Export vom …" | nachgezogen, Optik ist abgestimmt |
|
||||
| `@subframe/sync-disable` in `apps/web/app/ui/**` | lokal geänderte Komponente, die der Sync nicht überschreiben darf |
|
||||
|
||||
Ein typischer Interim-Kopf (`projects/PipelineKanbanPage.tsx`):
|
||||
|
||||
> `// R-P8b (P8 configurable Kanban) Interim-UI (AD-28) — pipeline board screen`
|
||||
> `// (D-KANBAN), composed from ~/ui …`
|
||||
|
||||
Ein typischer nachgezogener Kopf (`projects/ProjektListPage.tsx`):
|
||||
|
||||
> `// WF-P8 S03 "Projekt-Liste" — rebuilt against Davids Subframe export of`
|
||||
> `// 2026-07-28 (page 5c5bdb0a-995e-4c22-aa8b-e48333c60297) …`
|
||||
|
||||
**Autoritativ ist aber die Liste, nicht der Kommentar:**
|
||||
`.planning/SUBFRAME-FLOW-INVENTAR.md` trackt jeden Subframe-Flow mit Seitenzahl,
|
||||
Owner und Stand. Der dortige Hinweis ist wichtig: Der Subframe-Sync pflegt nur
|
||||
Components, Layouts und Theme — **Pages werden nie automatisch integriert**,
|
||||
jeder Screen wird einzeln hinter der Loader/Action-Grenze verdrahtet.
|
||||
|
||||
**Grenze der Methode:** Ein fehlender Marker sagt nichts. Von 214
|
||||
`*Page.tsx`-Dateien tragen 92 einen Interim-Marker, 36 einen Subframe-Bezug und
|
||||
**86 gar keinen** — überwiegend Plattform-Admin (31) und Integrationen (13),
|
||||
also interne Flächen mit niedrigem Politur-Druck.
|
||||
|
||||
---
|
||||
|
||||
## 2. Die Abgrenzung
|
||||
|
||||
### Nachgezogen — als Leitplanke brauchbar
|
||||
|
||||
| Bereich | Stand laut Inventar | Im Code |
|
||||
|---|---|---|
|
||||
| **Team-Chat** | 🟢 24 von 24 Screens integriert (Handoff 2026-08-08) | `screens/chat/` — kein einziger Interim-Marker |
|
||||
| **Form-Builder** | ✅ integriert (PR #71) | `screens/forms/` — Kopf nennt „WF-P16, screens S02/S02L/S03/S05" |
|
||||
| **Automation / Workflow-Editor** | 🟡 voller Delta-Nachzug (Davids „V1 Vollbild minimal") | `screens/automation/` — 6 Screens mit Subframe-Bezug, keiner interim |
|
||||
| **Projekt-Pipelines** | 🟡 Kernstrecke S01–S07 integriert | `screens/projects/` — 7 nachgezogen, 4 noch interim (darunter das Kanban selbst) |
|
||||
| **Aufgaben** | 🟡 Stufen 0–6 fertig | `screens/tasks/` — 2 nachgezogen, 1 interim |
|
||||
| **Lead-Liste** | 🟡 Kernstrecke (`/leads` = S01, `/leads/inbox` = S02) | `screens/leads/` — 2 nachgezogen, 6 noch interim |
|
||||
|
||||
### Vollständig Interim — nicht als Vorbild nehmen
|
||||
|
||||
Kein einziger nachgezogener Screen: **HR** (21 Seiten), **Mobile-PWA** (19),
|
||||
**CRM** (6), **Booking** (5), **DSGVO/Privacy** (5), **E-Mail-Postfach** (4),
|
||||
**Files** (3), **Kalender** (2), **Audit** (2), Signatur, Plugins, Auth —
|
||||
**und das Dashboard** (1).
|
||||
|
||||
### Noch gar nicht entworfen
|
||||
|
||||
Die zehn Auth-Flows (Login/2FA, Passwort-Reset, Set-Password, Admin-RBAC,
|
||||
Signup, Konto löschen, 2FA verwalten, Passwort ändern, Workspace-Auswahl,
|
||||
Profil & Sicherheit) stehen im Inventar auf ❌ offen — zusammen 44 Seiten.
|
||||
|
||||
Ergänzend: Die 30 Designer-Briefs unter `.planning/designer-briefs/` haben ihren
|
||||
Block „Davids Delta" durchgehend **leer**. Die tatsächlichen Handoffs werden
|
||||
über das Flow-Inventar und die Ticket-Docs geführt, nicht über die Briefs —
|
||||
102 Ticket-Docs führen einen offenen `design-nachzug`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Was daraus für dieses Dashboard folgt
|
||||
|
||||
### 3.1 KPI-Kachel: `StatsCard` ist gesetzt
|
||||
|
||||
Die nachgezogenen Screens benutzen durchgehend `~/ui/components/StatsCard`
|
||||
(Billing-Übersicht, Zahlungsseite, Admin-Health, Admin-Jobs, Admin-Billing).
|
||||
Die API ist knapp:
|
||||
|
||||
```tsx
|
||||
<StatsCard
|
||||
trend="neutral" | "positive" | "negative"
|
||||
icon={<FeatherUsers />}
|
||||
label={t("…")}
|
||||
value={formatters.number(totalSeats)}
|
||||
percentage={t("…")} // "" um den Slot abzuschalten
|
||||
/>
|
||||
```
|
||||
|
||||
Unsere `KPICard` kann mehr: Sparkline, Akzentfarbe, `richtungGut` (steigender
|
||||
Wert ist je nach Kennzahl gut oder schlecht), Trendlabel, ⋯-Menü zum Ausblenden.
|
||||
Das ist fachlich begründet — **aber es ist eine Abweichung vom gesetzten
|
||||
Baustein.** Beim Einbau gibt es genau zwei saubere Wege:
|
||||
|
||||
1. `StatsCard` erweitern und die Datei mit `@subframe/sync-disable` schützen
|
||||
(so wurde es dort schon einmal gemacht, siehe Kopf der Komponente), oder
|
||||
2. die eigene Karte bewusst als lokale Komposition begründen.
|
||||
|
||||
Was nicht geht: eine zweite KPI-Kachel kommentarlos danebenstellen.
|
||||
|
||||
### 3.2 Raster: 16 px, mobil einspaltig
|
||||
|
||||
Das durchgängige Muster der nachgezogenen Screens:
|
||||
|
||||
```
|
||||
mobile:grid-cols-1 grid w-full grid-cols-4 gap-4
|
||||
```
|
||||
|
||||
`gap-4` = 16 px = unser Kartenabstand ✓. Das `mobile:`-Präfix ist der
|
||||
hauseigene Breakpoint und in 44 Stellen belegt — unser Prototyp hat kein
|
||||
Mobil-Verhalten und muss es beim Einbau ergänzen.
|
||||
|
||||
### 3.3 Filterleiste: `FilterBar` statt Eigenbau
|
||||
|
||||
`ProjektListPage`, `AutomationListPage`, `AutomationExecutionsPage`,
|
||||
`FormsAdminPage` und die Betriebsmittel-Übersicht nutzen alle
|
||||
`~/ui/components/FilterBar`. Unsere `AnalyticsToolbar` ist handgebautes
|
||||
Tailwind — funktional gleichwertig, aber am gesetzten Baustein vorbei. Das ist
|
||||
die auffälligste Stelle, an der der Prototyp beim Einbau umgestellt gehört.
|
||||
|
||||
Bemerkenswert: Die Betriebsmittel-Übersicht wurde 2026-08-19 genau in diese
|
||||
Richtung überarbeitet — „auf kompakte Kennzahlen plus echte `FilterBar`
|
||||
umgestellt". Kennzahlenreihe über Filterleiste über Liste ist dort also bereits
|
||||
das etablierte Muster. Unser Seitenaufbau folgt derselben Logik.
|
||||
|
||||
### 3.4 Weitere Konventionen der fertigen Screens
|
||||
|
||||
- Seitenkopf über `PageShell` / `PageHeader` / `PageBody`, nie eigenes Chrome
|
||||
- i18n mit Namespace: `t("billing:subscriptionOverview.summary.plan")`
|
||||
- Screen-Komponente ist rein: `({ vm, actions })`, kein `useLoaderData`,
|
||||
kein `useFetcher` — Navigation und Schreibpfade sind Callbacks
|
||||
- Tabellen über `~/ui/components/Table`, Dichte über die Komponente wählen
|
||||
|
||||
---
|
||||
|
||||
## 4. Wo es keine Leitplanke gibt: die Diagramme
|
||||
|
||||
Recharts kommt im gesamten Repo an **drei** Stellen vor: die beiden
|
||||
Dashboard-Diagramme und `leads/LeadAnalyticsPage.tsx`. Es gibt keine geteilte
|
||||
Palette, kein Chart-Theme, keine Kategorienregel — die vorhandenen Diagramme
|
||||
setzen ihre Farben direkt im JSX.
|
||||
|
||||
Auch Davids Standards decken das nicht ab:
|
||||
`.planning/INTERIM-UI-DESIGN-STANDARDS.md` regelt Spacing, Control-Höhen,
|
||||
Dialoggrößen und Zustände sehr genau, **Datenvisualisierung gar nicht**.
|
||||
|
||||
Für dieses Dashboard heißt das beides:
|
||||
|
||||
- Für Kacheln, Filter, Tabellen und Seitenaufbau **gibt es** einen Standard —
|
||||
daran halten wir uns, statt ihn neu zu erfinden.
|
||||
- Für Diagramme gibt es keinen. Was wir in
|
||||
[DESIGN-SYSTEM.md](DESIGN-SYSTEM.md) und `chartTheme.ts` festlegen, füllt eine
|
||||
echte Lücke und wird im Zweifel zur Referenz für alle späteren Charts. Das ist
|
||||
der wertvollste, aber auch der begründungspflichtigste Teil der Übergabe —
|
||||
deshalb steht zu jeder Regel dort, warum sie so ist.
|
||||
|
||||
---
|
||||
|
||||
## 5. Selbst nachprüfen
|
||||
|
||||
```sh
|
||||
cd <hivegrid-cloud>
|
||||
|
||||
# Interim-Screens
|
||||
grep -rl "Interim-UI\|AD-28" apps/web/app/components/screens --include='*Page.tsx'
|
||||
|
||||
# nachgezogene Screens
|
||||
grep -rl "Davids\|WF-P[0-9]" apps/web/app/components/screens --include='*Page.tsx'
|
||||
|
||||
# autoritative Flow-Liste
|
||||
cat .planning/SUBFRAME-FLOW-INVENTAR.md
|
||||
```
|
||||
|
||||
Die Zahlen in diesem Dokument sind eine Momentaufnahme. Das Flow-Inventar wird
|
||||
fortgeschrieben (Stand dort: 2026-08-25, 12. Update) — vor einer Entscheidung
|
||||
lieber neu zählen.
|
||||
|
||||
---
|
||||
|
||||
## 6. Wo dieser Prototyp bewusst abweicht
|
||||
|
||||
Ein Abgleich mit den nachgezogenen Cloud-Screens fördert drei Stellen zutage, an
|
||||
denen dieser Prototyp anders arbeitet. Sie stehen hier, damit sie beim Einbau
|
||||
als **Entscheidung** erkennbar sind und nicht als Versehen wegkorrigiert werden.
|
||||
|
||||
| Thema | Cloud | Hier | Begründung |
|
||||
|---|---|---|---|
|
||||
| **Zahlen in Tabellen** | linksbündig; `align-right` nur für die Aktionsspalte | rechtsbündig mit `tabular-nums` | IBM Carbon und die eigene Planung (`UI_UX_GUIDELINES.md` §2) verlangen es: Nur rechtsbündig stehen Tausenderstellen untereinander, und genau darum geht es in einem Analytics-Dashboard. Die Cloud-Tabellen zeigen überwiegend Text, nicht Beträge |
|
||||
| **Filterleiste** | `~/ui` `FilterBar` mit Facetten-Slots | eigene `AnalyticsToolbar` | Der Prototyp braucht ein Zeitraum-Segment-Control, das die FilterBar nicht kennt. Beim Einbau ist das der erste Umbau — die Logik passt fast 1:1 auf `FacetFilterBar` |
|
||||
| **Formatierung** | `locale/format.ts`, Prozent erwartet 0–1 | eigene `formatters.ts`, Prozent erwartet 0–100 | Der Prototyp hat keine i18n-Schicht. Beim Einbau ersetzen, aber die **Achsenformatierung** (`achsenFormat`, `nettesRaster`) hat dort kein Gegenstück und sollte mitkommen |
|
||||
|
||||
Umgekehrt übernommen wurden: keine Versalien in Spaltenköpfen, Raster mit 16 px
|
||||
Abstand, Kennzahlenreihe über Filterleiste über Liste, Leerzustände an jeder
|
||||
Visualisierung.
|
||||
|
||||
**Offen für den Einbau** (aus dem Cloud-Abgleich, hier bewusst nicht gebaut):
|
||||
Screens als reine `({ vm, actions })`-Komponenten, Texte über i18n-Keys,
|
||||
`mobile:`-Verhalten, `data-ui-id` auf allen Flächen, Textalternative je Diagramm.
|
||||
Das ist Portierungsarbeit, keine Gestaltung — Details in
|
||||
[ZIELSYSTEM.md](ZIELSYSTEM.md) Abschnitt 3.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Navigation und Seitenaufbau
|
||||
|
||||
## Struktur
|
||||
|
||||
```
|
||||
Seitenleiste (fest, einklappbar auf Icon-Breite)
|
||||
├── Analyse
|
||||
│ ├── Übersicht / Kernkennzahlen, Handlungsbedarf
|
||||
│ ├── Finanzen /finanzen Umsatz, Forderungen, Zahlungsverhalten
|
||||
│ ├── Aufträge /auftraege Pipeline, Termine, Förderung
|
||||
│ ├── Kunden /kunden Akquise, Quellen, Reaktivierung
|
||||
│ └── Team /team Auslastung, Stunden, Abschlüsse
|
||||
└── Entwicklung
|
||||
└── Komponenten /komponenten Bausteine und Regeln (nur für die Umsetzung)
|
||||
```
|
||||
|
||||
**Warum Seitenleiste statt Tab-Leiste:** Die bestehende HiveGrid-Oberfläche
|
||||
navigiert links. Der Prototyp lässt sich so ohne Umbau der
|
||||
Informationsarchitektur einsetzen. Einklappen schafft Platz für breite Tabellen
|
||||
und das fünfspaltige KPI-Raster.
|
||||
|
||||
---
|
||||
|
||||
## Aufbau jeder Analyse-Seite
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Kopfzeile: Breadcrumb · Suche · Benachrichtigungen · Profil │ fest
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ Seitenkopf: Titel · Einordnung Aktualisieren Export ⇗ │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ Filterleiste: Zeitraum │ Dimensionen │ zurücksetzen │ klebt oben
|
||||
│ Aktive Filter als entfernbare Chips │
|
||||
│ Ausgeblendete Widgets (nur wenn welche versteckt sind) │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ KPI-Reihe (5 Karten, gleiche Höhe) │
|
||||
│ [nur Übersicht] Handlungsbedarf über volle Breite │
|
||||
│ Hauptdiagramm (2/3) │ Nebendiagramm (1/3) │
|
||||
│ Detaildiagramme (2–3, oben bündig) │
|
||||
│ Arbeitsliste (Tabelle) │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Die Reihenfolge folgt der Lesefrage: **Wie steht es? → Was ist zu tun? → Wie hat
|
||||
es sich entwickelt? → Woran liegt es?**
|
||||
|
||||
Auf der **Übersicht** steht der Handlungsbedarf direkt unter der KPI-Reihe und
|
||||
über die volle Breite. Sie ist die Seite der Geschäftsführung; dort ist „was ist
|
||||
zu tun" die zweite Frage, nicht die letzte. Jede Zeile trägt Betrag und Handlung
|
||||
(„Mahnlauf starten", „Nachfassen") — eine Liste ohne Verb ist ein Bericht.
|
||||
|
||||
Auf den vier Detailseiten bleibt die Arbeitsliste unten: Dort ist sie die
|
||||
Konsequenz aus dem, was darüber steht.
|
||||
|
||||
**Gleiche Kartenhöhe nur bei gleicher Struktur.** Die KPI-Reihe wird gestreckt,
|
||||
damit die fünf Karten als ein Objekt lesbar sind. Gemischte Reihen (Diagramm
|
||||
mit fester Höhe neben Tabelle) stehen auf `items-start` — Strecken erzeugt dort
|
||||
nur Weißraum.
|
||||
|
||||
---
|
||||
|
||||
## Interaktionen
|
||||
|
||||
| Element | Verhalten |
|
||||
|---|---|
|
||||
| **Zeitraum** | Heute · 7 Tage · 30 Tage · Quartal · Jahr · Gesamt. Rechnet alle zeitraumbezogenen Kennzahlen und Charts neu, inklusive Vorperiodenvergleich |
|
||||
| **Dimensionsfilter** | Mehrfachauswahl mit Suche; wirkt sofort. Nur Filter, die auf der Seite etwas bewirken, werden angeboten |
|
||||
| **Filter-Chips** | Jede Einschränkung erscheint als Chip und lässt sich einzeln entfernen |
|
||||
| **Widget ausblenden** | ⋯-Menü an jeder Karte. Ausgeblendete Karten erscheinen in der Filterleiste und lassen sich einzeln oder gesammelt zurückholen |
|
||||
| **Hover** | Balken, Ringsegmente und Trichterstufen heben sich hervor, die übrigen treten zurück; Verläufe zeigen einen Tooltip mit allen Reihen des Datenpunkts |
|
||||
| **Handlungsbedarf** | Jede Zeile ist ein Absprung in die zugehörige Arbeitsliste (im Prototyp Attrappe mit Hinweis) |
|
||||
| **Tastatur** | Fokus ist auf allen interaktiven Elementen sichtbar; Filter-Dropdown und Karten-Menü schließen mit Escape und geben den Fokus zurück |
|
||||
| **Ringlegende** | Klick hebt ein Segment dauerhaft hervor, erneuter Klick löst es wieder |
|
||||
| **Tabellen** | Spaltenkopf sortiert, Suchfeld filtert, Blättern über Pfeile |
|
||||
| **Seitenleiste** | Einklappbar; die Auswahl bleibt beim Seitenwechsel erhalten |
|
||||
|
||||
Filterauswahl und ausgeblendete Widgets überleben einen Reload
|
||||
(`localStorage`, Schlüssel `hivegrid.analytics.*`).
|
||||
|
||||
---
|
||||
|
||||
## Seitenabhängige Filterrelevanz
|
||||
|
||||
Definiert in `features/dashboard/data/filtering.ts`:
|
||||
|
||||
| Seite | Zeitraum | Gewerk | Auftragsstatus | Kundenquelle | Region | Mitarbeiter |
|
||||
|---|:--:|:--:|:--:|:--:|:--:|:--:|
|
||||
| Übersicht | ✓ | ✓ | | | ✓ | |
|
||||
| Finanzen | ✓ | ✓ | | ✓ | ✓ | |
|
||||
| Aufträge | ✓ | ✓ | ✓ | | ✓ | ✓ |
|
||||
| Kunden | ✓ | ✓ | | ✓ | ✓ | |
|
||||
| Team | ✓ | ✓ | | | | ✓ |
|
||||
|
||||
Ein Filter, der sichtbar ist aber nichts bewirkt, kostet Vertrauen in die
|
||||
Zahlen. Deshalb werden irrelevante Filter gar nicht erst angeboten — die
|
||||
Auswahl bleibt aber im Hintergrund erhalten und wirkt beim Seitenwechsel wieder.
|
||||
@@ -0,0 +1,215 @@
|
||||
# Zielsystem: HiveGrid Cloud
|
||||
|
||||
Wohin dieser Prototyp eingebaut wird, was dort heute schon steht und was beim
|
||||
Einbau konkret anzupassen ist.
|
||||
|
||||
Repo: `HiveGrid-Cloud` (Gitea `git.tool-ctrl.de/toolctrl-labs/HiveGrid-Cloud`).
|
||||
pnpm-Monorepo, React Router 7, Prisma/Postgres, mandantenfähig mit RLS.
|
||||
`apps/web` (Tenant- und Admin-App), dazu `api`, `worker`, `desktop`,
|
||||
`hocuspocus`, `portal-domain-controller` und rund 40 Pakete unter `packages/`.
|
||||
Größenordnung: 678 Route-Dateien, davon 575 im Core-Manifest.
|
||||
|
||||
---
|
||||
|
||||
## 1. Der Platz dieses Prototyps
|
||||
|
||||
Das Ziel ist der Tenant-Dashboard-Screen unter `…/<tenant>/dashboard`.
|
||||
Dort liegt bereits eine Implementierung:
|
||||
|
||||
| Datei | Was drin steht |
|
||||
|---|---|
|
||||
| `apps/web/app/components/screens/dashboard/DashboardPage.tsx` | Widget-Grid, 495 Zeilen |
|
||||
| `…/DashboardEditPanel.tsx` | Edit-Modus (Widget-Filter/Zeitraum) |
|
||||
| `…/DashboardDrilldownDialog.tsx` | Drilldown je Kachel |
|
||||
| `…/DashboardMonthlySeriesChart.tsx`, `…/DashboardStageDistributionChart.tsx` | die zwei vorhandenen Diagramme |
|
||||
| `apps/web/app/routes/app.$tenant/dashboard.tsx` | Loader, SSE-Stream, Export |
|
||||
|
||||
Fachlich ist das die Phase **P11** (Modul M20): drei feste Rollen-Layouts
|
||||
(**GF · Büro · Monteur**) aus einer Widget-Registry, live über SSE, plus das
|
||||
Komfort-Delta aus **P30** (Edit-Modus, Drilldown, CSV-/Report-Export,
|
||||
Realtime-Opt-out).
|
||||
|
||||
**Wichtig:** Dieser Stand ist ausdrücklich **Interim-UI nach AD-28** — der Dev
|
||||
baut aus `~/ui` gegen ein stabiles View-Model, damit das Ticket nicht auf den
|
||||
finalen Screen wartet. Der Kommentarkopf von `DashboardPage.tsx` sagt es
|
||||
wörtlich: *„Final Subframe optics = tracked design-nachzug (D-DASH / VD-P30)."*
|
||||
|
||||
Genau diese Lücke füllt dieser Prototyp. Der Einbau ist der geplante Nachzug,
|
||||
kein Umbau gegen den Strom.
|
||||
|
||||
---
|
||||
|
||||
## 2. Gemeinsame Basis — und wo sie auseinanderläuft
|
||||
|
||||
| | Prototyp | HiveGrid Cloud |
|
||||
|---|---|---|
|
||||
| Subframe-Projekt | `f023064a5ca0` | `f023064a5ca0` — **dasselbe** |
|
||||
| Bibliothek | `ui/`, 86 Komponenten | `apps/web/app/ui/`, Alias `~/ui/*`, 92 Komponenten |
|
||||
| Überschneidung | 82 Komponenten gleich benannt | dieselben 82 |
|
||||
| Diagramme | Recharts ^2.15.4 | Recharts ^2.15.3 — **dieselbe Bibliothek** |
|
||||
| Framework | Next.js 16 | React Router 7 |
|
||||
|
||||
Die vier Namensunterschiede: `DialogFullscreen` → `FullscreenDialog`,
|
||||
`DrawerRight` → `Drawer`, `AddressDialog` und `BannerOffline` existieren dort
|
||||
nicht. Cloud hat zusätzlich `BulkActionBar`, `ConfirmDialog`, `DropdownMenu`,
|
||||
`ModalBlank`, `ResultDialog`, `BannerPermanent`, `ChatSprechblase`.
|
||||
|
||||
Dass Komponentenbibliothek **und** Chart-Bibliothek übereinstimmen, ist der
|
||||
Grund, warum der Einbau realistisch ein Port ist und keine Neuimplementierung.
|
||||
|
||||
---
|
||||
|
||||
## 3. Was beim Einbau konkret anders wird
|
||||
|
||||
### 3.1 Daten kommen als View-Model-Prop
|
||||
|
||||
Der Prototyp importiert `DATENSATZ` direkt. In der Cloud gilt das Muster
|
||||
`<XPage vm={} actions={} />`: Die Route besitzt Loader, SSE und Actions, die
|
||||
Screen-Komponente ist rein und bekommt alles als Prop. Kein `useLoaderData`
|
||||
in der Komponente.
|
||||
|
||||
```ts
|
||||
export type DashboardPageVM = VMState<DashboardPageData>;
|
||||
|
||||
type VMState<T> =
|
||||
| { status: "idle" }
|
||||
| { status: "loading"; data?: T }
|
||||
| { status: "ready"; data: T }
|
||||
| { status: "empty" }
|
||||
| { status: "error"; data?: T; error: { messageKey: string; code?: string } };
|
||||
```
|
||||
|
||||
Fünf Zustände statt einem. Der Prototyp kennt nur „Daten da"; `loading`,
|
||||
`empty` und `error` sind je Seite zu ergänzen. Die Chart-Leerzustände
|
||||
(`ChartLeer`) decken davon nur einen Teil ab.
|
||||
|
||||
`DashboardPageData` führt außerdem `activeRole`, `layout`, `generatedAt`,
|
||||
`streamUrl`, `canCustomize`, `liveUpdatesEnabled`, `layoutSource` und
|
||||
`catalog` — das Dashboard ist dort rollen- und berechtigungsabhängig, was der
|
||||
Prototyp nicht abbildet.
|
||||
|
||||
### 3.2 Kein eigenes App-Chrome
|
||||
|
||||
`AppShell.tsx` aus diesem Prototyp fällt beim Einbau **weg**. Die Cloud-Routen
|
||||
laufen bereits in der echten App-Shell mit Sidebar, Tenant-Layout und
|
||||
Breadcrumb (`SubframeAppShell.tsx`, `AppSidebar.tsx`). Screen-Komponenten
|
||||
dürfen dort ausdrücklich **kein** `~/ui/layouts/DefaultPageLayout` rendern —
|
||||
das bringt eine eigene Beispiel-Sidebar mit und erzeugt eine doppelte
|
||||
Navigation. Stattdessen gilt `PageShell` / `PageHeader` / `PageBody` aus
|
||||
`components/screens/shared/PageShell.tsx`.
|
||||
|
||||
Unsere Seitenköpfe und die Filterleiste bleiben, die Rahmen-Navigation nicht.
|
||||
|
||||
### 3.3 Texte über i18n
|
||||
|
||||
Alle statischen Texte laufen über `react-i18next` (`t("app.dashboard.…")`).
|
||||
Dieser Prototyp hat deutsche Strings fest im JSX. Das ist beim Einbau
|
||||
mechanische Arbeit, aber flächendeckend — es betrifft jede Karte, jede
|
||||
Achsenbeschriftung und jeden Leerzustand.
|
||||
|
||||
Die deutschen **Bezeichner im Code** (`umsatz`, `auftraege`) bleiben davon
|
||||
unberührt und passen zur Cloud, deren UI ebenfalls deutsch ist (EN-Locale erst
|
||||
in P30).
|
||||
|
||||
### 3.4 Barrierefreiheit der Diagramme
|
||||
|
||||
In der Cloud sind Charts konsequent `aria-hidden="true"`, und die umgebende
|
||||
Karte liefert eine Textzusammenfassung plus Datentabelle für Screenreader.
|
||||
Dieser Prototyp hat das **nicht**. Beim Einbau ist es Pflicht — das Repo fährt
|
||||
ein a11y-Lint-Baseline-Gate und axe-Tests je Primitive.
|
||||
|
||||
### 3.5 Stabile Oberflächen-IDs
|
||||
|
||||
Jede relevante Fläche trägt ein `data-ui-id` (`ui.dashboard.widget.…`), erfasst
|
||||
in `docs/ui-inventory/surfaces.json`. Tests und Agenten adressieren darüber.
|
||||
Unsere Widget-IDs aus dem `WidgetContext` sind der natürliche Anknüpfungspunkt,
|
||||
müssen aber auf das dortige Namensschema umgestellt werden.
|
||||
|
||||
### 3.6 Spacing passt bereits
|
||||
|
||||
`.planning/INTERIM-UI-DESIGN-STANDARDS.md` schreibt fest: Page-Padding **24**,
|
||||
Card **16**, Grid-Gap **16**, Section-Gap **24**, Field-Gap **16**,
|
||||
Control-Höhe Medium **36**, Input-Radius **4**. Das deckt sich mit dem
|
||||
8-px-System in [DESIGN-SYSTEM.md](DESIGN-SYSTEM.md) Abschnitt 5 — hier ist
|
||||
nichts anzupassen.
|
||||
|
||||
---
|
||||
|
||||
## 4. Was dieser Prototyp dem Zielsystem voraushat
|
||||
|
||||
Nicht alles fließt nur in eine Richtung. Drei Dinge fehlen dort heute:
|
||||
|
||||
**Ein gemeinsames Chart-Theme.** Die beiden vorhandenen Diagramme setzen
|
||||
Farben direkt im JSX (`fill="#334155"`, `fill="#cbd5e1"`), jedes mit eigenem
|
||||
Gitter (`strokeDasharray="3 3"`), eigener Achsenbreite und eigenem
|
||||
Balkenradius. Es gibt keine geteilte Palette, keine Kategorien-Obergrenze und
|
||||
keine feste Farbzuordnung. `features/dashboard/utils/chartTheme.ts` ist genau
|
||||
das, was dort fehlt — und der Teil, der sich am ehesten unabhängig vom Rest
|
||||
übernehmen lässt.
|
||||
|
||||
**Ausformulierte Diagramm-Regeln.** [DESIGN-SYSTEM.md](DESIGN-SYSTEM.md) hat in
|
||||
der Cloud keine Entsprechung. Die Standards dort regeln Layout, Controls und
|
||||
Zustände sehr genau, Datenvisualisierung dagegen gar nicht.
|
||||
|
||||
**Fachliche Kennzahl-Definitionen.** [KPI-KATALOG.md](KPI-KATALOG.md) mit
|
||||
Zeitbezug und Berechnung je KPI ist im Zielsystem so nicht vorhanden; die
|
||||
Widget-Registry beschreibt Kacheln, nicht deren betriebswirtschaftliche
|
||||
Definition.
|
||||
|
||||
---
|
||||
|
||||
## 5. Wer entwirft was — der Rest ist noch nicht fertig designt
|
||||
|
||||
HiveGrid Cloud ist nach dem **Designer-First-Prinzip** organisiert
|
||||
(`.planning/DESIGNER-FIRST-PLAYBOOK.md`). Der Ablauf ist verbindlich:
|
||||
|
||||
1. Dev/Planung stellt je Phase einen **Designer-Brief** bereit —
|
||||
*was* das Modul tut und *welche UI-Elemente* vorkommen müssen, **kein Layout**.
|
||||
2. Der Designer entwirft die Screens in Subframe und darf Spec und UI/UX
|
||||
**überschreiben** — Anordnung und Layout sind eine Design-Entscheidung.
|
||||
3. Das **Delta** (geänderte Spec-Punkte, neue UX-Entscheidungen, Subframe-Link)
|
||||
wird in den Brief eingetragen.
|
||||
4. Der Dev setzt um und zieht Spec und Doku nach.
|
||||
|
||||
Es gibt 31 solcher Briefs (`.planning/designer-briefs/P01…P34.md`) — Auth, CRM,
|
||||
Lead-Inbox, Projekt-Pipelines, Automation-Flow-Editor, Form-Builder, Plantafel,
|
||||
Kalender, Telefonie, HR, Billing, Customer-Portal, Help-Center. Für einen
|
||||
großen Teil davon steht der Delta-Block **noch leer**: Der finale Screen ist
|
||||
noch nicht entworfen, die Route läuft bis dahin auf Interim-UI.
|
||||
|
||||
**Für die Einordnung dieses Prototyps heißt das dreierlei:**
|
||||
|
||||
- Das Dashboard ist **ein Baustein unter vielen**, nicht die Leitgestaltung der
|
||||
Anwendung. Es fügt sich in eine App-Shell und ein Design-System ein, die
|
||||
bereits stehen.
|
||||
- Was hier an Anordnung und Raster festgelegt ist, ist ein **Vorschlag**. Die
|
||||
Layout-Hoheit liegt beim Designer — der Brief zu P11 sagt ausdrücklich
|
||||
„Anordnung = Davids freie Hand". Verbindlich zu übergeben sind die
|
||||
**Kennzahl-Definitionen, die Chart-Regeln und das Verhalten**, nicht die
|
||||
Pixelanordnung.
|
||||
- Andere Bereiche der Anwendung sehen heute noch nicht final aus, weil sie
|
||||
entweder auf Interim-UI laufen oder noch beim Designer liegen. Ein Vergleich
|
||||
„unser Dashboard gegen den aktuellen Screen X" misst deshalb nicht
|
||||
zwangsläufig gegen den Zielzustand.
|
||||
|
||||
---
|
||||
|
||||
## 6. Wenn du dir das Zielsystem selbst ansehen willst
|
||||
|
||||
```sh
|
||||
corepack enable && pnpm install
|
||||
pnpm dev:services # Postgres + Redis
|
||||
pnpm dev:services:reset # Wegwerf-DB inkl. Migrationen und Demo-Daten
|
||||
pnpm dev:local
|
||||
```
|
||||
|
||||
Tenant-App auf `http://app.localhost:3000`. Die lokalen Demo-Zugänge (Tenant
|
||||
„HiveGrid Demo SHK") stehen im README von HiveGrid Cloud unter „App starten" —
|
||||
sie werden hier bewusst nicht wiederholt. Die Demo-Daten sind zunächst
|
||||
ausgeblendet und erscheinen über **„Demo-Daten anzeigen"** oder `?demo=show`.
|
||||
|
||||
Der Weg zum Dashboard ist nach dem Login die erste Seite der Tenant-App.
|
||||
|
||||
Für einen schnellen Überblick ohne Setup:
|
||||
`docs/ui-inventory/surface-map.md` listet alle stabilen Oberflächen mit ihren
|
||||
Quelldateien, `docs/route-inventory.md` die Größenordnung des Routenbestands.
|
||||
Reference in new issue
Block a user