# 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.hg-int.dev/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. --- ## 0. Rangfolge beim Einbau: HiveGrid gewinnt **Dieser Prototyp ist nachrangig.** Wo er von HiveGrid Cloud abweicht, gilt HiveGrid, ohne Rückfrage, ohne Ausnahme. Das betrifft alles Globale: | | Wer bestimmt | |---|---| | Schrift, Schriftgrößen, Zeilenhöhen | **HiveGrid** | | Abstände, Paddings, Margins, Raster | **HiveGrid** | | Farben der Oberfläche, Rahmen, Radien, Schatten | **HiveGrid** | | Control-Höhen, Komponentenvarianten, Zustände | **HiveGrid** | | App-Shell, Seitenleiste, Kopfzeile, Seitenkopf | **HiveGrid** | | i18n, Formatierung, Zeitzone | **HiveGrid** | Dass dieser Prototyp dieselbe Schrift, dieselbe Seitenleisten-Komponente und dieselben Token benutzt, hat **einen einzigen Zweck**: Er soll neben der Software lesbar sein und nicht wie ein Fremdkörper wirken. Es ist **keine Vorlage, an der sich die Software auszurichten hätte.** Fällt beim Einbau auf, dass hier ein Wert von HiveGrid abweicht, ist der Wert hier falsch, nicht dort. ### Was aus diesem Repo verbindlich ist Nur das Fachliche und das Analytics-Eigene: 1. **Die Navigationsstruktur als Verabredung**: zwei Einträge in der Seitenleiste (`Dashboard`, `Analytics`), und Analytics trägt eine **innere Leiste** mit den fünf Analyse-Seiten. Verbindlich ist die *Struktur*, nicht ihre Umsetzung hier: In der Software werden diese zwei Einträge in die bestehende `AppSidebar` eingehängt und die innere Leiste über das dortige `SidebarIconsLayout` gebaut. Unser Nachbau ist die Beschreibung, nicht das Bauteil. 2. **Kennzahl-Definitionen** ([KPI-KATALOG.md](KPI-KATALOG.md)), was gerechnet wird, mit welchem Zeitbezug. 3. **Diagrammregeln** ([DESIGN-SYSTEM.md](DESIGN-SYSTEM.md)): Palette, Achsen, Diagrammwahl, Leerzustände. Das ist die Lücke, die HiveGrid heute nicht füllt (siehe [LEITPLANKEN.md](LEITPLANKEN.md) Abschnitt 4). 4. **Verhalten**: Filterlogik, Zeitbezüge, Widget-Sichtbarkeit, Sortier- und Blätterverhalten. ### Was aus diesem Repo Gerüst ist und wegfällt `components/layout/AppShell.tsx`, unsere Kopie der Seitenleiste, `PageHeader`, die Kopfzeile mit Suche und Glocke, die Routen unter `app/` und die Attrappe des angemeldeten Benutzers (`data/nutzer.ts`). All das existiert, damit der Prototyp alleine lauffähig und beurteilbar ist. Beim Einbau übernehmen `SubframeAppShell`, `AppSidebar`, `PageShell`, `requireMember` und die Loader der Software. Das **Kachelraster des Dashboards** ist die Ausnahme in dieser Liste: Es ist kein Gerüst, sondern aus `DashboardPage.tsx` der Software übernommen, dieselben Klassen, dieselbe Ableitung aus `size.w` und `position.x`. Es fällt nicht weg, es trifft dort auf sein Original. **Kurz:** Aus diesem Repo kommt der Inhalt des Analytics-Bereichs, der Aufbau des Dashboards und die Verabredung, wo beide in der Navigation sitzen. Alles andere kommt aus HiveGrid. --- ## 1. Der Platz dieses Prototyps Das Ziel ist der Tenant-Dashboard-Screen unter `…//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 }) { if (vm.status !== "ready") return ; 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 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(); return ; } ``` `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; type VMState = | { 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. **Entschieden am 29.08.2026: Option 1**, mit dem Namen **„Analytics"**, der Prototyp setzt das um. Die Seitenleiste spiegelt jetzt die der Software (dieselbe Komponente, dieselben Gruppen); „Analytics" ist ein einzelner Menüpunkt neben „Dashboard", die fünf Seiten sind darin über Reiter erreichbar. *Ursprüngliche Empfehlung war der Name* **„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. **Nachtrag 29.08.2026:** Die Frage hängt an einer größeren: „Analytics" und „Dashboard" sind zwei Features. Siehe [SCOPE-ANALYTICS-DASHBOARD.md](SCOPE-ANALYTICS-DASHBOARD.md). ### 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.