Files
HiveGrid-Wireframes/hivegrid-cloud/docs/boilerplate-inventory/ui-shell.md
T

383 lines
25 KiB
Markdown

# Boilerplate-Inventur: UI-Layer & App-Shell
Faktische Bestandsaufnahme des Bereichs "UI-Layer & App-Shell" der Boilerplate
(`/Users/raffaelsheikh/Documents/GitHub/boilerplate`). Stand: Read-only-Inventur,
keine Aenderungen vorgenommen.
Inventarisierte Pakete/Pfade:
`packages/ui`, `packages/ui-app`, `packages/ui-mock`, `packages/ui-subframe`,
`packages/view-models` (Vertrags-Fundament), `apps/web`, `apps/storybook`.
## TL;DR — Reale Layer-Trennung
Die Layer-Trennung ist **noch nicht voll aktiviert**. Sie ist als bewusst
strukturierte "Adapter-/Vertrags-Grenze" angelegt, waehrend die echten
Laufzeit-Komponenten weiterhin in `apps/web/app/` leben.
- `@toolctrl/view-models` — reine TypeScript-Typen + kleine Helfer: View-Model-
Statusmodell (`VMState`), Shell-Registry-Typen (Sidebar/Breadcrumb/Mobile/
Portal), i18n-Key-Validierung. Kein React.
- `@toolctrl/ui-app` — App-Shell-Registry-Factories + headless `AppShell`-Stub
(rendert nur `data-*`-Attribute, kein echtes Layout). Konsumiert view-models.
- `@toolctrl/ui-mock` — Fixture-/Mock-Generator fuer Hot-Swap- und Visual-Tests
(erzeugt statisches HTML + serialisierbares Modul aus einem `MockPage`).
- `@toolctrl/ui-subframe` — **leerer Platzhalter** (`createSubframePlaceholder()`
liefert `{ status: "empty" }`). Subframe-Adapter ist reserviert, nicht gebaut.
- `@toolctrl/ui` — **source-only Adapter-Metadaten** (keine React-Komponenten).
Beschreibt nur stabile Namen + Verweise auf die echten Implementierungen in
`apps/web/app/`.
- `apps/web/app/` — echte Laufzeit: App-Shell (shadcn/Radix), Sidebar-Konfig,
RR7-Routing, ~707 `.tsx`-Komponenten, Theme/Dark-Mode, i18n.
- `apps/storybook` — Storybook 8 (react-vite) ueber die `packages/ui`-Stories.
Verbindung von Vertrag → echter Code laeuft ueber **stabile `data-ui-id`-IDs**
(`packages/.../application/ui/uiRegistry.ts`) und die maschinenlesbare
Inventur in `docs/ui-inventory/` (von Tests gegen das Dateisystem geprueft).
---
## Paket: `@toolctrl/view-models`
**Zweck:** Vertrags-Fundament fuer View-Model → UI. Reine Typen + winzige
Validierungs-/Konstruktor-Helfer, ohne React-Abhaengigkeit. Bildet die
gemeinsame Sprache, gegen die `ui-app`, `ui-mock` und die echte App komponieren.
**Zentrale Datei:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/view-models/src/index.ts`
**package.json:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/view-models/package.json` (Name `@toolctrl/view-models`, Export `.` → `./src/index.ts`)
**Haupt-Exporte (Typen):**
| Export | Bedeutung |
| --- | --- |
| `VMStatus` | `"idle" \| "loading" \| "ready" \| "empty" \| "error"` |
| `VMState<T>` | Diskriminierte Union ueber `VMStatus`; `error` traegt `{ messageKey; code? }` (i18n-Key, nicht Klartext) |
| `ActionCallbacks<T>` | `onCreate/onUpdate/onDelete/onRefresh` — Standard-CRUD-Callback-Vertrag |
| `I18nLabel` | `{ labelKey; descriptionKey? }` — UI-Labels sind **immer i18n-Keys** |
| `RegistryGate` | `{ permission?; flag? }` — Sichtbarkeits-Gate (Permission/Feature-Flag) pro Registry-Item |
| `SidebarRegistryItem` | `id/href/icon?/children?` + `I18nLabel` + `RegistryGate` |
| `BreadcrumbRegistryItem` | `id/href?` + Label + Gate |
| `MobileRegistryItem` | `id/href/priority?` + Label + Gate |
| `PortalRegistryItem` | `id/slot/order?` + Label + Gate |
| `ShellRegistries` | `{ sidebar?; breadcrumbs?; mobile?; portals? }` — das Shell-Konfig-Aggregat |
**Helfer-Funktionen:** `ready<T>(data)`, `empty<T>()`, `assertI18nLabel(label)`
(erzwingt Regex `^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$` fuer Label- und Description-Keys).
**HiveGrid-Relevanz:** Hoch und querschnittlich. Das `VMState`-Statusmodell und
der i18n-Key-Zwang sind direkt uebernehmbar als Daten-/UI-Vertrag fuer alle
HiveGrid-Module (Listen/Detail/Editoren), und die `ShellRegistries`-Typen sind
die Grundlage fuer datengetriebene Navigation (App-Shell-Querschnitt).
---
## Paket: `@toolctrl/ui-app`
**Zweck:** App-Shell-Schicht. Liefert Registry-Factories (sortiert/normalisiert
Sidebar/Breadcrumb/Mobile/Portal-Items) und einen headless `AppShell`-Stub.
Der Stub rendert **kein** echtes Layout — nur ein `<div>` mit `data-ui-shell`
und Zaehler-Attributen. Echte Shell lebt in `apps/web`.
**Zentrale Datei:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-app/src/index.ts`
**package.json:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-app/package.json` (deps: `@toolctrl/view-models`, `react`; Test-Subpath-Exports fuer axe/a11y-Template)
**Haupt-Exporte:**
| Export | Art | Verhalten |
| --- | --- | --- |
| `AppShellProps` | Typ | `{ registries?: ShellRegistries; children? }` |
| `createSidebarRegistry(items)` | fn | Kopie der Items |
| `createBreadcrumbRegistry(items)` | fn | Kopie der Items |
| `createMobileRegistry(items)` | fn | sortiert nach `priority` (default 100) |
| `createPortalRegistry(items)` | fn | sortiert nach `order` (default 100) |
| `createShellRegistries(input)` | fn | aggregiert + normalisiert alle 4 Registries |
| `EmptyPage({ labelKey })` | Komponente | Empty-State-Stub (`data-page-state="empty"`) |
| `AppShell({ registries, children })` | Komponente | headless Shell-Stub mit `data-*`-Zaehlern |
**a11y-Test-Vertrag:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-app/src/test-utils/a11y-template.ts`
exportiert `generateA11yTest(Component, defaultProps, options)` — kanonisches
axe-Test-Template (jede Komponente in `ui-app`/`ui-subframe` soll eine
co-lokierte `<Name>.a11y.test.tsx` mitliefern). axe-Setup unter `test-setup/axe.ts`.
**HiveGrid-Relevanz:** App-Shell-/Navigations-Querschnitt. Die Registry-
Factories sind die kanonische Art, HiveGrid-Sidebars/Breadcrumbs aus Daten zu
bauen (mit Priority/Order-Sortierung und Permission/Flag-Gating). `generateA11yTest`
ist als Compliance-/a11y-Querschnitt direkt nutzbar.
---
## Paket: `@toolctrl/ui-mock`
**Zweck:** Hot-Swap-/Visual-Test-Fundament. Erzeugt aus einem deklarativen
`MockPage`-Deskriptor deterministisch (a) ein serialisierbares JS-Modul und
(b) statisches HTML inkl. eigenem Inline-CSS — als "Drop-in"-Ersatz fuer eine
echte Seite. Liefert stabile Hashes/Metriken fuer Visual-Snapshots.
**Zentrale Datei:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-mock/src/index.ts`
**package.json:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-mock/package.json` (dep: `@toolctrl/view-models`)
**Visual-Baseline:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-mock/visual-baseline/`
**Haupt-Exporte:**
| Export | Art | Verhalten |
| --- | --- | --- |
| `MockPage<T>` | Typ | `{ id; route; title?/titleKey?; description?/descriptionKey?; state: VMState<T>; registries? }` |
| `HotSwapDrillData` | Typ | `{ cards[]; activity[] }` — Demo-Datenform |
| `MockVisualSnapshot` | Typ | `htmlHash/moduleHash/stateHash` (FNV-1a) + Byte-/Item-Metriken |
| `MockArtifacts` | Typ | `{ module; html; snapshot }` |
| `createMockPage(page)` | fn | Identitaet (Typ-Anker) |
| `createMockRegistry(pages)` | fn | `Map<route, MockPage>` |
| `generateHotSwapMock(page)` | fn | erzeugt `export const mockPage = ...` Modul-String |
| `generateMockPageHtml(page)` | fn | volles statisches HTML (Sidebar/Breadcrumbs/Cards/Activity/State) |
| `createVisualSnapshot(page, artifacts?)` | fn | Snapshot mit stabilen Hashes |
| `generateMockArtifacts(page)` | fn | `{ module, html, snapshot }` in einem Schritt |
| `createHotSwapDrillPage()` | fn | fertige Referenz-`MockPage` (`/mock/phase-c/hot-swap`) |
Enthaelt eine eingebaute Mini-Uebersetzungstabelle (`defaultMockTranslations`)
fuer die Mock-Texte (i18n-Keys → Klartext, deterministisch).
**HiveGrid-Relevanz:** Test-/QA-Querschnitt. Das Mock-Drop-in-Muster (deklarativer
Deskriptor → deterministisches HTML + Hash) ist als Vorlage fuer HiveGrid-Visual-
Regression und fuer das Bauen von Hot-Swap-/Whitelabel-Vorschauen verwendbar.
---
## Paket: `@toolctrl/ui-subframe`
**Zweck:** **Reservierte, leere** Paket-Grenze fuer kuenftige Subframe-generierte
UI. Bewusst leer, bis ein spaeterer Slice Import-Regeln + Review-Gates definiert.
**Zentrale Datei:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-subframe/src/index.ts`
- `uiSubframeRegistry = [] as const`
- `createSubframePlaceholder()` → `{ status: "empty" }`
**README:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui-subframe/README.md`
("Reserved package boundary for future Subframe-generated UI imports. Exports an empty registry.")
**HiveGrid-Relevanz:** Niedrig (Stub). Relevant nur als Marker dafuer, dass der
Subframe-Adapter-Layer der Boilerplate **nicht gebaut** ist — siehe `MEMORY.md`
("Subframe mandatory for all routes"). HiveGrid muesste diese Grenze selbst fuellen.
---
## Paket: `@toolctrl/ui`
**Zweck:** Source-only **Adapter-Metadaten** ("category migration boundary").
Enthaelt **keine** React-Komponenten und importiert React nicht. Beschreibt
stabile Namen pro Kategorie und zeigt per `source`-Feld auf die echten
Implementierungen in `apps/web/app/`. Erlaubt Agenten/Tools, stabile Namen
anzusprechen, bevor Laufzeit-Imports migriert sind.
**README:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui/README.md`
**package.json:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui/package.json`
**Migration-Doku:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/packages/ui/docs/migration.md`
**Subpath-Exporte (Kategorien):** `.`, `./buttons`, `./inputs`, `./dialogs`,
`./tables`, `./layout`, `./navigation`, `./feedback`.
**Zentrale Dateien:**
| Datei | Inhalt |
| --- | --- |
| `src/index.ts` | Re-Exporte aller Kategorien |
| `src/types.ts` | `uiAdapterCategories`, `UiAdapterDefinition`, `UiAdapterLifecycle` (`placeholder/available/deprecated`), `createEmptyAdapterRegistry`, `isUiAdapterCategory` |
| `src/navigation.ts` | `navigationAdapters` (status `available`): `AppSidebar`, `AdminSidebar`, `DocsSidebar`, `NavMain`, `NavSecondary`, `NavQuickLinks`, `NavActions`, `SidebarPrimitives` — mit `source`-Pfaden in `apps/web/app/...` |
| `src/layout.ts` | `layoutAdapters`: `ShadcnSidebarLayout`, `UiRegistry` |
| `src/buttons.ts` | `buttonAdapters` (`Button`, `buttonVariants`) |
| `src/inputs.ts`, `dialogs.ts`, `tables.ts`, `feedback.ts` | derzeit leere Readonly-Registries (Placeholder bis Migrations-Phase) |
| `src/stories/*.stories.tsx` | ~21 Storybook-Stories (Button, Input, Select, Dialog, DataTable, Table, Tabs, Toast, Tooltip, Popover, Card, Badge, Avatar, Checkbox, Radio, Switch, Textarea, Form, EmptyState, LoadingState, Sidebar) ueber `_demo-primitives.tsx` |
Alle `source`-Pfade aus `navigation.ts`/`layout.ts` (absolut):
`/Users/raffaelsheikh/Documents/GitHub/boilerplate/apps/web/app/application/sidebar/AppSidebar.tsx`,
`.../AdminSidebar.tsx`, `.../DocsSidebar.ts`,
`.../components/layouts/sidebars/shadcn/nav-main.tsx`, `nav-secondary.tsx`,
`nav-quick-links.tsx`, `nav-actions.tsx`,
`.../components/ui/sidebar.tsx`,
`.../components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx`,
`.../application/ui/uiRegistry.ts`.
**HiveGrid-Relevanz:** Konventions-Querschnitt. Liefert die Taxonomie
(buttons/inputs/dialogs/tables/layout/navigation/feedback) und das Lifecycle-
Modell, an dem sich eine HiveGrid-Komponentenbibliothek ausrichten kann. Faktisch
ist es jedoch nur Metadaten — die echten Komponenten liegen in `apps/web`.
---
## App: `apps/web` — echte App-Shell, Sidebar-Konfig, Routing
**package.json:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/apps/web/package.json`
**Stack:** React Router 7 (RR7), Vite, Tailwind v4, shadcn/Radix. SaasRock-aehnliche Struktur.
### App-Shell / Layout
| Asset | Pfad | Was |
| --- | --- | --- |
| `AppLayout` | `apps/web/app/components/app/AppLayout.tsx` | Einstieg: `SkipToContent` + `CommandPalette` + `ShadcnSidebarLayout`; Prop `layout: "app" \| "admin" \| "docs"` |
| `ShadcnSidebarLayout` | `apps/web/app/components/layouts/sidebars/shadcn/ShadcnSidebarLayout.tsx` | Authentifizierte Shell: `SidebarProvider` → `ShadcnAppSidebar` + `SidebarInset` (Header mit Breadcrumb/`SidebarTrigger`/`NavActions`, `<main id="main-content">`). Liest Tenant-`themeConfig` + Dark-Mode → injiziert `--sidebar*` CSS-Variablen. |
| `ShadcnAppSidebar` | `apps/web/app/components/layouts/sidebars/shadcn/app-sidebar.tsx` | Waehlt je `layout` die Menue-Quelle: `AppSidebar`/`AdminSidebar`/`DocsSidebar`; wendet Permission-/Sichtbarkeits-Filter (`getVisibleItem`) + Active-State an; rendert `NavMain`/`NavSecondary` |
| `NavMain` | `.../shadcn/nav-main.tsx` | Rendert `SidebarGroupDto[]` → shadcn `SidebarMenu` mit collapsible Untermenues; setzt `data-ui-id`-Attribute via `getUiIdAttributes` |
| weitere Nav | `.../shadcn/nav-secondary.tsx`, `nav-quick-links.tsx`, `nav-actions.tsx`, `nav-user.tsx`, `team-switcher.tsx` | Sekundaer-Nav, Quick-Links, Header-Aktionen (Search/AI/Notifications/Feedback), User-/Team-Switcher |
### Sidebar-Konfiguration (datengetrieben)
| Asset | Pfad | Was |
| --- | --- | --- |
| `SideBarItem` (Interface) | `apps/web/app/application/sidebar/SidebarItem.ts` | Item-Vertrag: `id/title/path/icon/items/permission/adminOnly/tenantUserTypes/isCollapsible/isSecondary/isQuickLink/redirectTo/uiIdPattern/uiIdParams/...` |
| `SidebarGroupDto` | `apps/web/app/application/sidebar/SidebarGroupDto.ts` | `{ id?; items: SideBarItem[]; title?; type?: "main"\|"secondary"\|"quick-link" }` |
| `AppSidebar(props)` | `apps/web/app/application/sidebar/AppSidebar.tsx` | Baut Tenant-App-Menue dynamisch aus `entities`, `entityGroups`, `productAccess` (Feature-Gating: crm-basic, email-marketing, workflow-engine, api, ai-credits); host-aware Pfade via `appPath`/`tenantPath` |
| `AdminSidebar(props)` | `apps/web/app/application/sidebar/AdminSidebar.tsx` | Statisches Admin-Menue in Segmenten Manage/Build/Market/Settings; Permission-getaggt; Plugin-Routen via `enabledAdminRouteItem`/`disabledPluginRouteSlugs` ausblendbar |
| `DocsSidebar()` | `apps/web/app/application/sidebar/DocsSidebar.ts` | Statischer Docs-Baum; vergibt stabile `ui.nav.docs.*`-IDs (`withDocsSidebarIds`) |
### UI-Registry (stabile IDs / data-ui-id)
| Asset | Pfad | Was |
| --- | --- | --- |
| `uiRegistry.ts` | `apps/web/app/application/ui/uiRegistry.ts` | Single source of truth fuer stabile UI-IDs: `UI_SURFACE_IDS`, `UI_ACTION_IDS`, `UI_NAV_IDS`, `UI_ID_PATTERNS`; Helfer `getSidebarSurfaceId(layout)`, `uiIdSegment(value)`, `getUiIdAttributes(item)` (erzeugt `data-ui-id` / `-pattern` / `-param-*`) |
### RR7-Routing-Konvention (`app/routes`)
| Asset | Pfad | Was |
| --- | --- | --- |
| Route-Manifest | `apps/web/app/routes.ts` | Nutzt **Remix-v1-Flat-Folder-Convention** ueber `remixConfigRoutes` + `createRoutesFromFolders` (NICHT die RR7-Default-Convention). Definiert `defaultIgnoredRouteFilePatterns` (`*.test.*`, `*.server.*`, `*.css`, dotfiles) und `defaultDisabledPluginRouteFilePatterns` (deaktivierbare Plugin-Routen) |
| Convention-Impl | `apps/web/app/utils/compat/v1-route-convention.ts` | Kopie von `remix-run/v1-compat-utils`; Param-Prefix `$`, Escape `[ ]`, Optional `( )`, Module-Exts `.js/.jsx/.ts/.tsx/.md/.mdx` |
| `react-router.config.ts` | `apps/web/react-router.config.ts` | RR7-App-Config |
| Routen-Tests | `apps/web/app/routes.test.ts`, `apps/web/app/utils/compat/v1-route-convention.test.ts` | Verifizieren das Manifest |
**Routen-Topologie (Auszug, `apps/web/app/routes/`, ~102 Eintraege):**
- Tenant-Space: `app.$tenant.tsx` (Layout) + `app.$tenant/` (dashboard, crm, email-marketing, notifications, settings, forms, g.$group, sites, internal, __portals, __widgets, __workflows, $entity.__autogenerated).
- Admin-Space: `admin.tsx` + `admin/` (accounts, analytics, entities, workflow-engine, api, feature-flags, navigation, portals, prompts, email-marketing, marketing-leads, knowledge-base, help-desk, onboarding, metrics, settings, audit-trails, events).
- Docs: `docs/`. Auth: `auth.*`, `forgot-password.*`, `login`-Flows. Fehlerseiten: `401/403/404/500/$`.
- Hot-Swap-Demo (dev-only): `apps/web/app/routes/hot-swap-demo.tsx`.
Shell-Verdrahtung: `app.$tenant.tsx` wrappt Tenant-Routen ueber
`<AppLayout layout="app">`; `app-sidebar.tsx` zieht die Menue-Daten ueber
`useAppOrAdminData()` (RR7 Loader-Daten) und ruft die passende Sidebar-Factory.
### Theme / Dark-Mode
| Asset | Pfad | Was |
| --- | --- | --- |
| `root.tsx` | `apps/web/app/root.tsx` | Setzt `dark`-Klasse aus `userSession.lightOrDarkMode` (gated ueber `DARK_MODE_IN_APP`), `theme-${rootData.theme}` (+ `-scaled`); Actions `toggleLightOrDarkMode`, `setTheme`, `setLocale` (Persistenz in User-Session/DB) |
| `themes.css` | `apps/web/app/themes.css` (86 KB) | Vordefinierte Themes als CSS-Variablen |
| `globals.css` | `apps/web/app/globals.css` | Globale Tokens/Tailwind-Base |
| `Theme`-Enum | `apps/web/app/application/enums/shared/Theme.ts` | `LIGHT \| DARK` |
| Theme-Preview-Store | `apps/web/app/stores/themePreviewStore.ts` (via `useThemePreviewStore`) | Live-Preview-Override im Shell-Layout |
| `parseThemeConfig` / `ThemeConfig` | `apps/web/app/utils/theme/ThemeConfig.ts` | Parst Tenant-`themeConfig` (Farben inkl. `darkColors`, `sidebar*`-Farben) |
| `tailwind.config.ts` | `boilerplate/tailwind.config.ts` (Repo-Root) | Tailwind-Setup |
**Prisma-Bezug (Theme/Shell-State):**
- `prisma/schema/accounts.prisma:17` — `User.lightOrDarkMode String?` (Hell/Dunkel-Praeferenz pro User).
- `prisma/schema/accounts.prisma:188` — `Tenant.themeConfig String?` (Per-Tenant-Whitelabel-Theme als serialisierter JSON-String).
- `prisma/schema/portals.prisma` — `Portal`, `PortalPage`, ... (Portal-Feature; Bezug zu `PortalRegistryItem`/Admin-Portals-Nav).
### i18n in der UI
| Asset | Pfad | Was |
| --- | --- | --- |
| Web-i18n-Setup | `apps/web/app/locale/` (`i18n.ts`, `i18next.server.ts`, `i18next.global.server.ts`, `format.ts`, `date-fns.ts`) | i18next + RR7 SSR-Integration |
| Locale-Daten | `apps/web/public/locales/{de,en}/*.json` | Namespaces: `translations` (legacy Bulk), `common`, `errors`, `mail`, `settings`, `billing`, `crm`, `auth`, `emailMarketing` |
| Shared-Paket | `packages/i18n/src/index.ts` (`@toolctrl/i18n`) | `defaultLocale="de"`, `supportedLocales=["de","en"]`, `i18nNamespaces`, `i18nConfig`, `getDirection`/`isRTL` (RTL-Support vorbereitet), Re-Exporte `formatNumber/Currency/Date/RelativeTime/List`, `formatIcu`, `createUseTranslate` |
Sidebars/Registries verwenden durchgaengig i18n-Keys (`t(...)` bzw. `labelKey`),
nie Klartext — `assertI18nLabel` (view-models) erzwingt das auf Vertragsebene.
### Komponenten-/Page-Bibliothek (real, in `apps/web`)
Insgesamt ~707 `.tsx`-Komponenten (ohne `*.test.tsx`/`*.stories.tsx`) unter
`apps/web/app/components/`. Verzeichnis-Highlights:
| Bereich | Pfad | Inhalt (Auszug) |
| --- | --- | --- |
| Primitives (shadcn/Radix) | `apps/web/app/components/ui/` | `button.tsx`, `input.tsx`, `select.tsx`, `dialog.tsx`, `sheet.tsx`, `drawer.tsx`, `popover.tsx`, `table.tsx`, `tabs.tsx`, `form.tsx`, `sidebar.tsx`, `command.tsx`, `toast.tsx`/`sonner.tsx`, `badge.tsx`, `card.tsx`, `calendar.tsx`, `chart.tsx`, `multi-select.tsx`, `stepper.tsx`, `pagination.tsx`, ... |
| Tabellen | `apps/web/app/components/ui/tables/` | `TableSimple.tsx`, `TableNew.tsx`, `TablePagination.tsx`, `GridPagination.tsx`, `ColumnSelector.tsx`, `CollapsibleRow.tsx`, `RowsLoadMoreCard.tsx`, `PreviewTableSimple.tsx` |
| Formulare | `apps/web/app/components/ui/forms/`, `.../input`, `.../selectors`, `.../editors` | `FormGroup.tsx`, `InputGroup.tsx` + reichhaltige Input-/Selector-/Editor-Komponenten |
| Modale/Dialoge | `apps/web/app/components/ui/modals/`, `.../slideOvers` | `Modal.tsx`, `ModalOrSlide.tsx`, `ConfirmDialog.tsx`, `ConfirmModal.tsx`, `ActionResultModal.tsx`, `ErrorModal.tsx`, `SuccessModal.tsx`, `OpenModal.tsx`, ... |
| Empty/Loading | `apps/web/app/components/ui/emptyState`, `.../loaders` | Empty-/Loading-States |
| Command-Palette | `apps/web/app/components/ui/commandPalettes/CommandPalette` | Kommando-Palette (im Shell verdrahtet) |
| Entitaeten/Editoren | `apps/web/app/components/entities`, `.../editors`, `.../json`, `.../pdf` | Entity-Builder-UI, Rich-Editoren, JSON-/PDF-Viewer |
| Weitere | `aceternity`, `ai-elements`, `analytics`, `audit`, `brand`, `cookies`, `dashboard`, `images`, `motion-primitives`, `notifications`, `pages`, `public`, `recaptcha` | App-spezifische Komponenten-Cluster |
### Hot-Swap-Aktivierung (Drop-in, real verdrahtet)
| Asset | Pfad | Was |
| --- | --- | --- |
| Renderer | `apps/web/app/components/ui/hot-swap/HotSwapPageRenderer.tsx` | Bruecke: `MockPage`-Deskriptor (`@toolctrl/ui-mock`) → RR7-JSX, nutzt `createShellRegistries()` aus `@toolctrl/ui-app` |
| Demo-Route | `apps/web/app/routes/hot-swap-demo.tsx` | Rendert `createHotSwapDrillPage()`; gated auf `NODE_ENV !== "production"` |
| Renderer-Test | `apps/web/app/components/ui/hot-swap/HotSwapPageRenderer.test.tsx` | Prueft Deskriptor → DOM (Sidebar/Breadcrumbs/Header/Cards/Activity) |
**HiveGrid-Relevanz (apps/web gesamt):** Sehr hoch. Dies ist der reale,
sofort wiederverwendbare Fundus: vollstaendige authentifizierte App-Shell
(App/Admin/Docs), datengetriebene Sidebar mit Permission-/Feature-Gating,
RR7-Flat-Folder-Routing inkl. Tenant- (`app.$tenant`) und Admin-Trennung,
Theme/Dark-Mode mit Per-Tenant-Whitelabel (`Tenant.themeConfig`), i18n (de/en),
und eine breite shadcn/Radix-Komponentenbibliothek (Tabellen/Forms/Modale).
Querschnittlich relevant fuer praktisch jedes HiveGrid-Modul.
---
## App: `apps/storybook`
**Zweck:** Storybook-8-Workbench + Visual-Regression (lost-pixel) + a11y-Addon.
**package.json:** `/Users/raffaelsheikh/Documents/GitHub/boilerplate/apps/storybook/package.json`
(`@storybook/react-vite` 8.6, Addons: essentials/a11y/interactions/themes; `lost-pixel` 3.22; dep `@toolctrl/i18n`)
**Zentrale Dateien:**
| Asset | Pfad | Was |
| --- | --- | --- |
| Storybook-Config | `apps/storybook/.storybook/main.ts` | Stories aus `apps/storybook/src/**` **und** `packages/{ui,ui-app,ui-subframe,ui-mock}/src/**/*.stories.*`; Tailwind-v4-Vite-Plugin; Alias `~` → `apps/web/app` |
| i18n-Bootstrap | `apps/storybook/.storybook/i18n.ts`, `preview.tsx`, `preview.css` | i18next + Theme-Decorator in Preview |
| Lost-Pixel | `apps/storybook/lostpixel.config.ts`, `.lostpixel/` | Visual-Regression-Config + Baselines |
| Test-Runner | `apps/storybook/.storybook/test-runner.ts` | `axe-playwright`-basierte a11y-Checks im Test-Runner |
| Docs (MDX) | `apps/storybook/src/Introduction.mdx`, `src/ThemeGuide.mdx` | Einfuehrung + Theme-Guide |
| Scripts | `package.json` | `storybook` (dev :6006), `build-storybook`, `test:storybook`, `visual-regression`(:update) |
**Status:** Stories sind aktuell (~21) groesstenteils inline-styled Demos ueber
`packages/ui/src/stories/_demo-primitives.tsx` (CSS-Variablen-Tokens aus
`preview.css`); der "echte Tailwind-v4-Pipeline"-Pfad ist im `main.ts` als
Migrations-Vorbereitung schon vorhanden (Tailwind-Plugin no-op fuer heutige Stories).
**HiveGrid-Relevanz:** QA-/Design-Querschnitt. Direkt nutzbar als Visual-
Regression- und a11y-Gate fuer eine HiveGrid-Komponentenbibliothek; der Alias
auf `apps/web/app` erlaubt, echte App-Komponenten in Stories zu ziehen.
---
## Maschinenlesbare UI-Inventur & Verifikations-Gates
Verbindet Vertrag ↔ echte Komponenten und wird von der Test-Suite geprueft.
| Asset | Pfad | Was |
| --- | --- | --- |
| Komponenten-Inventur | `docs/ui-inventory/components.json` | 71 Eintraege (`stableName`/`path`/`category`/`library`/`status`); Schema `docs/ui-inventory/schema.json` |
| Surface-Map | `docs/ui-inventory/surfaces.json` (+ `surfaces.schema.json`, `surface-map.md`) | Mapping stabiler `data-ui-id` → Quelldateien/Renderer |
| Inventur-Test | `apps/web/app/application/ui/uiInventory.test.ts` | Prueft, dass Inventur-Pfade existieren, alle `components/ui/*.tsx`-Primitives gelistet sind und Registry-IDs in der Surface-Map stehen |
| Adapter-Test | `apps/web/app/application/ui/uiAdapters.test.ts`, `uiRegistry.test.ts` | Prueft `@toolctrl/ui`-Adapter ↔ App-Registry-Konsistenz |
| Hot-Swap-Status | `docs/ui-inventory/hot-swap-status.md` | Dokumentiert, was verdrahtet ist (`ui-app`/`ui-mock` aktiv, `ui-subframe` Platzhalter) + Aktivierungs-Checkliste |
| Boundary-Gate | `scripts/verify-phase-c-boundaries.mjs` (npm `verify:phase-c-boundaries`) | Erzwingt, dass Aktivierung keine Laufzeit-Layer-Grenze verletzt |
| Hot-Swap-Verify | `scripts/verify-ui-hot-swap.ts` (npm `verify:ui-hot-swap`, `test:hot-swap`) | Mock-getriebener Hot-Swap-Drill |
| Visual-Verify | `scripts/verify-ui-mock-visual.ts` (npm `verify:ui-visual`) | Mock-Visual-Baseline |
**HiveGrid-Relevanz:** Governance-/Konventions-Querschnitt. Das Muster
"stabile ID-Registry + maschinenlesbare Inventur + dateisystem-geprueftes Gate"
ist direkt als HiveGrid-UI-Governance uebertragbar (Whitelabel/Hot-Swap-Faehigkeit
mit hartem CI-Schutz der Layer-Grenzen).
---
## Wichtige Vorbehalte (Anti-Erfindung)
- `@toolctrl/ui` enthaelt **keine** lauffaehigen Komponenten — nur Adapter-Metadaten,
die auf `apps/web/app/...` verweisen. Echte Primitives = `apps/web/app/components/ui/*`.
- `@toolctrl/ui-subframe` ist **leer** (Placeholder). Es gibt **keinen** gebauten
Subframe-Adapter-Layer in der Boilerplate.
- Der `AppShell` in `@toolctrl/ui-app` ist ein **headless Stub** (nur `data-*`),
nicht die echte sichtbare Shell. Sichtbare Shell = `ShadcnSidebarLayout`.
- Hot-Swap ist **nur** an der dev-only Demo-Route (`hot-swap-demo.tsx`) end-to-end
aktiv; Produktiv-Routen rendern weiterhin direkt aus `apps/web/app/components/`.
- Routing nutzt die **Remix-v1-Flat-Folder-Convention** (Compat-Layer), nicht die
RR7-Default-`routes.ts`-Convention.
- Es gibt **keine** Prisma-Modelle, die exklusiv zu den UI-Paketen gehoeren;
UI-relevant sind `User.lightOrDarkMode` und `Tenant.themeConfig` (accounts.prisma)
sowie `Portal*` (portals.prisma).