# Frontend Pages Module ## Overview The Pages module implements all Next.js pages using the **Pages Router** pattern. It contains 99 TypeScript files providing entry points for routing, authentication flows, entity CRUD operations, and specialized features like the visual tour builder and runtime presentations. **Location:** `frontend/src/pages/` **Total Files:** 100 TypeScript/TSX files **Router:** Next.js Pages Router (not App Router) **Rendering:** Client-Side Rendering (CSR) - appropriate for admin/builder applications --- ## Architecture Diagram ``` pages/ │ ├── _app.tsx # App entry point (324 LOC) │ ├── Redux Provider │ ├── DownloadContext Provider │ ├── Axios interceptors │ ├── PWA service worker │ ├── ErrorBoundary │ └── IntroGuide tour system │ ├── index.tsx # Root redirect → / ├── login.tsx # Auth → /login ├── forgot.tsx # Auth → /forgot ├── verify-email.tsx # Auth → /verify-email ├── password-reset.tsx # Auth → /password-reset │ ├── dashboard.tsx # Admin home → /dashboard ├── profile.tsx # User profile → /profile ├── search.tsx # Global search → /search ├── error.tsx # Error page → /error │ ├── constructor.tsx # Visual builder → /constructor (1515 LOC) ├── element-type-defaults.tsx # Global element defaults → /element-type-defaults ├── global-transition-defaults.tsx # Global transitions → /global-transition-defaults ├── global-ui-control-defaults.tsx # Global UI controls → /global-ui-control-defaults ├── global-ui-control-defaults/[controlType].tsx # Single global UI control ├── project-element-defaults.tsx# Project element defaults → /project-element-defaults ├── project-ui-control-settings.tsx # Project UI controls → /project-ui-control-settings │ ├── privacy-policy.tsx # Legal → /privacy-policy ├── terms-of-use.tsx # Legal → /terms-of-use │ ├── p/[projectSlug]/ # Public presentations │ ├── index.tsx # Production → /p/[slug] │ └── stage.tsx # Stage preview → /p/[slug]/stage │ ├── api/ # API routes │ ├── logError.ts # Error logging endpoint │ └── hello.js # Health check │ └── [entity]/ # 13 entity CRUD directories ├── [entity]-list.tsx # List with filters ├── [entity]-new.tsx # Create form ├── [entity]-edit.tsx # Edit form ├── [entity]-view.tsx # Read-only view ├── [entity]-table.tsx # Embedded table └── [entityId].tsx # Dynamic route ``` --- ## Page Categories ### 1. App Entry (`_app.tsx`) **Location:** `pages/_app.tsx` **Size:** 324 LOC The application entry point that wraps all pages with providers and global functionality. **Responsibilities:** | Feature | Description | |---------|-------------| | Redux Provider | Wraps app with Redux store | | DownloadContext | PWA download progress tracking | | Axios Setup | Base URL, JWT interceptors, 401 handling | | PWA Registration | Service worker registration (production only) | | ErrorBoundary | Global error catching | | IntroGuide | Optional tour guide system | | Head Meta | SEO, OG tags, PWA manifest | | DevModeBadge | Development mode indicator | **Key Code Patterns:** ```typescript // Per-page layout pattern export type NextPageWithLayout

