32 KiB
AGENTS.md
This file provides guidance to CODEX when working with code in this repository.
MAIN RULE: DON'T MADE UP ANYTHING!!! IF YOU NOT SURE - DOUBLECHECK IT VIA PROJECT DOCUMENTATION, TOOL DOCUMENTATION, APPROPRIATE MCP, WEB SEARCH OR JUST ASK FOR ME TO CLARIFY.
Project Overview
Tour Builder Platform - a web application for building and managing interactive virtual tours. Built with:
- Frontend: Next.js 15 with React 19, TypeScript, Redux Toolkit, Tailwind CSS
- Backend: Node.js/Express with Sequelize ORM
- Database: PostgreSQL
System Requirements
Required Software
- Node.js 24.x for backend runtime and TypeScript migration work
- PostgreSQL 14+
FFmpeg
FFmpeg is bundled with the backend via ffmpeg-static and ffprobe-static npm packages. No manual installation is required.
How it works:
- Pre-compiled binaries are downloaded during
npm install fluent-ffmpegis configured to use bundled binaries inbackend/src/services/videoProcessing.js- Works across all platforms (Linux, macOS, Windows) and Docker environments
Supported use case:
- Reversed video generation for back navigation transitions
Important Rules
- Never change global app configs casually - Do not modify
.envor database configuration without verifying the target environment. These configs are shared across environments and incorrect changes can break production. - Centralize environment variables - Do not read new app/runtime environment variables directly from services, routes, components, hooks, or feature modules. Backend app env vars must be added to
backend/src/utils/env-validation.ts, typed inbackend/src/types/env.ts/backend/src/types/config.ts, exposed throughbackend/src/config.ts, and consumed viaconfig. Frontend public env vars must be centralized infrontend/src/config.ts. Directprocess.envaccess is acceptable only in bootstrap/config entrypoints (load-env, logger bootstrap, DB/Umzug config, Next config), scripts, migrations, seeders, and tests. - Backend async handlers must be wrapped with
wrapAsynchelper for error propagation. - Use Passport JWT for protected routes:
passport.authenticate('jwt', { session: false }). Secure routes must read the authenticated user throughgetCurrentUser(req)frombackend/src/utils/request-context.ts; if absent, returnForbiddenError. - Access model - Keep the current permission model: effective permissions are
app_role.permissions + user.custom_permissions.Administrator,Platform Owner, andAccount Managerare platform-wide internal roles. Other internal users currently have all-project scope by default unless a future explicit override system is implemented.Publicusers must not have admin API permissions and can only access public production pages plus explicitly granted private production presentations. - Public role hardening - Do not grant RBAC permissions or custom permissions to
Publicusers. Do not represent private production presentation grants asREAD_PROJECTS,READ_TOUR_PAGES, or other admin permissions; useproduction_presentation_access. - Centralize access decisions - New authorization logic must go through an
AccessPolicy-style helper/service rather than ad hoc checks in routes/components. Keep admin API permissions separate from runtime presentation access.
Mandatory Rules For New Code
These rules are required for new code and for touched code when practical.
Backend Boundaries
- Routes/controllers: only authenticate, read runtime context, validate request input, call a service, and map the response. Do not put business logic in routes.
- Services/domain: own business logic, transactions, permission decisions, and orchestration.
- DB API/repository: only data access, filters, includes, pagination, and persistence mapping.
- Policy: all role/permission/runtime access decisions belong in policy/helper services, not scattered across routes.
- Validation: all new external
body,query, andparamsinputs must be validated before service calls.Joiis already available in backend and should be preferred unless there is a clear reason to use another validator. - ID handling: route params are canonical. For
PUT/PATCH/DELETE /:id, usereq.params.id; reject mismatched body ids. - Query safety: new list/autocomplete endpoints must define max
limit, default pagination, allowed sort fields, and allowed sort directions. - Service contracts: new service/DB API methods should use object/options signatures, e.g.
Service.update({ id, data, currentUser, transaction, runtimeContext })andDBApi.findAll(filter, { currentUser, transaction, runtimeContext }). - Logging: use the project logger in runtime backend code (
services,routes,middlewares,db/api,utils, app/bootstrap/config). Do not add newconsole.*calls outside migrations, seeders, scripts, or explicit debug-only CLI tooling.
Backend Typing
- New backend pure helpers, validators, and policy modules should use TypeScript or JSDoc/checkJs-compatible types.
- Define shared shapes for
currentUser,runtimeContext, service options, and validation results when touching related code. - Do not add global
Express.Requestaugmentation for project request fields. Usebackend/src/utils/request-context.tshelpers for request-scopedcurrentUser,runtimeContext, logging, runtime-public flags, and permission overrides. - Keep active backend source in strict TypeScript/ESM.
Frontend State And API
- Redux is for client/app state only: auth/session UI, theme/style, layout/sidebar, constructor UI state, and app preferences.
- TanStack Query is for server state: API reads, entity lists/details, mutations, invalidation, and background refetch.
- Do not create new entity CRUD Redux slices or new Redux thunks for server reads/mutations.
- Centralize API access through query hooks or a shared API client. Avoid direct
axioscalls inside feature UI components unless there is a clear existing local pattern. - New feature-specific code should live near the feature/domain it belongs to. Do not put feature logic into generic
components,hooks, orlibfolders when a feature-local module is clearer.
Frontend TypeScript And React
- Do not add new
anywithout a specific reason. Prefer existing domain types or add a narrow local type. - Avoid new
eslint-disable,@ts-ignore, and hook dependency suppressions. If unavoidable, add a short reason. - New hooks must follow React hook rules and should be structured so
react-hooks/exhaustive-depscan be enabled. - Do not add new client usage of
jsonwebtoken; usejwt-decodeor/auth/mefor client-side identity.
Database And Migrations
- Do not add indexes speculatively. Add indexes after checking query patterns or explaining the expected query.
- Migrations must be reversible or include an explicit rollback/backup plan.
- Do not drop columns/tables in the same change that stops using them unless explicitly requested and production data safety is covered.
- Do not normalize
ui_schema_jsoninto new tables unless there is a concrete current need; prefer validation/extraction helpers first. - Do not rewrite, rename, or reformat already applied backend migration files. Add new migrations only for new schema changes.
Disabled Features
The following features are implemented but currently disabled with false && conditions:
- Navigation blocking while preloading - Navigation buttons can be disabled until neighbor page backgrounds are preloaded. Currently disabled to allow instant navigation response. To re-enable, remove
false &&in:frontend/src/components/RuntimePresentation.tsx(isForwardNavDisabled, handleElementClick)frontend/src/pages/constructor.tsx(handleElementClick, isNavDisabled calculation)
Documentation Workflow
- Before each task: Research relevant documentation in
documentation/to understand existing implementations, patterns, and conventions. - After each task: Update affected documentation to reflect changes (API changes, new fields, modified workflows).
- After implementing new features/modules: Create new documentation file in
documentation/following the existing format, and add reference to the Feature Documentation table in this file.
Quick Start
# Terminal 1 - Backend (port 3000)
cd backend && npm run start-dev
# Terminal 2 - Frontend (port 3001)
cd frontend && npm run dev
Common Commands
Backend (run from backend/ directory, runs on port 3000 in the default dev_stage flow)
npm install # Install dependencies
npm run start-dev # Start server (loads .env, migrates, seeds, watches)
npm run lint # ESLint check
npm run typecheck # Strict TypeScript check for migrated backend scope
npm run test # Unit tests (Node test runner)
npm run test:integration # Integration tests; DB tests skip when Postgres is unavailable
npm run test:e2e # E2E HTTP tests with a local listener
npm run test:all # Unit, integration, and e2e suites
npm run build # Compile migrated TypeScript files
npm run db:migrate # Run migrations only
npm run db:migrate:undo # Undo last migration
npm run db:seed # Seed database only
npm run db:reset # Drop, create, migrate, and seed
Creating new migrations: Add new migration files deliberately under backend/src/db/migrations/ only when a schema change is required. Keep them reversible and preserve already applied migration files unchanged.
Note: Backend .env is loaded centrally by backend/src/load-env.ts for app and DB entrypoints. If NODE_ENV is absent, it defaults to dev_stage, matching the standard VM backend flow and using the .env DB settings. Do not add NODE_ENV=production to local startup unless a task explicitly requires the production config.
The backend defaults to port 3000 in dev_stage and 8080 otherwise; set PORT explicitly when a task needs a different port.
Frontend (run from frontend/ directory)
npm install # Install dependencies
npm run dev # Start dev server with Turbopack (port 3001)
npm run typecheck # TypeScript check without production build
npm run verify # Typecheck, lint, and production build
npm run build # Production build
npm run lint # ESLint check (.ts, .tsx files)
npm run format # Format code with Prettier
Standard VM Environment
The standard VM port split is:
- Frontend PM2 app
frontend-dev: Next.js production server on port3001 - Backend PM2 app
backend-dev:npm run startwithNODE_ENV=dev_stageon port3000 - Apache: public entrypoint and reverse proxy on port
80
Direct VM backend health checks should target http://127.0.0.1:3000/api/.... A protected endpoint returning 401 Unauthorized without JWT means the backend is reachable.
Docker (run from docker/ directory)
chmod +x start-backend.sh && chmod +x wait-for-it.sh # First time setup
docker-compose up # Start all services
rm -rf data && docker-compose up # Start with fresh database
Architecture
Backend Structure (backend/src/)
- routes/: Express route handlers (RESTful endpoints)
- db/api/: Database access layer (CRUD operations per model)
- db/models/: Sequelize model definitions
- db/migrations/: Database migrations
- db/seeders/: Seed data
- auth/: Passport.js authentication (JWT, Google OAuth, Microsoft OAuth)
- services/: Business logic services (emails, notifications, publishing, file storage)
- factories/: Router and service generation (
router.factory.js,service.factory.js) - middlewares/: Permission checking, runtime context handling
- helpers/: Utility functions (e.g.,
wrapAsyncfor async error handling)
Frontend Structure (frontend/src/)
- pages/: Next.js pages (each entity has
-list,-edit,-new,-view,-tablevariants) - components/: React components (PascalCase naming)
- stores/: Redux Toolkit slices (per entity + auth, main, style slices)
- layouts/: Page layouts (Authenticated, Guest)
- hooks/: Global custom hooks (
usePreloadOrchestrator,useNeighborGraph, etc.) - lib/: Utility libraries (
elementStyles,imagePreDecode,mediaDuration,parseJson) - types/: TypeScript type definitions (
constructor.ts,runtime.ts,preload.ts) - helpers/: Utility functions
- styles/: Global CSS with Tailwind (
_theme.cssuses@apply)
Key Domain Models
Projects, Tour Pages, Assets, Asset Variants, Permissions, Roles, Users, Publish Events, PWA Caches, Element Type Defaults, Project Element Defaults
Note: Page elements, navigation links, and transitions are stored directly in tour_pages.ui_schema_json.
Element Defaults Hierarchy
The system has a two-level defaults cascade for UI elements:
- element_type_defaults - Global platform-wide defaults (11 predefined types)
- project_element_defaults - Project-specific overrides (auto-snapshotted from global on project creation)
When creating new elements, defaults cascade: global → project. Instance-specific settings are stored in tour_pages.ui_schema_json.
Special Pages
constructor.tsx: Tour builder/editor with drag-drop, element positioning, page managementruntime.tsx: Tour playback with transitions, preloading, navigationp/[slug].tsx: Public tour pages with PWA offline support
State Management
- Redux Toolkit is the default only for client/app state (auth/session UI, style/theme, layout/sidebar, constructor UI state, app preferences)
- TanStack Query is the default for server state (API reads, entity lists/details, mutations, invalidation, background refetch)
- Do not create new entity CRUD Redux slices in
stores/[entity]/[entity]Slice.ts. - Core slices:
authSlice.ts(auth),mainSlice.ts(UI),styleSlice.ts(theming),constructorSlice.ts(editor) - Use
useAppSelectoranduseAppDispatchhooks fromstores/hooks.ts - Local hooks acceptable for ephemeral, component-scoped state (e.g.,
usePageNavigationfor session-scoped page history) - See stores-module.md for decision tree
Code Conventions
Backend
- ES6+ with arrow functions, const/let
- Document endpoints with Swagger JSDoc comments
- Lowercase filenames (e.g.,
auth.js,projects.js)
Frontend
- Functional components with TypeScript
- PascalCase for components and types, camelCase for variables/functions
- Custom hooks prefixed with
use(e.g.,useAuth) - Tailwind CSS with theme customization in
_theme.css
Styling
- Theme customization uses
@applydirective in_theme.css - Sidebar styles: Target
#asideMenu(defined inAsideMenuLayer.tsx) for sidebar overrides - Use highly specific selectors when overriding Tailwind utilities to avoid conflicts
- Themed blocks (
.theme-pink,.theme-green) standardize UI appearance - ensure custom overrides integrate cleanly
Error Handling
- Backend: Use centralized
commonErrorHandlermiddleware for uniform error responses - Frontend: Use React error boundaries for runtime errors
API Patterns
Routes follow pattern: /api/[entity]/ with standard CRUD operations
POST /api/auth/signin/local- LoginGET /api/auth/me- Current user (requires JWT)- Self-registration is disabled. New users are created by Administrator, Platform Owner, or Account Manager through the Users flow and receive an invitation/setup link.
All entity routes support: list, findOne, create, update, destroy operations with pagination and filtering.
Backend Factory Patterns
Router Factory (factories/router.factory.js)
Generates standard CRUD routes for entities:
module.exports = createEntityRouter('assets', AssetsService, AssetsDBApi, {
permissionEntity: 'assets',
});
Service Factory (factories/service.factory.js)
Generates service classes with transaction handling:
module.exports = createEntityService('assets', AssetsDBApi);
Base DB API (db/api/base.api.js)
All entity APIs extend GenericDBApi and override:
MODEL- Sequelize model referenceSEARCHABLE_FIELDS- Fields for text searchRANGE_FIELDS- Fields for range filteringENUM_FIELDS- Fields for exact match filteringgetFieldMapping(data)- Transform input data before save
File Storage
Controlled by FILE_STORAGE_PROVIDER env var or auto-detected from credentials:
s3- AWS S3 (requiresS3_BUCKET,S3_REGION,S3_ACCESS_KEY_ID,S3_SECRET_ACCESS_KEY)gcloud- Google Cloud Storagelocal- Local filesystem (default)
Publishing Workflow
Three-tier content environment model (distinct from NODE_ENV server environment):
- Dev (constructor editing) → Stage (preview) → Production (public)
Route-based environment access:
/p/[slug]- Production presentation/p/[slug]/stage- Stage presentation/constructor?projectId=- Dev editing (always dev environment)
Key endpoints:
POST /api/publish/save-to-stage- Dev → Stage (body:{ projectId })POST /api/publish- Stage → Production (body:{ projectId })
Environment isolation: Pages have environment field (dev, stage, production). The X-Runtime-Environment header (set by frontend) determines which content environment to query. Both frontend (RuntimePresentation.tsx) and backend (runtime-context.js) filter by this field.
Server Environment vs Content Environment:
NODE_ENVcontrols database config (production/dev_stage/development)tour_pages.environmentcontrols content visibility (dev/stage/production)
Private Production Presentations
Production presentations are public by default at /p/[slug]. Each project can switch production runtime visibility through projects.production_presentation_visibility (public default, private optional).
- Platform staff users with any RBAC permission can visit every private production presentation
- Customer users with
Publicrole can visit only private production presentations granted inproduction_presentation_access Administrator,Platform Owner, andAccount Managercan create users- When creating or editing a
Publicuser, the form shows a selector for private production presentations and saves DB access grants - Do not use config files or env vars for customer allowlists
- Do not grant customer viewer users broad permissions such as
READ_PROJECTSorREAD_TOUR_PAGES; any RBAC permission makes the user platform staff for private presentation access
See private-production-presentations.md.
PWA & Offline Support
- Service Worker: Generated by Serwist from
frontend/src/sw.ts→public/sw.js - Offline Caching: PWA_Caches model tracks cached assets per project
- Runtime Mode: Middleware distinguishes public vs authenticated access for offline tours
Asset Preloading Architecture
Runtime presentations use direct S3 downloads via presigned URLs for instant page navigation:
1. Request presigned URLs (max 50 per batch, 1-hour expiry)
POST /api/file/presign { urls: ["assets/img.jpg", ...] }
2. Download directly from S3 → Store in Cache API (< 5MB) or IndexedDB (≥ 5MB)
3. Create blob URL → Decode image → Store in readyBlobUrlsRef
4. Instant lookup during navigation (O(1))
const blobUrl = preloadOrchestrator.getReadyBlobUrl(originalUrl);
Preload Priority: Transition videos (+150) > Images (+100) > Audio (+50) > Video (+30)
Storage Layers:
| Layer | Size Limit | Purpose |
|---|---|---|
| Cache API | < 5MB | Fast asset storage |
| IndexedDB (Dexie) | ≥ 5MB | Large assets, offline data |
| Blob URLs | Memory | Pre-decoded for instant display |
Frontend Patterns
MUI X Data Grid v7
Use new valueGetter signature:
// For value transformation
valueGetter: (value) => value?.id ?? value
// For row access
valueGetter: (_value, row) => new Date(row.created_at)
Custom Hooks
Hooks are located in three places:
src/hooks/: Global hooks (usePreloadOrchestrator,usePageNavigationState,useTransitionPlayback,usePageNavigation)src/hooks/video/: Video playback primitives (8 composable hooks for video playback scenarios)- Component directories: Domain-specific hooks (
components/Assets/useAssetUploader.ts)
Key runtime hooks:
| Hook | Purpose |
|---|---|
usePreloadOrchestrator |
Stream-first asset preloading (current page + transitions only) |
usePageNavigationState |
Unified navigation state machine (replaces 6+ hooks) |
useTransitionPlayback |
Video transition playback coordination |
usePageNavigation |
Page history tracking with browser-like back behavior |
useCanvasScale |
Responsive canvas scaling with letterbox mode |
useNetworkAware |
Network condition monitoring for adaptive transitions |
useBackgroundDimensionSuggestion |
Detect background media dimensions for canvas size suggestions |
Video Hooks (src/hooks/video/):
| Hook | Purpose |
|---|---|
useVideoBlobUrl |
Resolve video URLs to blob URLs from preload cache |
useVideoPlaybackCore |
Core playback logic combining multiple primitives |
useVideoPlayer |
Complete UI video player hook |
Redux Entity Pattern
Use this pattern only when maintaining an existing Redux entity flow; new server-state code should use TanStack Query hooks instead.
import { fetch, deleteItem } from '../../stores/assets/assetsSlice';
const dispatch = useAppDispatch();
dispatch(fetch({ query: '?limit=100' }));
Feature Documentation
Detailed feature documentation is available in documentation/:
| Document | Description |
|---|---|
| project-architecture.md | Overall project structure and architecture |
| api-reference.md | API endpoints reference |
| authentication-system.md | JWT, OAuth (Google, Microsoft) authentication |
| rbac-system.md | Role-based access control system |
| user-management.md | User CRUD, roles assignment, account management |
| project-memberships.md | Team collaboration, per-project access control |
| asset-upload-variants.md | Asset upload pipeline and variant generation |
| ui-elements.md | UI Elements stored in ui_schema_json (buttons, hotspots, galleries, tooltips, media players) |
| page-transitions.md | Video transitions stored on navigation elements |
| project-transition-settings.md | Environment-aware CSS transition settings (fade, duration, easing) |
| video-playback.md | Video player implementation, iOS autoplay compatibility |
| assets-preloading.md | Asset preloading and caching strategy |
| publishing-workflow.md | Dev → Stage → Production publishing |
| private-production-presentations.md | Private production presentation allowlist, viewer users, and runtime access flow |
| offline-pwa-mode.md | PWA offline capabilities and caching |
| email-notification-service.md | Nodemailer/SES integration, verification emails, invitations |
| search-system.md | Global full-text search, permission-based filtering |
| deployment-vm.md | Standard VM deployment topology, PM2 recovery, ports, OOM/ffmpeg diagnostics |
| custom-domains-apache.md | Customer-owned presentation domains via Apache, DNS A records, Certbot, and host/path routing |
| page-links-navigation.md | Page navigation using targetPageSlug in elements |
| access-logs-audit-trail.md | Access logging, audit trail, activity tracking |
| db-cleanup-audit.md | Non-destructive DB cleanup audit, orphan checks, legacy schema checks, and soft-delete retention policy |
| global-ui-controls.md | Configurable fullscreen, sound, and offline system controls with global/project/page cascade |
| backend/database-schema.md | Complete database schema - all models, fields, relationships, indexes, constraints |
| backend/backend-architecture.md | Backend architecture - layers, design patterns, middleware, factories, file storage |
| backend/api-endpoints.md | Complete API reference - all endpoints, request/response formats, authentication, rate limits |
| backend/modules/core.md | Core module - index.js (entry), config.ts (configuration), helpers.js/utilities |
| backend/modules/auth.md | Auth module - Passport.js strategies (JWT, Google, Microsoft), login, invitation setup, password reset, email verification |
| backend/modules/middleware.md | Middleware module - rate limiting, permissions, runtime context, public access control, file uploads |
| backend/modules/routes.md | Routes module - 22 route files, factory pattern, CRUD endpoints, Swagger docs, auth/file/search/sql routes |
| backend/modules/services.md | Services module - business logic layer, 34 service files, factory pattern, file storage (S3/GCloud/Local), email, notifications, publishing |
| backend/modules/email.md | Email module - Nodemailer/AWS SES, transactional emails, HTML templates, token management, email verification, password reset, invitations |
| backend/modules/notifications.md | Notifications module - error classes (ValidationError, ForbiddenError), i18n message catalog, helper functions |
| backend/modules/factories.md | Factories module - createEntityRouter and createEntityService functions, code generation patterns for CRUD operations, boilerplate elimination |
| backend/modules/db-models.md | DB Models module - 16 Sequelize models, entity definitions, associations, validations, lifecycle hooks, soft delete patterns |
| backend/modules/db-api.md | DB API module - GenericDBApi base class, 18 entity APIs, declarative configuration, query filtering, runtime context helpers |
| backend/modules/db-migrations.md | DB Migrations module - Umzug runner, migration safety rules, schema evolution, data backfill |
| backend/modules/db-seeders.md | DB Seeders module - Umzug seeders, RBAC setup (7 roles, 54 permissions), sample data opt-in |
| backend/modules/db-config.md | DB Config module - typed DB config, environment validation (Joi), Umzug commands, sync/reset scripts |
| backend/modules/utilities.md | Utilities module - error classes, Pino logging, env validation, request helpers (wrapAsync, commonErrorHandler), DB utils, i18n messages |
| backend/testing.md | Backend test strategy - unit, integration, and e2e coverage, helpers, commands, and environment notes |
| frontend/hooks-reference.md | React hooks reference (useFormSync, useEntityTable, useOfflineMode, etc.) |
| frontend/video-hooks-module.md | Video playback primitive hooks (8 composable hooks for video scenarios) |
| frontend/constructor-page-editor.md | Constructor page editor - visual tour builder with drag-drop elements |
| frontend/runtime-presentation.md | Runtime presentation viewer - full-screen tour playback with transitions |
| frontend/navigation-smooth-transitions.md | Navigation & smooth transitions - page switching, transition video playback, last frame preservation, online/offline modes |
| frontend/ui-adaptivity-system.md | UI Adaptivity System - canvas units (--cu), responsive scaling, letterbox mode, element styling |
| frontend/ui-element-preloading-analysis.md | UI element processing & neighbor preloading - deep analysis of asset extraction, preload flow, offline caching |
| frontend/asset-upload-preloading-pwa-analysis.md | Deep analysis of asset upload, preload orchestrator, PWA/offline mode, storage layers, network awareness |
| frontend/frontend-architecture.md | Frontend architecture - 386 files, 14 modules, factories, hooks, Redux, PWA/offline, design patterns |
| frontend/pages-module.md | Pages module - 99 pages, _app.tsx entry, entity CRUD pattern, constructor, runtime presentations, layouts |
| frontend/components-module.md | Components module - 183 files, entity tables, factories, constructor, element settings, runtime, PWA/offline |
| frontend/hooks-module.md | Hooks module - 52 custom hooks, runtime/preloading, PWA/offline, constructor, tables/forms, UI utilities |
| frontend/stores-module.md | Stores module - Redux Toolkit, 17 slices (4 core + 13 entity), createEntitySlice factory, typed hooks |
| frontend/lib-module.md | Lib module - 19 utility files, asset URL management, element defaults/styles/effects, offline storage (Cache API + IndexedDB), download queue |
| frontend/types-module.md | Types module - 18 TypeScript definition files, entity types, constructor/runtime types, API/Redux types, permissions enum, offline/preload types |
| frontend/factories-module.md | Factories module - 5 factory files (~1,285 LOC), createListPage, createFormPage, createTableComponent, configBuilderFactory, createEntitySlice for 91% boilerplate reduction |
| frontend/schemas-module.md | Schemas module - 6 Zod validation schema files (~257 LOC), form validation for User, Asset, Project, TourPage, Role entities with type inference |
| frontend/helpers-module.md | Helpers module - 6 utility files (~304 LOC), dataFormatter (entity display), hasPermission (RBAC), notifyStateHandler (Redux), text formatters, file saver |
| frontend/layouts-module.md | Layouts module - 2 layout files (~262 LOC), LayoutAuthenticated (JWT auth, permissions, UI chrome), LayoutGuest (public pages), getLayout pattern |
| frontend/config-module.md | Config module - 12 config files (~523 LOC), Next.js/Tailwind/TypeScript build config, runtime settings (config.ts), menu configs, offline/preload configs, ESLint rules |
| frontend/context-module.md | Context module - DownloadContext (~256 LOC) for PWA download progress state, DownloadEventBus integration, useDownloadContext/useDownloadContextOptional hooks |
| frontend/interfaces-module.md | Interfaces module - ~15 type definition files (~2,200+ LOC), entity types, API contracts, Redux state shapes, permissions enum, constructor/runtime/offline/preload interfaces |