Files
tessera-ctl/.planning/phases/03-module-system-domaincheck/03-03-SUMMARY.md
T
schalli fbd7d44e88 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
2026-06-19 13:39:58 +02:00

5.6 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, key_decisions, patterns_established, requirements_completed, duration, completed
phase plan subsystem tags dependency_graph tech_stack key_files key_decisions patterns_established requirements_completed duration completed
03-module-system-domaincheck 03 module-ui-loader
lazy-loading
dynamic-imports
next-dynamic
category-page
module-cards
requires provides affects
module-registry-service
domaincheck-frontend-page
module-loader-registry
category-page
expanded-module-view
api-client-utils
sidebar-module-links
module-marketplace
added patterns
next-dynamic-ssr-false
slug-whitelist-registry
category-grid-layout
created modified
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
apps/web/src/messages/de.json
apps/web/src/messages/en.json
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
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
MOD-04
6min 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.