, IP = P> = NextPage & { getLayout?: (page: ReactElement) => ReactNode; }; function MyApp({ Component, pageProps }: AppPropsWithLayout) { const getLayout = Component.getLayout || ((page) => page); return ( {getLayout( <> ... {isDev && } )} ); } ``` **Axios Interceptors:** ```typescript // Request interceptor - attach JWT token axios.interceptors.request.use((config) => { const token = sessionStorage.getItem('token') || localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // Response interceptor - handle 401, presigned URL failures axios.interceptors.response.use( (response) => response, (error) => { // Handle presigned S3 URL failures (CORS issues) if (isPresignedS3Url(requestUrl) && isNetworkError) { disablePresignedUrls(); } // Handle 401 - clear tokens, redirect to login if (status === 401 && !isLoginRequest) { sessionStorage.removeItem('token'); localStorage.removeItem('token'); window.location.href = '/login'; } return Promise.reject(error); } ); ``` --- ### 2. Authentication Pages | Page | Route | Layout | Description | |------|-------|--------|-------------| | `login.tsx` | `/login` | Guest | Email/password login with OAuth hints | | `forgot.tsx` | `/forgot` | Guest | Password reset request | | `verify-email.tsx` | `/verify-email` | Guest | Email verification callback | | `password-reset.tsx` | `/password-reset` | Guest | Password reset with token | **Login Page Pattern:** ```typescript export default function Login() { const dispatch = useAppDispatch(); const { currentUser, isFetching, errorMessage, token } = useAppSelector( (state) => state.auth ); // Fetch user data when token exists useEffect(() => { if (token) dispatch(findMe()); }, [token]); // Redirect to dashboard if logged in useEffect(() => { if (currentUser?.id) router.push('/dashboard'); }, [currentUser?.id]); const handleSubmit = async (values) => { await dispatch(loginUser(values)); }; return (

); } Login.getLayout = (page: ReactElement) => {page}; ``` --- ### 3. Dashboard Page **Location:** `pages/dashboard.tsx` **Size:** 203 LOC **Route:** `/dashboard` Admin home page displaying entity counts and AI-generated widgets. **Features:** - Entity count cards with permission-based visibility - Links to entity list pages - Redirects viewer-only users with private production grants to the first allowed private presentation **Key Implementation:** ```typescript const Dashboard = () => { const { currentUser } = useAppSelector((state) => state.auth); const { getCount, getVisibleEntities } = useDashboardCounts(currentUser); const visibleEntities = getVisibleEntities(); return (
{visibleEntities.map((entity) => ( ))}
); }; Dashboard.getLayout = (page) => {page}; ``` --- ### 4. Entity CRUD Pages (78 pages) Each of the 13 entities has 6 standardized pages following consistent patterns. **Entities:** - users, roles, permissions - projects, project_memberships - assets, asset_variants, presigned_url_requests - tour_pages, project_audio_tracks - publish_events, pwa_caches, access_logs #### 4.1 List Page (`[entity]-list.tsx`) **Pattern:** Uses `createListPage` factory for most entities. **Factory Usage:** ```typescript // pages/users/users-list.tsx import { createListPage } from '../../factories/createListPage'; import TableUsers from '../../components/Users/TableUsers'; import { uploadCsv, setRefetch } from '../../stores/users/usersSlice'; const filters = [ { label: 'First Name', title: 'firstName' }, { label: 'Last Name', title: 'lastName' }, { label: 'E-Mail', title: 'email' }, ]; export default createListPage({ entityName: 'users', entityTitle: 'Users', TableComponent: TableUsers, filters, readPermission: 'READ_USERS', createPermission: 'CREATE_USERS', uploadCsvAction: uploadCsv, setRefetchAction: setRefetch, newItemLabel: 'Add/Invite User', }); ``` **Generated Features:** - Title and breadcrumbs - "New Item" button (permission-checked) - Filter button (adds filter rows) - Download CSV button - Upload CSV button with modal - Entity-specific table component **Custom List Page Example (assets-list.tsx):** ```typescript const AssetsTablesPage = () => { const { selectedProjectId } = useProjectSelector({ currentUser }); const { uploadingSections, runBatchUpload } = useAssetUploader({ selectedProjectId, onUploadComplete: () => loadAssets(selectedProjectId), }); return (
{ASSET_SECTIONS.map((section) => ( runBatchUpload(section, files)} onDeleteAsset={handleDeleteAsset} /> ))}
); }; AssetsTablesPage.getLayout = (page) => ( {page} ); ``` #### 4.2 New Page (`[entity]-new.tsx`) **Pattern:** Formik form with initial empty values. ```typescript // pages/projects/projects-new.tsx const initialValues = { name: '', slug: '', description: '', // ... all entity fields }; const ProjectsNew = () => { const dispatch = useAppDispatch(); const router = useRouter(); const handleSubmit = async (data) => { await dispatch(create(data)); await router.push('/projects/projects-list'); }; return (
{/* More fields */} router.push('...-list')} />
); }; ProjectsNew.getLayout = (page) => ( {page} ); ``` #### 4.3 Edit Page (`[entity]-edit.tsx`) **Pattern:** Uses `useEditPageSync` hook for data loading and form sync. ```typescript // pages/assets/assets-edit.tsx const EditAssetsPage = () => { const router = useRouter(); const dispatch = useAppDispatch(); const { values: initialValues, setValues, id } = useEditPageSync({ entitySelector: (state) => state.assets.assets, fetchAction: fetch, initialValues: initVals, }); const handleSubmit = async (data) => { if (id) { await dispatch(update({ id, data })); await router.push('/assets/assets-list'); } }; return (
{/* Form fields */}
); }; EditAssetsPage.getLayout = (page) => ( {page} ); ``` #### 4.4 View Page (`[entity]-view.tsx`) **Pattern:** Read-only display with related entity tables. ```typescript // pages/projects/projects-view.tsx const ProjectsView = () => { const dispatch = useAppDispatch(); const project = useAppSelector((state) => state.projects.projects); const { id } = router.query; useEffect(() => { dispatch(fetch({ id })); }, [id]); return ( {/* Main entity fields */}

