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
This commit is contained in:
1 parent
b8bf987d0e
commit
99be627dad
28 files changed
+1030
-340
No files matched your search
+9
-4
@@ -204,8 +204,13 @@ Umgekehrt übernommen wurden: keine Versalien in Spaltenköpfen, Raster mit 16 p
|
||||
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
|
||||
**Bereits umgesetzt:** Die Seiten sind reine `({ vm })`-Komponenten mit dem
|
||||
wortgleich gespiegelten `VMState`-Vertrag, die Berechnung liegt in reinen
|
||||
Buildern unter `view-models/`, und die fünf Seitenzustände sind ausgestaltet.
|
||||
Store-Zugriff gibt es ausschließlich in `routes/` — genau der Schicht, die beim
|
||||
Einbau durch den Loader ersetzt wird.
|
||||
|
||||
**Offen für den Einbau** (Portierungsarbeit, keine Gestaltung): Texte über
|
||||
i18n-Keys, `mobile:`-Verhalten, `data-ui-id` auf allen Flächen, Textalternative
|
||||
je Diagramm, `actions`-Callbacks statt Demo-Toasts. Details in
|
||||
[ZIELSYSTEM.md](ZIELSYSTEM.md) Abschnitt 3.
|
||||
+58
-9
@@ -61,12 +61,44 @@ 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
|
||||
### 3.1 Daten kommen als View-Model-Prop — bereits umgesetzt
|
||||
|
||||
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.
|
||||
**Das ist im Prototyp schon so gebaut.** Jede Analyse-Seite ist eine reine
|
||||
Komponente:
|
||||
|
||||
```tsx
|
||||
export function UebersichtSeite({ vm }: { vm: VMState<UebersichtDaten> }) {
|
||||
if (vm.status !== "ready") return <SeitenZustand vm={vm} />;
|
||||
const d = vm.data;
|
||||
…
|
||||
}
|
||||
```
|
||||
|
||||
Die Verdrahtung sitzt in einem eigenen Container unter
|
||||
`features/dashboard/routes/`, die Berechnung in einem reinen Builder unter
|
||||
`features/dashboard/view-models/`:
|
||||
|
||||
```
|
||||
view-models/uebersicht.vm.ts baueUebersichtVM(daten, filter) → VMState<UebersichtDaten>
|
||||
pages/UebersichtSeite.tsx ({ vm }) → JSX ← das ist der Teil, der portiert wird
|
||||
routes/UebersichtRoute.tsx Store + Builder + Seite ← das ist der Teil, der ersetzt wird
|
||||
app/page.tsx rendert den Container
|
||||
```
|
||||
|
||||
Beim Einbau tritt der Loader an die Stelle des Containers:
|
||||
|
||||
```tsx
|
||||
// apps/web/app/routes/app.$tenant/dashboard.tsx
|
||||
export default function Route() {
|
||||
const { vm } = useLoaderData<LoaderData>();
|
||||
return <UebersichtSeite vm={vm} actions={{ onAbsprung }} />;
|
||||
}
|
||||
```
|
||||
|
||||
`features/dashboard/view-models/index.ts` spiegelt `VMState` wortgleich aus
|
||||
`@hivegrid/view-models`. Der Import wird getauscht, die Seiten bleiben
|
||||
unberührt. Die fünf Zustände (`idle` · `loading` · `ready` · `empty` · `error`)
|
||||
sind über `SeitenZustand` bereits ausgestaltet.
|
||||
|
||||
```ts
|
||||
export type DashboardPageVM = VMState<DashboardPageData>;
|
||||
@@ -83,10 +115,27 @@ 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.
|
||||
Was der Builder noch **nicht** kennt: `DashboardPageData` führt in der Cloud
|
||||
zusätzlich `activeRole`, `layout`, `generatedAt`, `streamUrl`, `canCustomize`,
|
||||
`liveUpdatesEnabled`, `layoutSource` und `catalog`. Das Dashboard ist dort
|
||||
rollen- und berechtigungsabhängig — diese Felder kommen beim Einbau dazu.
|
||||
|
||||
### 3.1a Warum der Prototyp auf Next.js bleibt
|
||||
|
||||
Die Frage liegt nahe, weil die Software auf React Router 7 läuft. Ein Umbau
|
||||
brächte aber nichts: Im gesamten Prototyp gibt es **vier** Next-spezifische
|
||||
Importe, alle in zwei Dateien —
|
||||
|
||||
| Datei | Import | Schicksal beim Einbau |
|
||||
|---|---|---|
|
||||
| `components/layout/AppShell.tsx` | `next/link`, `next/navigation` | entfällt, die Cloud bringt Shell und Sidebar mit |
|
||||
| `app/layout.tsx` | `next`, `next/font/google` | entfällt, Root-Layout gehört der Cloud |
|
||||
|
||||
Portiert wird nicht die Anwendung, sondern der Ordner
|
||||
`features/dashboard/{pages,components,view-models,utils}` — und der enthält
|
||||
keine Framework-Bindung. React 19, Tailwind v4 und Recharts sind in beiden
|
||||
Repos dieselben. Ein Framework-Wechsel im Prototyp wäre Aufwand ohne Ertrag und
|
||||
würde die Gestaltungsarbeit nur verzögern.
|
||||
|
||||
### 3.2 Kein eigenes App-Chrome
|
||||
|
||||
|
||||
Reference in new issue
Block a user