Drei Fehler in der Übergabedoku gefunden und behoben. Die Zuordnung in DATENMODELL.md nannte HiveGrid-Entities, die es nach AD-20 nicht gibt: - 'Auftrag' → 'order': Auftrag ist eine Pipeline-View auf den Projekt-Kern, keine eigene Entität. Status ist pipeline-intern (stage_id). - 'Kunde' → 'company': Kunde ist ein Status am Kontakt (kontakt_status), keine Entität. Kontakt ist der Oberbegriff. - 'Rechnung' → 'invoice': heißt Rechnung und ist Sub-Entity am Projekt; ihr Status kennt kein 'ueberfaellig' — Überfälligkeit wird aus faellig_am abgeleitet. Dazu ein Befund mit Konsequenz: Zeiterfassung steht im ENTITY-MODEL im v2-Backlog. Damit hat die gesamte Team-Seite in v1 keine Datenquelle. Drei Wege sind in DATENMODELL.md festgehalten; die Entscheidung gehört vor den Einbau. Neu in ZIELSYSTEM.md 3.1b: wohin die Analyse-Seiten in der Navigation gehören. Die Software hat heute keine Analyse-Gruppe, aber bereits zwei konkurrierende Muster (Rollen-Dashboard unter /dashboard, Domänen-Analyse unter leads/analytics). Drei Optionen mit Konsequenzen und eine Empfehlung. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
322 lines
14 KiB
Markdown
322 lines
14 KiB
Markdown
# 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 — bereits umgesetzt
|
|
|
|
**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>;
|
|
|
|
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.
|
|
|
|
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.1b Wo die Analyse in der Navigation landet — offen
|
|
|
|
**Die Seitenleiste des Prototyps ist Gerüst, kein Vorschlag.** Sie existiert nur,
|
|
damit die Seiten einzeln aufrufbar sind, und entfällt beim Einbau (siehe 3.2).
|
|
Wohin die fünf Analyse-Seiten in der echten Navigation gehören, ist **noch nicht
|
|
entschieden**.
|
|
|
|
Der Stand in der Software: Die Seitenleiste hat sechs Gruppen —
|
|
Start (Dashboard, Aufgaben) · Kunden · Projekte · Kommunikation · Organisation ·
|
|
Automatisierung. **Eine Analyse-Gruppe gibt es nicht.** Es gibt aber bereits
|
|
zwei konkurrierende Muster:
|
|
|
|
| Muster | Beispiel | Charakter |
|
|
|---|---|---|
|
|
| Rollen-Dashboard als Startseite | `/dashboard` (P11/P30) | Widgets, drei Rollen-Layouts, „wie ist meine Lage" |
|
|
| Analyse **innerhalb** der Fachdomäne | `leads/analytics` — existiert bereits | vertieft genau eine Domäne |
|
|
|
|
Dieser Prototyp schlägt implizit ein drittes Muster vor: einen
|
|
**domänenübergreifenden Analysebereich** mit gemeinsamem Zeitraum- und
|
|
Dimensionsfilter.
|
|
|
|
**Drei Optionen:**
|
|
|
|
1. **Eigene Gruppe „Analyse"** neben Kunden/Projekte/Kommunikation, mit den fünf
|
|
Seiten darin.
|
|
*Dafür:* Die Seiten teilen sich einen Filterzustand und eine Leseführung
|
|
(Übersicht → Detail). Finanzen greift über Projekte, Rechnungen und Kontakte
|
|
hinweg und passt in keine einzelne Domäne.
|
|
*Dagegen:* eine siebte Gruppe in einer schon langen Seitenleiste.
|
|
2. **Verteilt in die Domänen** — Finanzen unter Projekte, Kunden-Analyse unter
|
|
Kunden, analog zu `leads/analytics`.
|
|
*Dafür:* folgt dem bestehenden Muster, keine neue Gruppe.
|
|
*Dagegen:* Der gemeinsame Zeitraumfilter zerfällt, die Übersichtsseite hat
|
|
keinen Ort, und der Vergleich zwischen Domänen wird unmöglich.
|
|
3. **Alles ins bestehende `/dashboard`** als zusätzliche Widgets.
|
|
*Dagegen:* Das Rollen-Dashboard beantwortet „wie ist meine Lage", die
|
|
Analyse „warum ist das so". Beides in eine Seite zu legen, macht aus dem
|
|
Einstieg eine Halde.
|
|
|
|
**Empfehlung: Option 1**, mit dem Namen **„Statistiken"** — so heißt der
|
|
Breadcrumb im Prototyp bereits, und „Analyse" ist im Haus durch
|
|
`leads/analytics` belegt. Das bestehende `/dashboard` bleibt Startseite; unsere
|
|
Seite „Übersicht" braucht dann einen anderen Namen, sonst konkurrieren zwei
|
|
Einstiegsseiten um dieselbe Rolle.
|
|
|
|
**Vorher zu klären sind die Begriffe** (Details in [DATENMODELL.md](DATENMODELL.md)):
|
|
|
|
| Prototyp | In der Software |
|
|
|---|---|
|
|
| „Übersicht" | kollidiert mit der Startseite „Dashboard" |
|
|
| „Aufträge" | keine Entität — eine Pipeline-View auf den Projekt-Kern (AD-20) |
|
|
| „Kunden" | keine Entität — ein Status am `Kontakt` |
|
|
| „Team" | thematisch unter Organisation/HR; Datenquelle erst in v2 |
|
|
|
|
Das ist eine Produkt- und Designentscheidung, keine Umsetzungsfrage. Sie gehört
|
|
in die Runde mit dem Head of Design, nicht in den Einbau.
|
|
|
|
### 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.
|