From fbd7d44e882cfdd37eed10c06a03b46b36789c78 Mon Sep 17 00:00:00 2001 From: Schalli Date: Fri, 19 Jun 2026 13:39:58 +0200 Subject: [PATCH] docs(03-03): complete module UI lazy loading plan summary - Document module loader registry, category pages, and expanded module views - Record threat mitigations T-03-09, T-03-10, T-03-11 - Self-check passed: all files and commits verified --- .../03-03-SUMMARY.md | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 .planning/phases/03-module-system-domaincheck/03-03-SUMMARY.md diff --git a/.planning/phases/03-module-system-domaincheck/03-03-SUMMARY.md b/.planning/phases/03-module-system-domaincheck/03-03-SUMMARY.md new file mode 100644 index 0000000..60594ea --- /dev/null +++ b/.planning/phases/03-module-system-domaincheck/03-03-SUMMARY.md @@ -0,0 +1,110 @@ +--- +phase: 03-module-system-domaincheck +plan: 03 +subsystem: module-ui-loader +tags: [lazy-loading, dynamic-imports, next-dynamic, category-page, module-cards] +dependency_graph: + requires: + - module-registry-service + - domaincheck-frontend-page + provides: + - module-loader-registry + - category-page + - expanded-module-view + - api-client-utils + affects: [sidebar-module-links, module-marketplace] +tech_stack: + added: [] + patterns: [next-dynamic-ssr-false, slug-whitelist-registry, category-grid-layout] +key_files: + created: + - apps/web/src/lib/module-loader.ts + - apps/web/src/lib/api.ts + - apps/web/src/app/(portal)/modules/[category]/page.tsx + - apps/web/src/app/(portal)/modules/[category]/components/ModuleCard.tsx + - apps/web/src/app/(portal)/modules/[category]/[moduleSlug]/page.tsx + modified: + - apps/web/src/messages/de.json + - apps/web/src/messages/en.json +key_decisions: + - "MODULE_REGISTRY whitelist pattern: only slugs explicitly listed can trigger dynamic imports (T-03-09)" + - "Client components for all pages to support next/dynamic with ssr:false and client-side API fetching" + - "Inline SVG icons to avoid external icon library dependency while keeping ModuleCard self-contained" +patterns_established: + - "Module registry pattern: slug -> { component, cardComponent? } mapping for lazy loading" + - "Category page pattern: [category] dynamic route with API fetch + filter + grid render" + - "Expanded module view pattern: [moduleSlug] route with whitelist check + dynamic component render" +requirements_completed: [MOD-04] +duration: 6min +completed: 2026-06-19 +--- + +# Phase 03 Plan 03: Module UI Lazy Loading + Category Pages Summary + +**Lazy-loaded module UI rendering via next/dynamic with slug-whitelist registry, category grid pages, and expanded module views** + +## Performance + +- **Duration:** 6 min +- **Started:** 2026-06-19T11:32:50Z +- **Completed:** 2026-06-19T11:38:50Z +- **Tasks:** 2/2 +- **Files created:** 5 +- **Files modified:** 2 + +## Accomplishments + +- MODULE_REGISTRY in module-loader.ts maps module slugs to dynamically imported components via next/dynamic with ssr:false, ensuring inactive modules are never bundled (MOD-04) +- Category page at /modules/[category] fetches active modules from API, filters by category, and renders a responsive card grid (D-04) +- ModuleCard component displays icon, name, localized description, and link to expanded view with hover effects (D-05a) +- Expanded module view at /modules/[category]/[moduleSlug] loads the module component on demand via whitelist lookup (D-05b) +- API client utilities (getActiveModules, getModuleBySlug) provide typed fetch wrappers for module endpoints +- i18n keys added for "modules" namespace in both German and English + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Module Loader Utility + Category Page with Lazy Cards** - `a191628` (feat) +2. **Task 2: Expanded Module View with Dynamic Routing** - `de63b10` (feat) + +## Files Created/Modified + +- `apps/web/src/lib/module-loader.ts` - MODULE_REGISTRY with loadModuleComponent and loadModuleCard exports +- `apps/web/src/lib/api.ts` - getActiveModules and getModuleBySlug API client functions +- `apps/web/src/app/(portal)/modules/[category]/page.tsx` - Category page with module grid and empty state +- `apps/web/src/app/(portal)/modules/[category]/components/ModuleCard.tsx` - Module card with icon, description, and link +- `apps/web/src/app/(portal)/modules/[category]/[moduleSlug]/page.tsx` - Expanded module view with lazy loading and not-found state +- `apps/web/src/messages/de.json` - Added modules namespace (categoryTitle, openModule, noModules, notFound, etc.) +- `apps/web/src/messages/en.json` - Added modules namespace (categoryTitle, openModule, noModules, notFound, etc.) + +## Decisions Made + +- **Slug whitelist pattern:** MODULE_REGISTRY acts as an allowlist — only explicitly registered slugs can trigger dynamic imports. Arbitrary URL slugs get a not-found state, never an import attempt (T-03-09). +- **Client components throughout:** All three pages use 'use client' to support next/dynamic with ssr:false and client-side fetch with credentials:include for auth cookie forwarding. +- **Inline SVG icons:** ModuleCard uses inline SVGs rather than an icon library to avoid adding a dependency for a small number of icons. Icons are mapped by the module's icon property (e.g., 'Globe' for domaincheck). +- **Category filtering:** The category page fetches all active modules and filters client-side by category. This keeps the API simple (single /modules/active endpoint) while supporting category-based views. + +## Threat Mitigations Applied + +| Threat ID | Mitigation | Implementation | +|-----------|------------|----------------| +| T-03-09 | Slug whitelist via MODULE_REGISTRY | module-loader.ts: only registered slugs resolve to components; [moduleSlug]/page.tsx checks `slug in MODULE_REGISTRY` before loading | +| T-03-10 | API filters by tenant active modules | [category]/page.tsx: fetches from /modules/active which is tenant-scoped server-side | +| T-03-11 | Accepted | Module names/descriptions are non-sensitive catalog data | + +## Deviations from Plan + +None - plan executed exactly as written. + +## Verification Results + +- `pnpm --filter web build`: PASS +- Route `/modules/[category]` generated: confirmed in build output (2.01 kB) +- Route `/modules/[category]/[moduleSlug]` generated: confirmed in build output (1.95 kB) +- Domaincheck module code at `/modules/domaincheck` (1.47 kB) is separate from category page, confirming code-splitting works +- No stubs or placeholders found in created files + +## Self-Check: PASSED + +All 5 created files verified on disk. Both task commits (a191628, de63b10) verified in git log.