# 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 { 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) => ( {page} ); ``` --- ### 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) => ( {page} ); // _app.tsx applies it const getLayout = Component.getLayout ?? ((page) => page); return getLayout(); ``` ### 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>; 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 { 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