39948-vm/frontend/docs/frontend-architecture.md
2026-07-03 16:11:24 +02:00

1239 lines
63 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Frontend Architecture
## Overview
The frontend is a **Next.js 15** application with **React 19**, **TypeScript**, **Redux Toolkit**, and **Tailwind CSS**. It uses the Pages Router with client-side rendering (CSR), optimized for complex admin/builder interactions and PWA offline capabilities.
**Location:** `frontend/src/`
**Total Files:** 408 TypeScript/TSX files across 14 directories
---
## Tech Stack
| Technology | Version | Purpose |
|------------|---------|---------|
| Next.js | 15.3.1 | React framework with Pages Router |
| React | 19.0.0 | UI library |
| TypeScript | 5.x | Type safety |
| Redux Toolkit | 2.1.0 | State management |
| MUI X DataGrid | 7.0.0 | Data tables |
| Tailwind CSS | 3.4.1 | Utility-first styling |
| Formik | 2.4.x | Form management |
| Axios | 1.x | HTTP client |
| Serwist | 9.x | PWA service worker |
| Dexie.js | 4.x | IndexedDB wrapper |
---
## Architecture Diagram
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ ENTRY LAYER │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ pages/_app.tsx │ │
│ │ • Redux Provider (store.ts) │ │
│ │ • DownloadContext (PWA progress) │ │
│ │ • Axios interceptors (JWT refresh, error handling) │ │
│ │ • PWA service worker registration (Serwist) │ │
│ │ • ErrorBoundary wrapper │ │
│ │ • i18n initialization │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ LAYOUT LAYER │
│ ┌───────────────────────────────────┐ ┌─────────────────────────────────┐ │
│ │ Authenticated.tsx │ │ Guest.tsx │ │
│ │ • JWT validation (client-side) │ │ • Public routes only │ │
│ │ • Permission checking per route │ │ • Login, Register, Forgot │ │
│ │ • Project existence guard │ │ • Minimal layout │ │
│ │ • NavBar, AsideMenu, FooterBar │ │ │ │
│ │ • Tour guide (IntroGuide) │ │ │ │
│ └───────────────────────────────────┘ └─────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ PAGE LAYER (99 pages) │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Entity CRUD Pages (78 pages = 13 entities × 6 pages each) │ │
│ │ • [entity]-list.tsx → createListPage factory │ │
│ │ • [entity]-new.tsx → Formik forms with validation │ │
│ │ • [entity]-edit.tsx → useEditPageSync hook │ │
│ │ • [entity]-view.tsx → Read-only display │ │
│ │ • [entity]-table.tsx → Embedded table view │ │
│ │ • [entityId].tsx → Dynamic route handler │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Special Pages (21 pages) │ │
│ │ • constructor.tsx → Visual tour builder (47KB) │ │
│ │ • dashboard.tsx → Admin home with entity counts │ │
│ │ • p/[slug]/index.tsx → Public production presentation │ │
│ │ • p/[slug]/stage.tsx → Stage preview presentation │ │
│ │ • login.tsx, forgot.tsx, password-reset.tsx → Auth flows │ │
│ │ • profile.tsx → User profile management │ │
│ │ • element-type-defaults.tsx → Global element defaults │ │
│ │ • project-element-defaults.tsx → Project element overrides │ │
│ │ • search.tsx → Global search │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMPONENT LAYER (193 components) │
│ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────┐ │
│ │ Entity Components (78) │ │ Constructor (15) │ │ Runtime (5) │ │
│ │ • Table[Entity] │ │ • ElementEditorPanel │ │ • Runtime- │ │
│ │ • configure*Cols │ │ • CanvasElement │ │ Presentation│ │
│ │ • Card[Entity] │ │ • CanvasBackground │ │ • Runtime- │ │
│ │ • List[Entity] │ │ • ConstructorMenu │ │ Element │ │
│ │ • Form fields │ │ • PageSelector │ │ • UiElement- │ │
│ └────────────────────────┘ │ • TransitionPreview │ │ Renderer │ │
│ ┌────────────────────────┐ └────────────────────────┘ │ │ │
│ │ ElementSettings (23) │ ┌────────────────────────┐ └────────────────┘ │
│ │ • StyleSettings │ │ Offline/PWA (5) │ ┌────────────────┐ │
│ │ • NavigationSettings │ │ • DownloadProgress │ │ Generic (50+) │ │
│ │ • EffectsSettings │ │ • OfflineToggle │ │ • CardBox* │ │
│ │ • CarouselSettings │ │ • StorageUsage │ │ • FormField* │ │
│ │ • MediaSettings │ │ • OfflineStatus │ │ • NavBar* │ │
│ │ • TooltipSettings │ └────────────────────────┘ │ • AsideMenu* │ │
│ └────────────────────────┘ │ • BaseButton │ │
│ ┌────────────────────────┐ │ • Pagination │ │
│ │ Factories (2) │ │ • Search │ │
│ │ • createTableComp │ └────────────────┘ │
│ │ • configBuilderFact │ │
│ └────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ HOOKS LAYER (32 hooks) │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Runtime/Preloading (8 hooks) │ │
│ │ usePreloadOrchestrator ──→ usePageSwitch ──→ useBackgroundTransition │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ useNeighborGraph useTransitionPlayback ──→ useReversePlayback│ │
│ │ │ │ │
│ │ ▼ │ │
│ │ usePageDataLoader ──→ usePreloadProgress │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Constructor (7 hooks) │ │
│ │ useConstructorElements ──→ useConstructorPageActions │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ useCanvasElementDrag ──→ useCanvasElapsedTime │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ useTransitionPreview ──→ useIconPreload ──→ useMediaDurationProbe │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────┐ ┌────────────────────────┐ ┌────────────────┐ │
│ │ Offline/PWA (4) │ │ Forms/Tables (4) │ │ Utility (8) │ │
│ │ • useOfflineMode │ │ • useFormSync │ │ • useCSV- │ │
│ │ • usePWAPreload │ │ • useEntityTable │ │ Handling │ │
│ │ • useStorageQuota │ │ • useFilterItems │ │ • useElement- │ │
│ │ • useNetworkAware │ │ • useEditPageSync │ │ Effects │ │
│ └────────────────────────┘ └────────────────────────┘ │ • useOutside- │ │
│ │ Click │ │
│ │ • useDraggable│ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ STATE LAYER │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Redux Store (stores/store.ts) │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Entity Slices (13) - via createEntitySlice factory │ │ │
│ │ │ users, roles, permissions, projects, project_memberships, │ │ │
│ │ │ assets, asset_variants, presigned_url_requests, tour_pages, │ │ │
│ │ │ project_audio_tracks, publish_events, pwa_caches, access_logs │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Core Slices (3) │ │ │
│ │ │ • authSlice - JWT token, current user, permissions │ │ │
│ │ │ • mainSlice - UI state (sidebar, dark mode) │ │ │
│ │ │ • styleSlice - Theme configuration │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ React Context │ │
│ │ • DownloadContext - PWA download progress tracking │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ LIBRARY LAYER (20 files) │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ lib/offline/ - PWA Storage │ │
│ │ • StorageManager.ts - Cache API + IndexedDB dual storage │ │
│ │ • DownloadManager.ts - Asset download orchestration │ │
│ │ • DownloadEventBus.ts - Progress event system (Observer pattern) │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ lib/offlineDb/ - IndexedDB Persistence │ │
│ │ • OfflineDbManager.ts - Dexie.js wrapper for large assets │ │
│ │ • schema.ts - Database schema definition │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ lib/ - Utilities │ │
│ │ • assetUrl.ts - Presigned URL management, expiry handling │ │
│ │ • elementDefaults.ts - Default element configurations (17KB) │ │
│ │ • elementStyles.ts - Element CSS generation │ │
│ │ • elementEffects.ts - Element animation effects │ │
│ │ • constructorHelpers.ts - Canvas manipulation utilities │ │
│ │ • navigationHelpers.ts - Link resolution, transition detection │ │
│ │ • extractPageLinks.ts - Parse navigation links from ui_schema │ │
│ │ • imagePreDecode.ts - Image pre-decoding for smooth transitions │ │
│ │ • mediaDuration.ts - Audio/video duration extraction │ │
│ │ • mediaHelpers.ts - Media URL handling │ │
│ │ • parseJson.ts - Safe JSON parsing with fallbacks │ │
│ │ • logger.ts - Structured logging (dev/prod modes) │ │
│ │ • slugHelpers.ts - URL slug generation │ │
│ │ • tourFlowHelpers.ts - Page ordering utilities │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ SUPPORT LAYER │
│ ┌────────────────────┐ ┌────────────────────┐ ┌────────────────────────┐ │
│ │ factories/ (3) │ │ schemas/ (6) │ │ types/ (12) │ │
│ │ • createListPage │ │ • userSchema │ │ • constructor.ts │ │
│ │ • createFormPage │ │ • assetSchema │ │ • entities.ts │ │
│ │ • index │ │ • projectSchema │ │ • runtime.ts │ │
│ └────────────────────┘ │ • tourPageSchema │ │ • preload.ts │ │
│ ┌────────────────────┐ │ • roleSchema │ │ • presentation.ts │ │
│ │ helpers/ (6) │ └────────────────────┘ │ • offline.ts │ │
│ │ • dataFormatter │ ┌────────────────────┐ │ • permissions.ts │ │
│ │ • textFormatters │ │ config/ (2) │ │ • forms.ts │ │
│ │ • userPermissions │ │ • preload.config │ │ • filters.ts │ │
│ │ • notifyState │ │ • offline.config │ │ • api.ts │ │
│ │ • humanize │ └────────────────────┘ │ • redux.ts │ │
│ │ • fileSaver │ └────────────────────────┘ │
│ └────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Directory Structure
```
frontend/src/
├── pages/ # 99 files - Next.js pages
│ ├── _app.tsx # App entry point
│ ├── api/ # API routes (logError.ts, hello.js)
│ ├── p/[projectSlug]/ # Public presentation routes
│ │ ├── index.tsx # Production presentation
│ │ └── stage.tsx # Stage preview
│ ├── constructor.tsx # Visual tour builder
│ ├── dashboard.tsx # Admin home
│ ├── [entity]/ # 13 entity directories
│ │ ├── [entityId].tsx # Dynamic route
│ │ ├── [entity]-list.tsx # List page
│ │ ├── [entity]-new.tsx # Create page
│ │ ├── [entity]-edit.tsx # Edit page
│ │ ├── [entity]-view.tsx # View page
│ │ └── [entity]-table.tsx# Embedded table
│ └── ... # Auth, profile, search, etc.
├── components/ # 193 files - React components
│ ├── [Entity]/ # 13 entity component directories
│ │ ├── Table[Entity].tsx # DataGrid wrapper
│ │ ├── configure[Entity]Cols.tsx # Column config
│ │ ├── Card[Entity].tsx # Card view
│ │ └── List[Entity].tsx # List view
│ ├── Constructor/ # 15 files - Tour builder components
│ ├── ElementSettings/ # 23 files - Element configuration
│ ├── UiElements/ # 16 files - Unified element rendering (shared by Constructor + Runtime)
│ ├── Offline/ # 5 files - PWA components
│ ├── Generic/ # 3 files - Reusable table/form
│ ├── DataGrid/ # 1 file - Column config factory
│ ├── Factory/ # 1 file - Table component factory
│ ├── Uploaders/ # 1 file - Upload service
│ └── *.tsx # ~50 standalone components
├── hooks/ # 32 files - Custom React hooks
│ ├── index.ts # Re-exports
│ ├── usePreloadOrchestrator.ts # Asset preloading (26KB)
│ ├── useTransitionPlayback.ts # Video transitions (23KB)
│ ├── usePageSwitch.ts # Page navigation (14KB)
│ ├── useConstructorElements.ts # Element CRUD (12KB)
│ └── ... # 26 more hooks
├── stores/ # Redux state
│ ├── store.ts # Store configuration
│ ├── hooks.ts # useAppSelector, useAppDispatch
│ ├── createEntitySlice.ts # Entity slice factory (10KB)
│ ├── authSlice.ts # Authentication state
│ ├── mainSlice.ts # UI state
│ ├── styleSlice.ts # Theme state
│ └── [entity]/ # 13 entity slice directories
│ └── [entity]Slice.ts # Generated via factory
├── lib/ # 20 files - Utility libraries
│ ├── offline/ # PWA storage layer
│ │ ├── StorageManager.ts # Cache API + IndexedDB
│ │ ├── DownloadManager.ts# Download orchestration
│ │ └── DownloadEventBus.ts # Progress events
│ ├── offlineDb/ # IndexedDB layer
│ │ ├── OfflineDbManager.ts # Dexie wrapper
│ │ └── schema.ts # DB schema
│ ├── assetUrl.ts # Presigned URL management
│ ├── elementDefaults.ts # Default element configs
│ ├── elementStyles.ts # CSS generation
│ ├── elementEffects.ts # Animation effects
│ ├── constructorHelpers.ts # Canvas utilities
│ ├── navigationHelpers.ts # Link resolution
│ ├── extractPageLinks.ts # Parse links from schema
│ ├── imagePreDecode.ts # Image pre-decoding
│ ├── mediaDuration.ts # Media duration extraction
│ ├── mediaHelpers.ts # Media URL handling
│ ├── parseJson.ts # Safe JSON parsing
│ ├── logger.ts # Structured logging
│ ├── slugHelpers.ts # Slug generation
│ └── tourFlowHelpers.ts # Page ordering
├── types/ # 12 files - TypeScript definitions
│ ├── constructor.ts # Canvas/element types
│ ├── entities.ts # Entity interfaces
│ ├── runtime.ts # Presentation runtime
│ ├── preload.ts # Preloading state
│ ├── presentation.ts # Navigation/transitions
│ ├── offline.ts # PWA/cache types
│ ├── permissions.ts # RBAC types
│ ├── forms.ts # Form field types
│ ├── filters.ts # Data filtering
│ ├── api.ts # API response types
│ ├── redux.ts # Store types
│ └── index.ts # Re-exports
├── factories/ # 3 files - Page generators
│ ├── createListPage.tsx # List page factory
│ ├── createFormPage.tsx # Form page factory
│ └── index.ts # Re-exports
├── schemas/ # 6 files - Validation schemas
│ ├── userSchema.ts # User validation
│ ├── assetSchema.ts # Asset validation
│ ├── projectSchema.ts # Project validation
│ ├── tourPageSchema.ts # Tour page validation
│ ├── roleSchema.ts # Role validation
│ └── index.ts # Re-exports
├── helpers/ # 6 files - Utility functions
│ ├── dataFormatter.js # Data formatting
│ ├── textFormatters.ts # Text formatting
│ ├── userPermissions.ts # Permission checking
│ ├── notifyStateHandler.ts # Toast notifications
│ ├── humanize.ts # Human-readable strings
│ └── fileSaver.ts # File download
├── layouts/ # 2 files - Page layouts
│ ├── Authenticated.tsx # Auth layout with nav
│ └── Guest.tsx # Public layout
├── config/ # 2 files - Configuration
│ ├── preload.config.ts # Preload settings
│ └── offline.config.ts # PWA settings
├── context/ # 1 file - React Context
│ └── DownloadContext.tsx # PWA download progress
├── interfaces/ # 1 file - Shared interfaces
│ └── index.ts # Common UI interfaces
└── css/ # Stylesheets
└── _theme.css # Tailwind theme customization
```
---
## Module Details
### 1. Pages Module (99 files)
#### Entity CRUD Pages (78 files)
Each of the 13 entities has 6 standardized pages:
| Page Pattern | Purpose | Factory/Hook |
|--------------|---------|--------------|
| `[entity]-list.tsx` | List with filters, pagination, CSV export | `createListPage` factory |
| `[entity]-new.tsx` | Create form with validation | Formik + entity schema |
| `[entity]-edit.tsx` | Edit form with data sync | `useEditPageSync` hook |
| `[entity]-view.tsx` | Read-only display | Direct entity fetch |
| `[entity]-table.tsx` | Embedded table view | `createTableComponent` |
| `[entityId].tsx` | Dynamic route redirect | Redirects to -view |
**Entities:** users, roles, permissions, projects, project_memberships, assets, asset_variants, presigned_url_requests, tour_pages, project_audio_tracks, publish_events, pwa_caches, access_logs
#### Special Pages (21 files)
| Page | Size | Description |
|------|------|-------------|
| `index.tsx` | 1KB | Root redirect to login or projects list |
| `constructor.tsx` | 47KB | Visual tour builder with canvas, drag-drop, element editing |
| `dashboard.tsx` | 6KB | Admin home with entity counts, quick actions |
| `p/[projectSlug]/index.tsx` | 2KB | Public production presentation (wraps RuntimePresentation) |
| `p/[projectSlug]/stage.tsx` | 2KB | Stage preview presentation |
| `_app.tsx` | 11KB | App entry, providers, interceptors |
| `login.tsx` | 8KB | Authentication with OAuth options |
| `forgot.tsx` | 3KB | Password reset request |
| `profile.tsx` | 6KB | User profile management |
| `element-type-defaults.tsx` | 4KB | Global element defaults editor |
| `project-element-defaults.tsx` | 6KB | Project-level element overrides |
| `search.tsx` | 3KB | Global search results |
---
### 2. Components Module (193 files)
#### Entity Components (78 files, 13 directories)
Each entity directory contains:
| File Pattern | Purpose |
|--------------|---------|
| `Table[Entity].tsx` | MUI DataGrid wrapper with actions |
| `configure[Entity]Cols.tsx` | Column definitions using configBuilderFactory |
| `Card[Entity].tsx` | Card view for single entity |
| `List[Entity].tsx` | List view variant |
| `useAssetUploader.ts` | (Assets only) Upload hook |
| `ProjectSelector.tsx` | (Assets only) Project filter |
#### Constructor Components (15 files)
| Component | Size | Description |
|-----------|------|-------------|
| `ElementEditorPanel.tsx` | 22KB | Right panel for element editing |
| `ConstructorToolbar.tsx` | ~19KB | Floating main toolbar with mode, Page actions, Elements actions, save/stage/exit controls |
| `ConstructorMenu.tsx` | 5KB | Legacy/collapsible constructor menu component |
| `ConstructorControlsPanel.tsx` | 2KB | Legacy/secondary constructor controls |
| `CanvasElement.tsx` | 3KB | Draggable element on canvas |
| `CanvasBackground.tsx` | 3KB | Background image/video layer |
| `CreateTransitionForm.tsx` | 3KB | Transition configuration modal |
| `TransitionPreviewOverlay.tsx` | 1KB | Transition video preview |
| `PageSelector.tsx` | 1KB | Page dropdown selector |
| `InteractionModeToggle.tsx` | 2KB | Edit/interact mode toggle |
| `AssetSelectCompact.tsx` | 2KB | Compact asset picker |
| `BackgroundSettingsEditor.tsx` | 2KB | Background configuration |
| `ElementEditorHeader.tsx` | 1KB | Editor panel header |
| `MenuActionButton.tsx` | 1KB | Menu button component |
| `types.ts` | 7KB | Constructor type definitions |
| `index.ts` | 0.3KB | Re-exports |
#### ElementSettings Components (23 files)
Settings panels for different element aspects:
| Component | Description |
|-----------|-------------|
| `StyleSettingsSection.tsx` | Position, size, colors, borders |
| `StyleSettingsSectionCompact.tsx` | Compact version for constructor |
| `NavigationSettingsSection.tsx` | Link targets, transitions |
| `NavigationSettingsSectionCompact.tsx` | Compact version |
| `EffectsSettingsSection.tsx` | Animations, hover effects |
| `EffectsSettingsSectionCompact.tsx` | Compact version |
| `CarouselSettingsSection.tsx` | Gallery/carousel configuration |
| `GallerySettingsSection.tsx` | Image gallery settings |
| `MediaSettingsSection.tsx` | Video/audio player settings |
| `MediaSettingsSectionCompact.tsx` | Compact version |
| `TooltipSettingsSection.tsx` | Tooltip configuration |
| `TooltipSettingsSectionCompact.tsx` | Compact version |
| `DescriptionSettingsSection.tsx` | Text content settings |
| `DescriptionSettingsSectionCompact.tsx` | Compact version |
| `CommonSettingsSection.tsx` | Shared element settings |
| `CommonSettingsSectionCompact.tsx` | Compact version |
| `GallerySettingsSection.tsx` | Gallery settings |
| `GallerySettingsSectionCompact.tsx` | Compact version |
| `GalleryCarouselSettingsSectionCompact.tsx` | Gallery carousel settings |
| `ElementSettingsTabs.tsx` | Tab navigation |
| `useElementSettingsForm.ts` | Form state management hook |
| `types.ts` | Type definitions |
| `index.ts` | Re-exports |
#### UiElements Components (16 files)
Unified element rendering shared by both Constructor (CanvasElement) and Runtime (RuntimeElement):
| Component | Description |
|-----------|-------------|
| `UiElementRenderer.tsx` | Main entry point - delegates to per-type components |
| `shared/useElementWrapperStyle.ts` | Shared hook for consistent wrapper styling |
| `elements/NavigationElement.tsx` | Navigation button (next/prev) |
| `elements/GalleryElement.tsx` | Image gallery grid |
| `elements/TooltipElement.tsx` | Tooltip with icon |
| `elements/DescriptionElement.tsx` | Title + text block |
| `elements/CarouselElement.tsx` | Image carousel with navigation |
| `elements/LogoElement.tsx` | Logo display |
| `elements/SpotElement.tsx` | Hotspot/clickable area |
| `elements/VideoPlayerElement.tsx` | Embedded video player |
| `elements/AudioPlayerElement.tsx` | Embedded audio player |
| `elements/PopupElement.tsx` | Popup/modal dialog |
| `GalleryCarouselOverlay.tsx` | Gallery/carousel fullscreen overlay |
| `ElementPreview.tsx` | Preview element in constructor |
| `defaults.ts` | Default element configurations |
| `types.ts` | Element type definitions |
#### Offline Components (5 files)
| Component | Description |
|-----------|-------------|
| `DownloadProgressPanel.tsx` | Shows download progress for PWA |
| `OfflineToggle.tsx` | Enable/disable offline mode |
| `StorageUsageDisplay.tsx` | Shows storage quota usage |
| `OfflineStatusIndicator.tsx` | Online/offline status badge |
| `index.ts` | Re-exports |
#### Factory Components (2 files)
| Component | Description |
|-----------|-------------|
| `createTableComponent.tsx` | Factory that generates table wrapper components |
| `configBuilderFactory.tsx` | Factory that generates DataGrid column configurations |
#### Generic Components (3 files)
| Component | Size | Description |
|-----------|------|-------------|
| `GenericTable.tsx` | 14KB | Reusable DataGrid with filters, pagination |
| `GenericFormField.tsx` | 6KB | Reusable form field component |
| `index.ts` | 0.2KB | Re-exports |
#### Standalone Components (~50 files)
| Category | Components |
|----------|------------|
| **Layout** | `NavBar`, `NavBarItem`, `FooterBar`, `AsideMenu`, `AsideMenuItem`, `AsideMenuLayer`, `AsideMenuList` |
| **Cards** | `CardBox`, `CardBoxModal`, `CardBoxComponentBody`, `CardBoxComponentTitle`, `CardBoxComponentFooter`, `CardBoxComponentEmpty` |
| **Forms** | `FormField`, `FormFieldCompact`, `FormFilePicker`, `FormImagePicker`, `FormCheckRadio`, `FormCheckRadioGroup` |
| **Buttons** | `BaseButton`, `BaseButtons`, `BaseIcon`, `BaseDivider` |
| **Data** | `DataGridMultiSelect`, `SelectField`, `SelectFieldMany`, `SwitchField`, `RichTextField`, `ImageField` |
| **Sections** | `SectionMain`, `SectionFullScreen`, `SectionTitle`, `SectionTitleLineWithButton` |
| **User** | `UserAvatar`, `UserAvatarCurrentUser`, `UserCard`, `IconRounded` |
| **Utils** | `Pagination`, `Search`, `SearchResults`, `LoadingSpinner`, `NotificationBar`, `OverlayLayer`, `ClickOutside` |
| **Auth** | `PasswordSetOrReset` |
| **Features** | `TourFlowManager`, `RuntimePresentation`, `RuntimeElement`, `DevModeBadge`, `IntroGuide`, `LanguageSwitcher`, `ErrorBoundary`, `DragDropFilePicker`, `ListActionsPopover` |
---
### 3. Hooks Module (32 files)
#### Runtime/Preloading Hooks (8 hooks)
| Hook | Size | Description | Used By |
|------|------|-------------|---------|
| `usePreloadOrchestrator` | 26KB | Coordinates asset preloading with priority queue, blob URL caching | RuntimePresentation |
| `useTransitionPlayback` | 23KB | Manages video transition playback (forward/reverse) | RuntimePresentation |
| `usePageSwitch` | 14KB | Handles page navigation with preloaded assets | RuntimePresentation |
| `useReversePlayback` | 11KB | Reverse video playback for back navigation | useTransitionPlayback |
| `useNeighborGraph` | 8KB | Builds navigation graph for preload prioritization | usePreloadOrchestrator |
| `usePageDataLoader` | 7KB | Loads project and pages data with caching | RuntimePresentation |
| `usePreloadProgress` | 6KB | Tracks preload progress percentages | RuntimePresentation |
| `useBackgroundTransition` | 5KB | Fade-out animation during page transitions | RuntimePresentation |
#### Constructor Hooks (7 hooks)
| Hook | Size | Description | Used By |
|------|------|-------------|---------|
| `useConstructorElements` | 12KB | Element CRUD operations on canvas | constructor.tsx |
| `useConstructorPageActions` | 10KB | Page management (create, delete, reorder) | constructor.tsx |
| `useMediaDurationProbe` | 6KB | Extracts duration from video/audio files | constructor.tsx |
| `useTransitionPreview` | 6KB | Previews transition videos | constructor.tsx |
| `useDraggable` | 6KB | Generic drag functionality | CanvasElement |
| `useIconPreload` | 5KB | Preloads icon assets | constructor.tsx |
| `useCanvasElementDrag` | 4KB | Canvas-specific element dragging | CanvasElement |
| `useCanvasElapsedTime` | 3KB | Animation timing for canvas | constructor.tsx |
#### Offline/PWA Hooks (4 hooks)
| Hook | Size | Description | Used By |
|------|------|-------------|---------|
| `useOfflineMode` | 11KB | Detects and manages offline state | _app.tsx, Offline components |
| `usePWAPreload` | 5KB | Preloads assets for PWA mode | RuntimePresentation |
| `useNetworkAware` | 5KB | Network quality detection | usePreloadOrchestrator |
| `useStorageQuota` | 3KB | Monitors storage usage | StorageUsageDisplay |
#### Form/Table Hooks (4 hooks)
| Hook | Size | Description | Used By |
|------|------|-------------|---------|
| `useEntityTable` | 8KB | Table state management (pagination, sorting, filters) | All entity tables |
| `useEditPageSync` | 5KB | Syncs form data with Redux on edit pages | All -edit pages |
| `useFilterItems` | 5KB | Filter state management | createListPage |
| `useFormSync` | 3KB | Form state synchronization | Form components |
#### Utility Hooks (8 hooks)
| Hook | Size | Description | Used By |
|------|------|-------------|---------|
| `useDashboardCounts` | 8KB | Fetches entity counts for dashboard | dashboard.tsx |
| `useElementEffects` | 3KB | Applies element animation effects | RuntimeElement |
| `useCSVHandling` | 4KB | CSV import/export | List pages |
| `useOutsideClick` | 3KB | Detects clicks outside element | Modals, dropdowns |
| `usePageNavigation` | 5KB | Page navigation state | RuntimePresentation |
| `useProjectAssets` | 3KB | Fetches and manages project assets | Projects pages |
| `useDevCompilationStatus` | 1KB | Hot reload status indicator | DevModeBadge |
---
### 4. Stores Module (22 files)
#### Store Configuration
| File | Description |
|------|-------------|
| `store.ts` | Redux store configuration with all slices |
| `hooks.ts` | Typed hooks: `useAppSelector`, `useAppDispatch` |
| `createEntitySlice.ts` | Factory for generating entity slices |
| `introSteps.ts` | Tour guide step definitions |
#### Core Slices
| Slice | Size | State |
|-------|------|-------|
| `authSlice.ts` | 4KB | `{ token, currentUser, permissions, loading, error }` |
| `mainSlice.ts` | 1KB | `{ isAsideMobileExpanded, isAsideLgActive, darkMode }` |
| `styleSlice.ts` | 3KB | `{ asideStyle, navBarStyle, footerStyle, ... }` |
| `usersSlice.ts` | 3KB | Extended user slice with password reset actions |
#### Entity Slices (13)
Generated via `createEntitySlice` factory, each provides:
```typescript
interface EntityState<T> {
rows: T[];
row: T | null;
loading: boolean;
error: string | null;
count: number;
filters: FilterState;
}
// Async thunks
fetch({ query: string }) // GET /api/[entity]?...
findById(id: string) // GET /api/[entity]/:id
create(data: T) // POST /api/[entity]
update({ id, data }) // PUT /api/[entity]/:id
deleteItem(id: string) // DELETE /api/[entity]/:id
```
---
### 5. Library Module (20 files)
#### Offline Storage (lib/offline/)
| File | Size | Description |
|------|------|-------------|
| `StorageManager.ts` | 7KB | Dual storage abstraction (Cache API < 5MB, IndexedDB >= 5MB) |
| `DownloadManager.ts` | 12KB | Download orchestration with retry, progress |
| `DownloadEventBus.ts` | 5KB | Event system for download progress (Observer pattern) |
#### IndexedDB (lib/offlineDb/)
| File | Size | Description |
|------|------|-------------|
| `OfflineDbManager.ts` | 8KB | Dexie.js wrapper for large asset storage |
| `schema.ts` | 1KB | IndexedDB schema: `assets { url, blob, size, timestamp }` |
#### Element Utilities
| File | Size | Description |
|------|------|-------------|
| `elementDefaults.ts` | 18KB | Default configurations for all 11 element types |
| `elementStyles.ts` | 3KB | Generates inline CSS from element config |
| `elementEffects.ts` | 6KB | Animation effect definitions and application |
| `fonts.ts` | 2KB | Font family definitions and mappings |
#### Asset Utilities
| File | Size | Description |
|------|------|-------------|
| `assetUrl.ts` | 10KB | Presigned URL management, expiry tracking, refresh |
| `imagePreDecode.ts` | 6KB | Pre-decodes images for smooth transitions |
| `mediaDuration.ts` | 3KB | Extracts duration from video/audio elements |
| `mediaHelpers.ts` | 4KB | Media URL normalization, type detection |
#### Navigation Utilities
| File | Size | Description |
|------|------|-------------|
| `navigationHelpers.ts` | 4KB | `resolveNavigationTarget()`, `isTransitionBlocking()` |
| `extractPageLinks.ts` | 6KB | Parses navigation links from `ui_schema_json` |
#### Constructor Utilities
| File | Size | Description |
|------|------|-------------|
| `constructorHelpers.ts` | 8KB | Canvas manipulation, element positioning |
| `slugHelpers.ts` | 2KB | URL slug generation for pages |
| `tourFlowHelpers.ts` | 3KB | Page ordering, drag-drop reordering |
#### General Utilities
| File | Size | Description |
|------|------|-------------|
| `parseJson.ts` | 3KB | Safe JSON parsing with fallbacks |
| `logger.ts` | 4KB | Structured logging (console in dev, silent in prod) |
---
### 6. Types Module (12 files)
| File | Size | Description |
|------|------|-------------|
| `constructor.ts` | 10KB | Canvas element, page, selection types |
| `entities.ts` | 7KB | All 13 entity interfaces |
| `runtime.ts` | 2KB | Runtime presentation state types |
| `preload.ts` | 1KB | Preload queue, progress types |
| `presentation.ts` | 2KB | Navigation, transition types |
| `offline.ts` | 4KB | PWA cache, download types |
| `permissions.ts` | 4KB | RBAC permission types |
| `forms.ts` | 2KB | Form field, validation types |
| `filters.ts` | 2KB | Data filter types |
| `api.ts` | 1KB | API response types |
| `redux.ts` | 1KB | Store, dispatch types |
| `index.ts` | 0.3KB | Re-exports |
---
### 7. Factories Module (3 files)
| Factory | Size | Description |
|---------|------|-------------|
| `createListPage.tsx` | 6KB | Generates list pages with filters, CSV, pagination |
| `createFormPage.tsx` | 9KB | Generates form pages with validation |
| `index.ts` | 0.1KB | Re-exports |
**createListPage Usage:**
```typescript
export default createListPage({
entityName: 'assets',
entityNamePlural: 'Assets',
permissionPrefix: 'ASSETS',
columns: configureAssetsCols,
filterFields: ['name', 'type', 'status'],
});
```
**createFormPage Usage:**
```typescript
export default createFormPage({
entityName: 'assets',
schema: assetSchema,
fields: ['name', 'file', 'project'],
});
```
---
### 8. Schemas Module (6 files)
Formik/Yup validation schemas:
| Schema | Fields Validated |
|--------|-----------------|
| `userSchema.ts` | email, firstName, lastName, password, role |
| `assetSchema.ts` | name, file, type, project |
| `projectSchema.ts` | name, slug, description |
| `tourPageSchema.ts` | name, slug, order, background |
| `roleSchema.ts` | name, permissions |
| `index.ts` | Re-exports |
---
### 9. Helpers Module (6 files)
| Helper | Description |
|--------|-------------|
| `dataFormatter.js` | Format dates, numbers, booleans for display |
| `textFormatters.ts` | Truncate, capitalize, slugify text |
| `userPermissions.ts` | `hasPermission()`, `canAccess()` utilities |
| `notifyStateHandler.ts` | Toast notification triggers |
| `humanize.ts` | Convert snake_case to readable text |
| `fileSaver.ts` | Trigger file downloads |
---
### 10. Layouts Module (2 files)
| Layout | Description |
|--------|-------------|
| `Authenticated.tsx` | Full layout with JWT validation, permission checks, NavBar, AsideMenu, FooterBar, IntroGuide |
| `Guest.tsx` | Minimal layout for public pages (login, password reset, public presentations) |
**Per-Page Layout Pattern:**
```typescript
UsersListPage.getLayout = (page: ReactElement) => (
<LayoutAuthenticated permission="READ_USERS">
{page}
</LayoutAuthenticated>
);
```
---
### 11. Config Module (2 files)
| Config | Settings |
|--------|----------|
| `preload.config.ts` | Priority weights, batch sizes, concurrent limits |
| `offline.config.ts` | Cache names, storage thresholds, expiry times |
**Preload Priority Weights:**
```typescript
export const PRELOAD_PRIORITY = {
TRANSITION_VIDEO: 150, // Highest - needed for navigation
IMAGE: 100,
AUDIO: 50,
VIDEO: 30, // Lowest - large files
};
```
---
### 12. Context Module (1 file)
| Context | Description |
|---------|-------------|
| `DownloadContext.tsx` | PWA download progress tracking, shared across components |
**Usage:**
```typescript
const { progress, isDownloading, startDownload, cancelDownload } = useDownloadContext();
```
---
## Design Patterns
### 1. Factory Pattern
**Eliminates ~80% boilerplate code:**
| Factory | Generates | Files Eliminated |
|---------|-----------|-----------------|
| `createEntitySlice` | Redux slices with CRUD thunks | 13 × 200 LOC = 2600 LOC |
| `createListPage` | List pages with filters, pagination | 13 × 150 LOC = 1950 LOC |
| `createFormPage` | Form pages with validation | 13 × 180 LOC = 2340 LOC |
| `createTableComponent` | Table wrapper components | 13 × 50 LOC = 650 LOC |
| `configBuilderFactory` | Column configurations | 13 × 80 LOC = 1040 LOC |
**Total: ~8500 LOC eliminated through factories**
### 2. Custom Hook Pattern
Hooks are composed hierarchically:
```
usePreloadOrchestrator
├── useNeighborGraph (build priority queue)
├── useNetworkAware (adjust batch size)
└── Internal: presigned URL fetching, blob caching
usePageSwitch
├── usePreloadOrchestrator.getReadyBlobUrl()
└── useBackgroundTransition (fade animation)
useTransitionPlayback
├── useReversePlayback (back navigation)
└── Internal: video element management
```
### 3. HOC via getLayout
Per-page layout injection without wrapper components:
```typescript
// Page defines its layout
MyPage.getLayout = (page) => (
<LayoutAuthenticated permission="READ_ASSETS">
{page}
</LayoutAuthenticated>
);
// _app.tsx applies it
const getLayout = Component.getLayout ?? ((page) => page);
return getLayout(<Component {...pageProps} />);
```
### 4. Repository Pattern
Entity slices abstract all API calls:
```typescript
// Component only knows about Redux actions
dispatch(fetch({ query: '?limit=100' }));
dispatch(create(newAsset));
dispatch(update({ id, data: changes }));
dispatch(deleteItem(id));
// Slice handles API communication
const fetch = createAsyncThunk('assets/fetch', async ({ query }) => {
const response = await axios.get(`/api/assets${query}`);
return response.data;
});
```
### 5. Observer Pattern
Download progress events:
```typescript
// DownloadEventBus.ts
class DownloadEventBus {
private listeners: Map<string, Set<Callback>>;
emit(event: 'progress' | 'complete' | 'error', data: any) {
this.listeners.get(event)?.forEach(cb => cb(data));
}
on(event: string, callback: Callback) {
this.listeners.get(event)?.add(callback);
}
}
// Consumer
downloadEventBus.on('progress', ({ url, percent }) => {
setProgress(prev => ({ ...prev, [url]: percent }));
});
```
### 6. Strategy Pattern
Storage selection based on file size:
```typescript
// StorageManager.ts
class StorageManager {
async store(url: string, blob: Blob) {
if (blob.size < 5 * 1024 * 1024) { // < 5MB
return this.cacheApiStore(url, blob); // Fast, limited
} else {
return this.indexedDbStore(url, blob); // Slow, unlimited
}
}
}
```
---
## Key Flows
### 1. Runtime Presentation Flow
```
User navigates to /p/[slug]
┌─────────────────────────────────────┐
│ usePageDataLoader │
│ • Fetch project by slug │
│ • Fetch pages for environment │
│ • Determine initial page │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ usePreloadOrchestrator │
│ • Build neighbor graph │
│ • Calculate priorities │
│ • Request presigned URLs │
│ • Download assets to cache │
│ • Pre-decode images │
│ • Store blob URLs │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ RuntimePresentation renders │
│ • Background (image/video) │
│ • RuntimeElement for each element │
│ └── UiElementRenderer │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ User clicks navigation element │
│ • resolveNavigationTarget() │
│ • isTransitionBlocking() │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ usePageSwitch │
│ • Get blob URL from cache (O(1)) │
│ • useBackgroundTransition() │
│ • If transition video: │
│ useTransitionPlayback() │
│ • Switch to new page │
└─────────────────────────────────────┘
```
### 2. Constructor Flow
```
User opens /constructor?projectId=X
┌─────────────────────────────────────┐
│ Fetch project and pages │
│ • Redux: projects.findById() │
│ • Redux: tour_pages.fetch() │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ useConstructorElements │
│ • Parse ui_schema_json │
│ • Initialize element state │
│ • Provide CRUD methods │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Canvas renders │
│ • CanvasBackground │
│ • CanvasElement for each element │
│ • useCanvasElementDrag │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ User edits element │
│ • ElementEditorPanel opens │
│ • useElementSettingsForm │
│ • Settings sections render │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Save changes │
│ • Update ui_schema_json │
│ • Redux: tour_pages.update() │
│ • API: PUT /api/tour_pages/:id │
└─────────────────────────────────────┘
```
### 3. Offline/PWA Flow
```
User enables offline mode
┌─────────────────────────────────────┐
│ DownloadManager.startDownload() │
│ • Get asset manifest │
│ • Request presigned URLs │
│ • Queue downloads │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ For each asset: │
│ • Fetch with progress tracking │
│ • DownloadEventBus.emit() │
│ • StorageManager.store() │
│ - Cache API (< 5MB) │
│ - IndexedDB (>= 5MB) │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Service worker intercepts │
│ • Check Cache API │
│ • Check IndexedDB │
│ • Return cached response │
└─────────────────────────────────────┘
```
---
## Entity Architecture
Each entity follows a consistent structure:
```
[Entity] (e.g., Assets)
├── pages/assets/
│ ├── [assetsId].tsx # Dynamic route → view
│ ├── assets-list.tsx # List with filters
│ ├── assets-new.tsx # Create form
│ ├── assets-edit.tsx # Edit form
│ ├── assets-view.tsx # Read-only view
│ └── assets-table.tsx # Embedded table
├── components/Assets/
│ ├── TableAssets.tsx # DataGrid wrapper
│ ├── configureAssetsCols.tsx # Column definitions
│ ├── CardAssets.tsx # Card view
│ ├── ListAssets.tsx # List view
│ ├── useAssetUploader.ts # Upload hook (assets only)
│ └── ProjectSelector.tsx # Filter component
├── stores/assets/
│ └── assetsSlice.ts # Redux slice via factory
└── schemas/
└── assetSchema.ts # Validation schema
```
**13 Entities:**
- users, roles, permissions
- projects, project_memberships
- assets, asset_variants, presigned_url_requests
- tour_pages, project_audio_tracks
- publish_events, pwa_caches, access_logs
---
## Configuration
### Next.js Config (`next.config.mjs`)
```javascript
const nextConfig = {
reactStrictMode: true,
typescript: {
ignoreBuildErrors: false, // Enforce type checking
},
eslint: {
ignoreDuringBuilds: false, // Enforce linting
},
images: {
domains: ['cdn.platform.com', 's3.amazonaws.com'],
},
// PWA via Serwist
// Turbopack enabled for dev
};
```
### Tailwind Config
Theme customization in `css/_theme.css`:
```css
/* Sidebar styling */
#asideMenu {
@apply bg-gray-900 text-white;
}
/* Theme variants */
.theme-pink .card {
@apply border-pink-500;
}
```
---
## Performance Optimizations
### 1. Asset Preloading
- **Priority Queue:** Transition videos > Images > Audio > Video
- **Batch Fetching:** Max 50 presigned URLs per request
- **Network Awareness:** Reduce batch size on slow connections
- **Blob URL Caching:** O(1) lookup during navigation
### 2. Image Pre-Decoding
```typescript
// imagePreDecode.ts
export async function preDecodeImage(blobUrl: string): Promise<void> {
const img = new Image();
img.src = blobUrl;
await img.decode(); // Decode before display
}
```
### 3. Lazy Loading
- Route-based code splitting (Next.js automatic)
- Dynamic imports for large components
### 4. Redux Optimization
- Normalized entity state
- Selective re-renders via `useAppSelector`
- Memoized selectors where needed
---
## Related Documentation
| Document | Description |
|----------|-------------|
| [hooks-reference.md](./hooks-reference.md) | Detailed hook documentation |
| [runtime-presentation.md](./runtime-presentation.md) | Runtime presentation architecture |
| [constructor-page-editor.md](./constructor-page-editor.md) | Constructor page documentation |
| [assets-preloading.md](../../documentation/assets-preloading.md) | Asset preloading strategy |
| [offline-pwa-mode.md](../../documentation/offline-pwa-mode.md) | PWA offline capabilities |
| [ui-elements.md](../../documentation/ui-elements.md) | UI element types and configuration |
---
## File Reference by Size
### Largest Files (> 10KB)
| File | Size | Description |
|------|------|-------------|
| `pages/constructor.tsx` | 47KB | Visual tour builder |
| `hooks/usePreloadOrchestrator.ts` | 26KB | Asset preloading |
| `hooks/useTransitionPlayback.ts` | 23KB | Video transitions |
| `components/Constructor/ElementEditorPanel.tsx` | 22KB | Element editor |
| `components/RuntimePresentation.tsx` | 19KB | Tour playback |
| `lib/elementDefaults.ts` | 18KB | Element defaults |
| `components/TourFlowManager.tsx` | 17KB | Page management |
| `hooks/usePageSwitch.ts` | 14KB | Page navigation |
| `components/Generic/GenericTable.tsx` | 14KB | Reusable table |
| `hooks/useConstructorElements.ts` | 12KB | Element CRUD |
| `lib/offline/DownloadManager.ts` | 12KB | Download management |
| `hooks/useReversePlayback.ts` | 11KB | Reverse video |
| `hooks/useOfflineMode.ts` | 11KB | Offline detection |
| `pages/_app.tsx` | 11KB | App entry |
| `lib/assetUrl.ts` | 10KB | Presigned URLs |
| `stores/createEntitySlice.ts` | 10KB | Slice factory |
| `types/constructor.ts` | 10KB | Constructor types |
---
## Summary
The frontend architecture demonstrates several strong patterns:
**Strengths:**
- Factory patterns eliminate ~8500 LOC of boilerplate
- 32 custom hooks provide clean separation of concerns
- Dual storage strategy (Cache API + IndexedDB) enables robust offline
- Per-page layout pattern with permission checking
- Strong TypeScript typing across 12 type definition files
**Architecture Decisions:**
- CSR-only approach (appropriate for admin/builder app)
- Pages Router (stable, full-featured)
- Redux Toolkit for predictable state management
- MUI X DataGrid for complex data tables
- Tailwind CSS for utility-first styling
**Total Codebase:**
- 408 TypeScript/TSX files
- ~80,000 LOC estimated
- 14 distinct modules
- 13 entity CRUD implementations