From 4ea1b6144f26cfc296b57e5aa32bafa98300829f Mon Sep 17 00:00:00 2001 From: Schalli Date: Mon, 22 Jun 2026 13:53:44 +0200 Subject: [PATCH] docs(04): UI design contract for marketplace & portal navigation Co-Authored-By: Claude Sonnet 4.6 --- .../04-UI-SPEC.md | 445 ++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 .planning/phases/04-marketplace-portal-navigation/04-UI-SPEC.md diff --git a/.planning/phases/04-marketplace-portal-navigation/04-UI-SPEC.md b/.planning/phases/04-marketplace-portal-navigation/04-UI-SPEC.md new file mode 100644 index 0000000..c5ae000 --- /dev/null +++ b/.planning/phases/04-marketplace-portal-navigation/04-UI-SPEC.md @@ -0,0 +1,445 @@ +--- +phase: 4 +slug: marketplace-portal-navigation +status: draft +shadcn_initialized: false +preset: none +created: 2026-06-22 +--- + +# Phase 4 — UI Design Contract + +> Visual and interaction contract for Marketplace & Portal Navigation. Generated by gsd-ui-researcher, verified by gsd-ui-checker. + +--- + +## Design System + +| Property | Value | +|----------|-------| +| Tool | none (hand-rolled components with shadcn-compatible CSS variable naming) | +| Preset | not applicable | +| Component library | none (custom components, Radix-compatible token structure) | +| Icon library | Inline SVG (project convention from Phase 1-3, no external icon library) | +| Font | Inter, system-ui, -apple-system, sans-serif (declared as `--font-sans` in globals.css) | + +**Note:** The project uses shadcn-compatible CSS custom property naming (`--primary`, `--card`, `--muted`, etc.) and Tailwind v4 `@theme inline` mapping, but does not have `components.json`. All UI components are hand-rolled. This phase continues that pattern. + +--- + +## Spacing Scale + +Declared values (must be multiples of 4): + +| Token | Value | Usage | +|-------|-------|-------| +| xs | 4px | Icon gaps, inline badge padding, chip internal spacing | +| sm | 8px | Compact element spacing, card internal gaps, filter chip gaps | +| md | 16px | Default element spacing, card padding, search bar padding | +| lg | 24px | Section padding, space between marketplace header and grid | +| xl | 32px | Layout gaps, space between major page sections | +| 2xl | 48px | Empty state vertical padding, page top/bottom padding | +| 3xl | 64px | Not used in this phase | + +Exceptions: Sidebar search input height at 36px (not a spacing token — component-specific dimension for comfortable text input within narrow 240px sidebar). Marketplace card minimum height at 180px for visual consistency in the grid. + +--- + +## Typography + +| Role | Size | Weight | Line Height | Usage in Phase 4 | +|------|------|--------|-------------|-------------------| +| Body | 14px | 400 (regular) | 1.5 | Card descriptions, sidebar module names, filter labels | +| Label | 12px | 600 (semibold) | 1.4 | Category badges, status badges, sidebar section headers, version text | +| Heading | 20px | 700 (bold) | 1.2 | Page titles ("Marktplatz"), section headings | +| Display | 28px | 700 (bold) | 1.2 | Not used in this phase (reserved for Dashboard phase) | + +**Font weights used:** 400 (regular) and 600 (semibold). 700 (bold) only for page headings per existing codebase convention (`text-2xl font-bold` in admin pages). + +--- + +## Color + +All values in OKLCH (project convention from Phase 1, defined in `globals.css`). + +| Role | Light Mode | Dark Mode | Usage | +|------|-----------|-----------|-------| +| Dominant (60%) | `oklch(0.99 0 0)` — near-white | `oklch(0.23 0.01 260)` — dark gray-blue | Page background, marketplace grid background | +| Secondary (30%) | `oklch(1 0 0)` / `oklch(0.96 0 0)` — white/light gray | `oklch(0.27 0.01 260)` / `oklch(0.30 0.01 260)` — dark card/muted | Marketplace cards (`bg-card`), sidebar (`bg-sidebar`), filter bar background, tenant selector dropdown | +| Accent (10%) | `oklch(0.91 0.19 102)` — Tessera yellow | `oklch(0.91 0.19 102)` — same yellow | See reserved-for list below | +| Destructive | `oklch(0.55 0.2 27)` — red | `oklch(0.55 0.2 27)` — same red | Deactivation confirmation dialog only | +| Success | `oklch(0.55 0.16 145)` — green | `oklch(0.55 0.16 145)` — green | "Aktiviert" status badge, activation success toast | + +**Accent reserved for:** +1. "Aktivieren" / "Modul aktivieren" primary CTA button background +2. Active module card border highlight on hover (`hover:border-primary/30` — existing pattern) +3. Active sidebar item background accent (`bg-sidebar-accent`) +4. Marketplace search input focus ring (`ring-ring` maps to primary) +5. Tenant context selector active state indicator +6. Toggle switch active state (existing pattern from admin/modules) + +**Accent explicitly NOT used for:** +- Category badges (use `bg-muted` with `text-muted-foreground`) +- Status text (use semantic green/red) +- Card backgrounds (use `bg-card`) + +--- + +## Component Inventory + +### New Components (Phase 4) + +| Component | Location | Purpose | +|-----------|----------|---------| +| `MarketplaceCard` | `apps/web/src/app/(portal)/marketplace/components/MarketplaceCard.tsx` | Module card for marketplace grid — extends ModuleCard pattern with activation toggle and status badge | +| `MarketplaceSearch` | `apps/web/src/app/(portal)/marketplace/components/MarketplaceSearch.tsx` | Search input with live filter, debounced 300ms | +| `CategoryFilter` | `apps/web/src/app/(portal)/marketplace/components/CategoryFilter.tsx` | Horizontal chip/pill row for category filtering | +| `StatusFilter` | `apps/web/src/app/(portal)/marketplace/components/StatusFilter.tsx` | Tab bar: "Alle" / "Aktiviert" / "Verfuegbar" for filtering by activation status | +| `TenantContextSelector` | `apps/web/src/app/(portal)/marketplace/components/TenantContextSelector.tsx` | Dropdown for Super-Admin to switch tenant context in marketplace | +| `ModuleDetailView` | `apps/web/src/app/(portal)/marketplace/[slug]/page.tsx` | Compact detail page for a single module | +| `ActivationDialog` | `apps/web/src/app/(portal)/marketplace/components/ActivationDialog.tsx` | Confirmation dialog for deactivation only (activation is immediate with undo toast) | +| `SidebarSearch` | `apps/web/src/components/layout/sidebar-search.tsx` | Inline search/filter field within sidebar categories section | + +### Reused Components (from Phase 1-3) + +| Component | Source | Reuse | +|-----------|--------|-------| +| `ModuleCard` | `apps/web/src/app/(portal)/modules/[category]/components/ModuleCard.tsx` | Pattern reference for MarketplaceCard — same card structure, extended with activation controls | +| `Sidebar` | `apps/web/src/components/layout/sidebar.tsx` | Extended with SidebarSearch and individual module links per category | +| `Header` | `apps/web/src/components/layout/header.tsx` | Unchanged — breadcrumb updates for marketplace route | +| `AppShell` | `apps/web/src/components/layout/app-shell.tsx` | Unchanged — marketplace pages render within existing shell | + +--- + +## Layout Specifications + +### Marketplace Page (`/marketplace`) + +``` ++----------------------------------------------------------+ +| Header (sticky, 60px) | ++--------+-------------------------------------------------+ +| Sidebar | Marketplace Page | +| 240px | | +| | [Tenant Selector - Super-Admin only] | +| | | +| | Marktplatz (h1, 20px bold) | +| | Entdecken Sie verfuegbare Module (14px muted) | +| | | +| | [Search input, full width] | +| | [StatusTabs: Alle | Aktiviert | Verfuegbar] | +| | [CategoryChips: Alle | Domain-Tools | Utils...] | +| | | +| | +--------+ +--------+ +--------+ | +| | | Card 1 | | Card 2 | | Card 3 | | +| | +--------+ +--------+ +--------+ | +| | +--------+ +--------+ +--------+ | +| | | Card 4 | | Card 5 | | Card 6 | | +| | +--------+ +--------+ +--------+ | ++--------+-------------------------------------------------+ +``` + +**Grid responsive breakpoints:** +- Mobile (<768px): 1 column, sidebar hidden +- Tablet (768px-1023px): 2 columns, sidebar collapsed or hidden +- Desktop (1024px-1279px): 3 columns, sidebar expanded +- Wide (1280px+): 4 columns, sidebar expanded + +**Grid gap:** 16px (`gap-4`) + +### MarketplaceCard Anatomy + +``` ++------------------------------------------+ +| [Icon 24x24] Module Name | +| Category Badge Status Badge| +| | +| Localized description text, max 2 lines | +| with line-clamp-2 truncation. | +| | +| v1.0.0 [Aktivieren] (button) | ++------------------------------------------+ +``` + +- Card padding: 20px (`p-5`) +- Card border: `border border-border` (1px) +- Card background: `bg-card` +- Card border-radius: 8px (`rounded-lg`) +- Card hover: `hover:shadow-md hover:border-primary/30` (existing pattern) +- Icon container: `rounded-md bg-muted p-2.5` (40px total, existing pattern) +- Status badge (activated): `bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400` rounded-full px-2 py-0.5 text-xs +- Status badge (available): `bg-muted text-muted-foreground` rounded-full px-2 py-0.5 text-xs +- Category badge: `bg-muted text-muted-foreground` rounded-full px-2 py-0.5 text-xs +- Activation button (activate): `bg-primary text-primary-foreground` rounded-md px-3 py-1.5 text-sm font-medium +- Activation button (deactivate): `border border-border text-foreground hover:bg-muted` rounded-md px-3 py-1.5 text-sm font-medium + +### Module Detail Page (`/marketplace/[slug]`) + +Compact detail view (appropriate for v1 module count). Single-column layout within main content area. + +``` ++------------------------------------------+ +| < Zurueck zum Marktplatz | +| | +| [Icon 48x48] | +| Module Name (h1, 20px bold) | +| Category Badge v1.0.0 | +| | +| Full localized description text. | +| No line clamping. Renders full text. | +| | +| Status: Aktiviert / Nicht aktiviert | +| [Aktivieren / Deaktivieren] (button) | ++------------------------------------------+ +``` + +- Back link: `text-sm text-muted-foreground hover:text-foreground` with left chevron icon +- Icon container: `rounded-lg bg-muted p-4` (56px total) — larger than card icon +- Max content width: `max-w-2xl` (672px) + +### Sidebar Enhancement + +``` +Sidebar (240px expanded): ++----------------------------------+ +| [Dashboard icon] Dashboard | +| [Cart icon] Marktplatz | ++----------------------------------+ +| [Search input _______________] | <-- NEW: SidebarSearch ++----------------------------------+ +| KATEGORIEN [chevron] | +| [Folder] Domain Tools (1) | +| - Domaincheck | +| [Folder] Utilities (2) | +| - Tool A | +| - Tool B | ++----------------------------------+ +| VERWALTUNG | +| [Users icon] Benutzer | +| [Home icon] Mandanten | (Super-Admin only) +| [Box icon] Module | +| [Layers icon] LDAP | ++----------------------------------+ +| [Collapse] | +| [User Info] | ++----------------------------------+ + +Sidebar (64px collapsed): ++--------+ +| [Dash] | +| [Cart] | ++--------+ +| [Usr] | +| [Home] | (Super-Admin only) +| [Box] | +| [Lyrs] | ++--------+ +| [<] | +| [Avtr] | ++--------+ +``` + +**Sidebar Search (SidebarSearch):** +- Position: Between main nav (Dashboard/Marketplace) and Categories section +- Height: 36px +- Padding: 8px horizontal within sidebar's 12px padding (`px-3` within `p-3` nav) +- Placeholder text: "Module suchen..." (14px, muted-foreground) +- Border: `border border-border rounded-md` +- Focus: `focus:ring-2 focus:ring-ring focus:border-transparent` +- Behavior: Filters visible module names and category names in real-time. Categories with no matching modules are hidden. +- Collapsed sidebar: Search field hidden (not enough space at 64px) + +**Sidebar collapsed mode decision:** Show main navigation icons (Dashboard, Marketplace) and admin icons. Do NOT show individual module icons -- category/module list is hidden when collapsed. This keeps the collapsed sidebar clean and avoids icon overload. + +### Tenant Context Selector + +``` ++------------------------------------------+ +| Mandanten-Kontext: [Dropdown v] | +| +--------------------------------------+ | +| | Firma A GmbH [x] | | <-- currently selected +| | Firma B AG | | +| | Test-Mandant | | +| +--------------------------------------+ | ++------------------------------------------+ +``` + +- Position: Top of marketplace page, above title, only visible for `SUPER_ADMIN` role +- Container: `bg-muted/50 rounded-lg p-3 mb-4 border border-border` +- Label: "Mandanten-Kontext:" 12px semibold text-muted-foreground +- Dropdown: Native `