383 lines
25 KiB
Markdown
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).
|