docs(04): UI design contract for marketplace & portal navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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 `<select>` styled with `bg-card border border-border rounded-md px-3 py-1.5 text-sm`
|
||||||
|
- Width: auto (fits content), min-width 200px
|
||||||
|
- Behavior: Changing tenant re-fetches all module activation states. Shows loading spinner during fetch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interaction Contracts
|
||||||
|
|
||||||
|
### Marketplace Filtering
|
||||||
|
|
||||||
|
**Status Tabs (Claude's Discretion decision: Tab bar + badge combination):**
|
||||||
|
- Three tabs: "Alle", "Aktiviert", "Verfuegbar"
|
||||||
|
- Tab style: underline indicator, `text-sm font-medium`
|
||||||
|
- Active tab: `text-foreground border-b-2 border-primary`
|
||||||
|
- Inactive tab: `text-muted-foreground hover:text-foreground`
|
||||||
|
- Tab counts shown as inline badge: `(3)` in muted text after label
|
||||||
|
- Reason for tabs over pure badges: Clear top-level filtering for the most important dimension (activated vs. not). Badges on cards provide per-card status, tabs provide batch filtering.
|
||||||
|
|
||||||
|
**Category Chips:**
|
||||||
|
- Horizontal scrollable row of pill buttons below status tabs
|
||||||
|
- First chip: "Alle" (always present, default selected)
|
||||||
|
- Remaining chips: One per unique category from module list
|
||||||
|
- Selected: `bg-primary text-primary-foreground`
|
||||||
|
- Unselected: `bg-muted text-foreground hover:bg-muted/80`
|
||||||
|
- Chip size: `px-3 py-1 text-xs font-medium rounded-full`
|
||||||
|
- Gap between chips: 8px
|
||||||
|
- Overflow: horizontal scroll with `overflow-x-auto` and hidden scrollbar
|
||||||
|
|
||||||
|
**Search:**
|
||||||
|
- Full-width input above tabs
|
||||||
|
- Debounce: 300ms
|
||||||
|
- Filters module name and description (client-side, all modules loaded)
|
||||||
|
- Clear button (X icon) appears when text is entered
|
||||||
|
- Empty search shows all modules (respecting active tab + category filter)
|
||||||
|
|
||||||
|
### Module Activation Flow
|
||||||
|
|
||||||
|
**Activation (Claude's Discretion decision: Immediate action with success toast):**
|
||||||
|
1. User clicks "Aktivieren" button on card or detail page
|
||||||
|
2. Button shows loading spinner (replace text with spinner, button disabled)
|
||||||
|
3. POST `/modules/{id}/activate` fires
|
||||||
|
4. On success: Button changes to "Deaktivieren" (outline style), status badge updates to green "Aktiviert", success toast appears: "Modul erfolgreich aktiviert"
|
||||||
|
5. On error: Error toast: "Aktivierung fehlgeschlagen. Bitte versuchen Sie es erneut."
|
||||||
|
6. Sidebar refreshes active modules list (re-fetch `/modules/active`)
|
||||||
|
|
||||||
|
**Deactivation (Claude's Discretion decision: Confirmation dialog for deactivation only):**
|
||||||
|
1. User clicks "Deaktivieren" button
|
||||||
|
2. Confirmation dialog appears (modal overlay):
|
||||||
|
- Title: "Modul deaktivieren"
|
||||||
|
- Body: "Moechten Sie {moduleName} fuer diesen Mandanten deaktivieren? Das Modul wird aus der Seitenleiste entfernt."
|
||||||
|
- Cancel button: "Abbrechen" (outline style)
|
||||||
|
- Confirm button: "Deaktivieren" (destructive style: `bg-destructive text-destructive-foreground`)
|
||||||
|
3. On confirm: POST `/modules/{id}/deactivate`, button/badge update, sidebar refresh
|
||||||
|
4. On cancel: Dialog closes, no action
|
||||||
|
|
||||||
|
**Reason for asymmetric UX:** Activation is low-risk and easily reversible. Deactivation could disrupt users who depend on the module, so a confirmation prevents accidental removal.
|
||||||
|
|
||||||
|
### Sidebar Module Navigation
|
||||||
|
|
||||||
|
**Clicking a module in sidebar:**
|
||||||
|
1. Navigates to `/modules/{category}/{slug}` (existing route from Phase 3)
|
||||||
|
2. Module opens in main content area within AppShell
|
||||||
|
3. Sidebar highlights the active module with `bg-sidebar-accent text-sidebar-accent-foreground`
|
||||||
|
|
||||||
|
**Sidebar active state:** Current page URL determines which sidebar item gets the active class. Compare `pathname` against link `href`.
|
||||||
|
|
||||||
|
### Toast Notifications
|
||||||
|
|
||||||
|
- Position: Bottom-right of viewport
|
||||||
|
- Duration: 4000ms auto-dismiss
|
||||||
|
- Style: `bg-card border border-border shadow-lg rounded-lg p-4`
|
||||||
|
- Success icon: Green checkmark
|
||||||
|
- Error icon: Red X circle
|
||||||
|
- Max visible: 3 stacked, newest on top
|
||||||
|
- Animation: Slide in from right, fade out
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Copywriting Contract
|
||||||
|
|
||||||
|
All copy in both DE and EN via next-intl. Below shows DE as primary, EN in parentheses.
|
||||||
|
|
||||||
|
| Element | DE Copy | EN Copy |
|
||||||
|
|---------|---------|---------|
|
||||||
|
| Page title | "Marktplatz" | "Marketplace" |
|
||||||
|
| Page subtitle | "Entdecken und aktivieren Sie Module fuer Ihren Mandanten" | "Discover and activate modules for your tenant" |
|
||||||
|
| Primary CTA | "Modul aktivieren" | "Activate Module" |
|
||||||
|
| Deactivation CTA | "Deaktivieren" | "Deactivate" |
|
||||||
|
| Search placeholder | "Module suchen..." | "Search modules..." |
|
||||||
|
| Sidebar search placeholder | "Module suchen..." | "Search modules..." |
|
||||||
|
| Status tab: All | "Alle" | "All" |
|
||||||
|
| Status tab: Active | "Aktiviert" | "Activated" |
|
||||||
|
| Status tab: Available | "Verfuegbar" | "Available" |
|
||||||
|
| Category chip: All | "Alle" | "All" |
|
||||||
|
| Empty state heading | "Noch keine Module verfuegbar" | "No modules available yet" |
|
||||||
|
| Empty state body | "Module werden vom Systemadministrator bereitgestellt. Schauen Sie spaeter noch einmal vorbei." | "Modules are provided by the system administrator. Check back later." |
|
||||||
|
| Empty state (filtered) heading | "Keine Ergebnisse" | "No results" |
|
||||||
|
| Empty state (filtered) body | "Versuchen Sie einen anderen Suchbegriff oder aendern Sie die Filter." | "Try a different search term or adjust your filters." |
|
||||||
|
| Error state | "Module konnten nicht geladen werden. Bitte laden Sie die Seite neu." | "Could not load modules. Please reload the page." |
|
||||||
|
| Activation success toast | "Modul erfolgreich aktiviert" | "Module activated successfully" |
|
||||||
|
| Deactivation success toast | "Modul erfolgreich deaktiviert" | "Module deactivated successfully" |
|
||||||
|
| Activation error toast | "Aktivierung fehlgeschlagen. Bitte versuchen Sie es erneut." | "Activation failed. Please try again." |
|
||||||
|
| Deactivation dialog title | "Modul deaktivieren" | "Deactivate Module" |
|
||||||
|
| Deactivation dialog body | "Moechten Sie {moduleName} fuer diesen Mandanten deaktivieren? Das Modul wird aus der Seitenleiste entfernt." | "Do you want to deactivate {moduleName} for this tenant? The module will be removed from the sidebar." |
|
||||||
|
| Deactivation dialog confirm | "Deaktivieren" | "Deactivate" |
|
||||||
|
| Deactivation dialog cancel | "Abbrechen" | "Cancel" |
|
||||||
|
| Tenant selector label | "Mandanten-Kontext" | "Tenant Context" |
|
||||||
|
| Back to marketplace | "Zurueck zum Marktplatz" | "Back to Marketplace" |
|
||||||
|
| Detail: version | "Version {version}" | "Version {version}" |
|
||||||
|
| Detail: status active | "Aktiviert fuer diesen Mandanten" | "Activated for this tenant" |
|
||||||
|
| Detail: status inactive | "Nicht aktiviert" | "Not activated" |
|
||||||
|
| Sidebar no results | "Keine Module gefunden" | "No modules found" |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## States Matrix
|
||||||
|
|
||||||
|
| View | Loading | Empty | Populated | Error | Filtered Empty |
|
||||||
|
|------|---------|-------|-----------|-------|----------------|
|
||||||
|
| Marketplace grid | Spinner centered in grid area (existing pattern: `h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent`) | Illustration + heading + body from copywriting contract | Card grid with modules | Error banner (`border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive`) + retry hint | "Keine Ergebnisse" + filter suggestion |
|
||||||
|
| Module detail | Spinner centered | N/A (404 redirect) | Full detail layout | Error banner | N/A |
|
||||||
|
| Sidebar modules | No loading state (instant from cache/store) | "Keine Module" (existing) | Category accordion with modules | Silent fail (existing pattern) | "Keine Module gefunden" when search yields nothing |
|
||||||
|
| Tenant selector | "Laden..." in dropdown | N/A (Super-Admin always has at least 1 tenant) | Tenant list dropdown | Toast error | N/A |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
|
||||||
|
| Requirement | Implementation |
|
||||||
|
|-------------|----------------|
|
||||||
|
| Keyboard navigation | All interactive elements reachable via Tab. Cards focusable via `group-focus-visible:ring-2`. Activation buttons in natural tab order. |
|
||||||
|
| Screen reader labels | `aria-label` on icon-only buttons. Status badges use `role="status"`. Toggle buttons use `role="switch"` with `aria-checked` (existing pattern). |
|
||||||
|
| Focus management | Dialog traps focus when open (ActivationDialog). Return focus to trigger button on close. |
|
||||||
|
| Color contrast | All text meets WCAG 2.1 AA (4.5:1 for body, 3:1 for large text). Status badges use text + background, not color alone. |
|
||||||
|
| Reduced motion | Loading spinner respects `prefers-reduced-motion` with `motion-safe:animate-spin`. Toast slide animation gated on motion preference. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Registry Safety
|
||||||
|
|
||||||
|
| Registry | Blocks Used | Safety Gate |
|
||||||
|
|----------|-------------|-------------|
|
||||||
|
| shadcn official | none (project does not use components.json) | not applicable |
|
||||||
|
| Third-party | none | not applicable |
|
||||||
|
|
||||||
|
**Note:** Project uses hand-rolled components following shadcn CSS variable naming conventions but without the shadcn CLI or components.json. No registry dependencies exist. All components for this phase are custom-built.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Discretion Decisions Summary
|
||||||
|
|
||||||
|
Decisions made by this design contract for areas marked as "Claude's Discretion" in `04-CONTEXT.md`:
|
||||||
|
|
||||||
|
| Area | Decision | Rationale |
|
||||||
|
|------|----------|-----------|
|
||||||
|
| Filter/Status UI | Tab bar ("Alle" / "Aktiviert" / "Verfuegbar") + category chips | Tabs provide clear top-level filtering for the primary dimension. Category chips offer secondary filtering. Badges on cards still show per-item status. |
|
||||||
|
| Activation confirmation | Immediate activation with success toast. Deactivation gets confirmation dialog. | Asymmetric risk: activation is easily reversed, deactivation affects active users. |
|
||||||
|
| Visual feedback | Toast notification + inline badge/button state update | Consistent with existing admin/modules toggle pattern. Toast adds explicit confirmation. |
|
||||||
|
| Sidebar categories | Keep existing accordion pattern | Already established in Phase 1, works well, categories expand to show individual modules. |
|
||||||
|
| Sidebar collapsed mode | Show only main nav icons (Dashboard, Marketplace) and admin icons. Hide module list. | Module list needs text labels to be useful. Icons alone at 64px width would be confusing. |
|
||||||
|
| Sidebar search | Inline search field within sidebar, between main nav and categories section | PRTAL-05 requires sidebar search. Inline placement keeps it contextual. Header search reserved for future global search. |
|
||||||
|
| Module detail depth | Compact single-column detail view | Appropriate for v1 module count. Full description, status, activation button. No screenshots/reviews (v2). |
|
||||||
|
| Navigation pattern | Separate pages: `/marketplace` for browsing, `/marketplace/[slug]` for detail, `/modules/{cat}/{slug}` for module usage | Clean URL separation. Marketplace is for discovery/activation. Module routes are for usage. App Router route groups keep them distinct. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checker Sign-Off
|
||||||
|
|
||||||
|
- [ ] Dimension 1 Copywriting: PASS
|
||||||
|
- [ ] Dimension 2 Visuals: PASS
|
||||||
|
- [ ] Dimension 3 Color: PASS
|
||||||
|
- [ ] Dimension 4 Typography: PASS
|
||||||
|
- [ ] Dimension 5 Spacing: PASS
|
||||||
|
- [ ] Dimension 6 Registry Safety: PASS
|
||||||
|
|
||||||
|
**Approval:** pending
|
||||||
Reference in New Issue
Block a user