Name

{project?.name}

{/* Related entities tables */}

Assets

... {project?.assets_project?.map((item) => ( router.push(`/assets/assets-view/?id=${item.id}`)}> {/* More columns */} ))}
{item.name}
); }; ProjectsView.getLayout = (page) => ( {page} ); ``` #### 4.5 Table Page (`[entity]-table.tsx`) **Pattern:** Embedded table for use within other pages. ```typescript // Minimal wrapper around the table component // Usually just renders the table with pagination ``` #### 4.6 Dynamic Route (`[entityId].tsx`) **Pattern:** Project workspace or redirect to view page. **Project Workspace Example:** ```typescript // pages/projects/[projectsId].tsx const ProjectWorkspacePage = () => { const { projectsId } = router.query; const [project, setProject] = useState(null); // Load project data useEffect(() => { if (!projectId) return; axios.get(`/projects/${projectId}`).then((res) => setProject(res.data)); }, [projectId]); return ( {/* Project info card */}

{project?.name}

Slug: {project?.slug}

{/* Action cards grid */}

Constructor

Assets

{/* More action cards */}
{/* Publish section */}

Publish

setIsPublishModalActive(true)} /> window.open(presentationLinks.production)} /> window.open(presentationLinks.stage)} />
); }; ``` --- ### 5. Constructor Page **Location:** `pages/constructor.tsx` **Size:** 1515 LOC (largest page) **Route:** `/constructor?projectId=X` Visual tour builder with canvas-based element editing. **Features:** - Canvas with background image/video/audio - Draggable positioned elements - Element editor panel with General/CSS/Effects tabs - Main constructor toolbar with Edit/Interact mode, Page actions, Elements actions, Save, Stage, Exit, and Collapse sections - Save and Stage toolbar buttons keep a fixed compact width and reserve the timestamp row to avoid layout shifts between one-line and two-line states - Page selector, reorder, create, duplicate, and delete actions - Element copy/paste across pages in the same presentation - Transition preview - Asset preloading with blob URL caching - Gallery carousel overlay with draggable buttons **Key Hooks Used:** | Hook | Purpose | |------|---------| | `useConstructorElements` | Element CRUD, selection, nested item helpers, and constructor-local element clipboard | | `useConstructorPageActions` | Page save/create/duplicate and Save to Stage operations | | `useCanvasElementDrag` | Element positioning with percentage coordinates | | `useTransitionPreview` | Transition video preview state | | `usePreloadOrchestrator` | Asset preloading with S3 presigned URLs | | `usePageSwitch` | Page navigation with background transitions | | `useTransitionPlayback` | Transition video playback coordination | | `useBackgroundTransition` | Background overlay during page switch | | `useIconPreload` | Icon asset preloading | | `useMediaDurationProbe` | Media duration extraction with caching | | `useDraggable` | Draggable panel positioning | | `useOutsideClick` | Click-outside detection for deselection | **Component Structure:** ```typescript const ConstructorPage = () => { const router = useRouter(); const canvasRef = useRef(null); const projectId = router.query.projectId; const pageIdFromRoute = router.query.pageId; // Element management const { elements, getElements, selectedElementId, selectedElement, addElement, updateElement, removeSelectedElement, selectElement, copySelectedElement, pasteCopiedElement, canPasteElement, } = useConstructorElements({ initialElements, elementDefaultsByType, allowedNavigationTypes, }); // Page persistence and page creation/duplication const { saveConstructor, saveToStage, createPage, duplicatePage, } = useConstructorPageActions({ activePageId, elements, getElements, pageBackground, onReload: handleReload, }); return (
{/* Center: Canvas */}
{elements.map((element) => ( selectElement(element.id)} onUpdate={updateElement} /> ))}
{/* Right: Element Editor */} {selectedElementId && ( )} {/* Overlays */}
); }; ConstructorPage.getLayout = (page) => ( {page} ); ``` --- ### 6. Runtime Presentation Pages Public presentation pages for viewing published tours. #### Production Presentation **Location:** `pages/p/[projectSlug]/index.tsx` **Route:** `/p/[slug]` (e.g., `/p/cardiff`) ```typescript export default function ProductionPresentation() { const router = useRouter(); const { projectSlug } = router.query; if (!projectSlug || typeof projectSlug !== 'string') { return
Loading...
; } return ( ); } ProductionPresentation.getLayout = (page) => {page}; ``` #### Stage Presentation **Location:** `pages/p/[projectSlug]/stage.tsx` **Route:** `/p/[slug]/stage` ```typescript export default function StagePresentation() { const router = useRouter(); const { projectSlug } = router.query; return ( ); } StagePresentation.getLayout = (page) => {page}; ``` **Environment Distinction:** - `production` - Public, no auth required - `stage` - Preview, may require auth --- ### 7. Element Defaults Pages #### Global Element Type Defaults **Location:** `pages/element-type-defaults.tsx` **Route:** `/element-type-defaults` Lists all 11 global element type defaults. ```typescript const ElementTypeDefaultsPage = () => { const [rows, setRows] = useState([]); useEffect(() => { axios.get('/element-type-defaults?limit=1000').then((res) => { setRows(res.data.rows); }); }, []); return ( {rows.map((item) => (

{item.name || toHumanLabel(item.element_type)}

{item.element_type} • Order {item.sort_order}

))}
); }; ElementTypeDefaultsPage.getLayout = (page) => ( {page} ); ``` The page also links to `/global-transition-defaults` for transition defaults and lists global UI controls as separate items linking to per-control editors. #### Global Transition Defaults **Location:** `pages/global-transition-defaults.tsx` **Route:** `/global-transition-defaults` Dedicated editor for platform-wide page transition defaults. #### Global UI Control Defaults **Location:** `pages/global-ui-control-defaults.tsx` **Route:** `/global-ui-control-defaults` List page for platform-wide runtime controls. Each item opens a dedicated editor at `/global-ui-control-defaults/offline`, `/global-ui-control-defaults/fullscreen`, or `/global-ui-control-defaults/sound`. The editor uses `UiControlsSettingsForm` with General Settings, CSS Styles, and Effects tabs. #### Project Element Defaults **Location:** `pages/project-element-defaults.tsx` **Route:** `/project-element-defaults?projectId=X` Project-specific element defaults that override global defaults. #### Project UI Control Settings **Location:** `pages/project-ui-control-settings.tsx` **Route:** `/project-ui-control-settings?projectId=X&environment=dev` Dedicated editor for per-project fullscreen, sound, and offline control overrides. The form resolves global defaults as fallback, saves project environment overrides, and can remove the project override to return to global defaults. --- ### 8. Search Page **Location:** `pages/search.tsx` **Route:** `/search?query=X` Global search results page. ```typescript const SearchView = () => { const searchQuery = router.query.query; const [searchResults, setSearchResults] = useState([]); useEffect(() => { axios.post('/search', { searchQuery }).then((res) => { setSearchResults(res.data); }); }, [searchQuery]); // Group results by table const groupedResults = searchResults.reduce((acc, item) => { acc[item.tableName] = acc[item.tableName] || []; acc[item.tableName].push(item); return acc; }, {}); return ( ); }; SearchView.getLayout = (page) => ( {page} ); ``` --- ### 9. API Routes Next.js API routes for server-side functionality. #### Error Logging (`api/logError.ts`) Stores runtime errors to JSON file for debugging. ```typescript export default async function handler(req, res) { const dataFilePath = path.join(process.cwd(), 'json/runtimeError.json'); if (req.method === 'GET') { const jsonData = await fsPromises.readFile(dataFilePath, 'utf-8'); return res.status(200).json(JSON.parse(jsonData)); } if (req.method === 'POST') { await fsPromises.writeFile(dataFilePath, JSON.stringify(req.body)); return res.status(200).json({ message: 'Data stored successfully' }); } if (req.method === 'DELETE') { await fsPromises.writeFile(dataFilePath, '{}'); return res.status(200).json({ message: 'Data deleted successfully' }); } } ``` --- ### 10. Static Pages | Page | Route | Description | |------|-------|-------------| | `index.tsx` | `/` | Redirects guests to `/login` and authenticated users to `/projects/projects-list` | | `error.tsx` | `/error` | Error display page | | `privacy-policy.tsx` | `/privacy-policy` | Privacy policy text | | `terms-of-use.tsx` | `/terms-of-use` | Terms of use text | | `profile.tsx` | `/profile` | User profile edit | --- ## Layout System ### getLayout Pattern Pages define their layout via a static `getLayout` property: ```typescript // Authenticated page MyPage.getLayout = (page: ReactElement) => ( {page} ); // Guest page (public) LoginPage.getLayout = (page: ReactElement) => ( {page} ); // No layout (full-screen) RuntimePage.getLayout = (page: ReactElement) => page; ``` ### Layout Components | Layout | Purpose | Features | |--------|---------|----------| | `LayoutAuthenticated` | Protected pages | JWT validation, permission check, NavBar, Sidebar, Footer | | `LayoutGuest` | Public pages | Minimal layout, no auth required | **LayoutAuthenticated Features:** - JWT token validation (client-side decode) - Permission checking per route - Project existence guard - NavBar with user menu - AsideMenu sidebar - FooterBar - IntroGuide tour integration --- ## Permission System Pages declare required permissions via layout: ```typescript // Single permission {page} // Multiple permissions (any) {page} // No permission required (authenticated only) {page} ``` **Permission Naming Convention:** - `READ_[ENTITY]` - View list/details - `CREATE_[ENTITY]` - Create new records - `UPDATE_[ENTITY]` - Edit existing records - `DELETE_[ENTITY]` - Delete records --- ## Factory Pattern ### createListPage Factory Generates standardized list pages with common functionality. **Input:** ```typescript interface ListPageConfig { entityName: string; // API endpoint name entityTitle: string; // Page title TableComponent: Component; // Table component filters: Filter[]; // Available filters readPermission: string; // Required permission createPermission: string; // For "New" button uploadCsvAction: AsyncThunk; // CSV upload action setRefetchAction: Action; // Trigger refetch newItemLabel?: string; // "New Item" button label cardBoxId?: string; // For tour targeting } ``` **Generated Output:** - Page component with title - "New Item" button (permission-checked) - Filter button and filter rows - CSV download/upload buttons - Table component with pagination - Layout with permission check --- ## Page File Reference ### By Size (Largest First) | File | Size | Description | |------|------|-------------| | `constructor.tsx` | 1515 LOC | Visual tour builder | | `projects/projects-view.tsx` | 827 LOC | Project view with all relations | | `_app.tsx` | 324 LOC | App entry point | | `privacy-policy.tsx` | 292 LOC | Legal page | | `login.tsx` | 230 LOC | Authentication | | `terms-of-use.tsx` | 205 LOC | Legal page | | `dashboard.tsx` | 203 LOC | Admin home | | `project-element-defaults.tsx` | 199 LOC | Project defaults | | `profile.tsx` | 182 LOC | User profile | | `projects/[projectsId].tsx` | 354 LOC | Project workspace | | `project-ui-control-settings.tsx` | 226 LOC | Project UI controls | | `element-type-defaults.tsx` | 277 LOC | Defaults hub | | `global-transition-defaults.tsx` | 221 LOC | Global transition defaults | | `global-ui-control-defaults/[controlType].tsx` | 153 LOC | Single global UI control | | `global-ui-control-defaults.tsx` | 103 LOC | Global UI controls list | | `search.tsx` | 97 LOC | Search results | | `index.tsx` | 25 LOC | Root redirect | | `forgot.tsx` | 85 LOC | Password reset | ### By Category | Category | Files | Total LOC | |----------|-------|-----------| | Entity CRUD | 78 | ~4000 | | Authentication | 4 | ~400 | | Special Pages | 10 | ~3100 | | Static/Legal | 4 | ~600 | | API Routes | 2 | ~100 | | Runtime | 2 | ~60 | | App Entry | 1 | ~324 | | Element Defaults | 8 | ~1100 | --- ## Routing Reference ### Static Routes | Route | Page | Layout | Permission | |-------|------|--------|------------| | `/` | index.tsx | Guest | Redirect entry | | `/login` | login.tsx | Guest | - | | `/forgot` | forgot.tsx | Guest | - | | `/verify-email` | verify-email.tsx | Guest | - | | `/password-reset` | password-reset.tsx | Guest | - | | `/dashboard` | dashboard.tsx | Authenticated | - | | `/profile` | profile.tsx | Authenticated | - | | `/search` | search.tsx | Authenticated | CREATE_SEARCH | | `/error` | error.tsx | Guest | - | | `/constructor` | constructor.tsx | Authenticated | UPDATE_TOUR_PAGES | | `/element-type-defaults` | element-type-defaults.tsx | Authenticated | READ_PAGE_ELEMENTS | | `/global-transition-defaults` | global-transition-defaults.tsx | Authenticated | UPDATE_PAGE_ELEMENTS | | `/global-ui-control-defaults` | global-ui-control-defaults.tsx | Authenticated | READ_PAGE_ELEMENTS | | `/project-element-defaults` | project-element-defaults.tsx | Authenticated | READ_PAGE_ELEMENTS | | `/project-ui-control-settings` | project-ui-control-settings.tsx | Authenticated | UPDATE_PAGE_ELEMENTS | | `/privacy-policy` | privacy-policy.tsx | Guest | - | | `/terms-of-use` | terms-of-use.tsx | Guest | - | ### Dynamic Routes | Route Pattern | Page | Description | |---------------|------|-------------| | `/p/[projectSlug]` | p/[projectSlug]/index.tsx | Production presentation | | `/p/[projectSlug]/stage` | p/[projectSlug]/stage.tsx | Stage presentation | | `/global-ui-control-defaults/[controlType]` | global-ui-control-defaults/[controlType].tsx | Single global UI control editor | | `/[entity]/[entityId]` | [entity]/[entityId].tsx | Entity workspace/redirect | ### Entity Routes (×13) | Entity | List | New | Edit | View | |--------|------|-----|------|------| | users | `/users/users-list` | `/users/users-new` | `/users/users-edit?id=X` | `/users/users-view?id=X` | | roles | `/roles/roles-list` | ... | ... | ... | | permissions | `/permissions/permissions-list` | ... | ... | ... | | projects | `/projects/projects-list` | ... | ... | ... | | project_memberships | `/project_memberships/...` | ... | ... | ... | | assets | `/assets/assets-list` | ... | ... | ... | | asset_variants | `/asset_variants/...` | ... | ... | ... | | presigned_url_requests | `/presigned_url_requests/...` | ... | ... | ... | | tour_pages | `/tour_pages/tour_pages-list` | ... | ... | ... | | project_audio_tracks | `/project_audio_tracks/...` | ... | ... | ... | | publish_events | `/publish_events/...` | ... | ... | ... | | pwa_caches | `/pwa_caches/pwa_caches-list` | ... | ... | ... | | access_logs | `/access_logs/access_logs-list` | ... | ... | ... | --- ## Common Patterns ### 1. Page with Formik Form ```typescript import { Formik, Form, Field } from 'formik'; const MyPage = () => { const handleSubmit = async (values) => { await dispatch(create(values)); router.push('/entity/entity-list'); }; return (
); }; ``` ### 2. Page with Data Fetching ```typescript const MyPage = () => { const dispatch = useAppDispatch(); const data = useAppSelector((state) => state.entity.items); const { id } = router.query; useEffect(() => { dispatch(fetch({ id })); }, [id]); return
{data?.name}
; }; ``` ### 3. Page with useEditPageSync ```typescript const EditPage = () => { const { values, setValues, id } = useEditPageSync({ entitySelector: (state) => state.entity.items, fetchAction: fetch, initialValues: { name: '', ... }, }); return ( ... ); }; ``` ### 4. Page with Permission Check ```typescript MyPage.getLayout = (page) => ( {page} ); ``` --- ## Related Documentation | Document | Description | |----------|-------------| | [frontend-architecture.md](./frontend-architecture.md) | Overall frontend architecture | | [hooks-reference.md](./hooks-reference.md) | Custom hooks documentation | | [runtime-presentation.md](./runtime-presentation.md) | Runtime presentation details | | [constructor-page-editor.md](./constructor-page-editor.md) | Constructor page details | | [ui-elements.md](../../documentation/ui-elements.md) | UI element types |