# Frontend Interfaces Module ## Overview The Interfaces module provides **TypeScript type definitions** for the entire frontend application. It defines entity types, API contracts, Redux state shapes, form configurations, and domain-specific interfaces for the constructor, runtime, and offline systems. **Primary Location:** `frontend/src/types/` **Secondary Locations:** - `frontend/src/interfaces/` - Legacy UI types - `frontend/src/components/*/types.ts` - Component-specific types - `frontend/src/lib/*.ts` - Library types **Total Files:** ~15 type definition files (~2,680+ LOC) --- ## Architecture ``` frontend/src/ ├── types/ # Primary type definitions │ ├── index.ts (14 LOC) # Central re-exports │ ├── entities.ts (276 LOC) # Entity interfaces │ ├── api.ts (62 LOC) # API types │ ├── redux.ts (60 LOC) # Redux state types │ ├── forms.ts (76 LOC) # Form configuration │ ├── filters.ts (74 LOC) # Filter/table config │ ├── permissions.ts (122 LOC) # Permission enum │ ├── offline.ts (194 LOC) # PWA/offline types │ ├── preload.ts (57 LOC) # Preload types │ ├── runtime.ts (80 LOC) # Runtime types │ ├── constructor.ts (418 LOC) # Constructor types │ └── presentation.ts (107 LOC) # Shared presentation │ ├── interfaces/ # Legacy UI types │ └── index.ts (122 LOC) # Menu, color, user types │ ├── components/ │ ├── Constructor/types.ts (312 LOC) # Constructor props │ ├── ElementSettings/types.ts (293 LOC) # Settings props │ └── DataGrid/configBuilderFactory.tsx # Column metadata │ └── lib/ ├── elementStyles.ts (146 LOC) # CSS style interfaces └── elementEffects.ts (267 LOC) # Effect interfaces ``` --- ## Type Categories ### Category Overview | Category | Location | Purpose | |----------|----------|---------| | **Entities** | `types/entities.ts` | Database model interfaces | | **API** | `types/api.ts` | Request/response types | | **Redux** | `types/redux.ts` | Store state shapes | | **Forms** | `types/forms.ts` | Form field config | | **Filters** | `types/filters.ts` | Table/filter config | | **Permissions** | `types/permissions.ts` | RBAC enum | | **Offline/PWA** | `types/offline.ts` | Offline storage types | | **Preload** | `types/preload.ts` | Asset preloading | | **Runtime** | `types/runtime.ts` | Presentation playback | | **Constructor** | `types/constructor.ts` | Visual editor types | | **Presentation** | `types/presentation.ts` | Shared presentation | | **UI/Menu** | `interfaces/index.ts` | Navigation config | | **Styles** | `lib/elementStyles.ts` | CSS properties | | **Effects** | `lib/elementEffects.ts` | Animation config | --- ## Entity Interfaces (`types/entities.ts`) ### Base Entity All entities extend the base interface: ```typescript export interface BaseEntity { id: string; createdAt?: string; updatedAt?: string; } ``` ### Core Entities #### User ```typescript export interface User extends BaseEntity { firstName?: string; lastName?: string; phoneNumber?: string; email: string; disabled?: boolean; avatar?: ImageFile[]; app_role?: Role | null; custom_permissions?: PermissionEntity[]; password?: string; } ``` #### Project ```typescript export interface Project extends BaseEntity { name: string; slug?: string; description?: string; logo_url?: string; favicon_url?: string; og_image_url?: string; } ``` #### Asset ```typescript export interface Asset extends BaseEntity { project?: Project | string | null; name: string; asset_type: 'image' | 'video' | 'audio' | 'file'; type?: 'general' | 'icon' | 'background_image' | 'audio' | 'video' | 'transition' | 'logo' | 'favicon' | 'document'; cdn_url?: string; storage_key?: string; mime_type?: string; size_mb?: number; width_px?: number; height_px?: number; duration_sec?: number; checksum?: string; is_public?: boolean; is_deleted?: boolean; deleted_at_time?: string | Date; } ``` #### TourPage ```typescript export interface TourPage extends BaseEntity { project?: Project | string | null; title?: string; slug?: string; background_asset?: Asset | string | null; audio_asset?: Asset | string | null; is_start_page?: boolean; sort_order?: number; } ``` ### Entity Type Map ```typescript export type EntityName = | 'users' | 'roles' | 'permissions' | 'projects' | 'project_memberships' | 'assets' | 'asset_variants' | 'tour_pages' | 'project_audio_tracks' | 'publish_events' | 'pwa_caches' | 'presigned_url_requests' | 'access_logs' | 'element_type_defaults'; export interface EntityTypeMap { users: User; roles: Role; projects: Project; assets: Asset; tour_pages: TourPage; // ... all 14 entities mapped } ``` ### All Entity Interfaces | Interface | Fields | Purpose | |-----------|--------|---------| | `User` | 10 | User accounts | | `Role` | 2 | Role definitions | | `PermissionEntity` | 1 | Permission records | | `Project` | 6 | Tour projects | | `Asset` | 14 | Media files | | `AssetVariant` | 5 | Image variants | | `TourPage` | 7 | Tour pages | | `PageElement` | 10 | UI elements | | `PageLink` | 4 | Navigation links | | `Transition` | 5 | Video transitions | | `ProjectAudioTrack` | 14 | Audio tracks | | `ProjectMembership` | 8 | Team access | | `PublishEvent` | 17 | Publish history | | `PwaCache` | 4 | PWA cache records | | `PresignedUrlRequest` | 11 | URL requests | | `AccessLog` | 9 | Audit logs | | `UIElement` | 7 | UI components | | `ImageFile` | 5 | Upload metadata | --- ## API Interfaces (`types/api.ts`) ### Response Types ```typescript // Paginated list response export interface PaginatedResponse { rows: T[]; count: number; } // API error structure export interface ApiError { message?: string; statusCode?: number; errors?: Record; } ``` ### Request Types ```typescript // Fetch parameters export interface FetchParams { id?: string; query?: string; limit?: number; page?: number; } // List query parameters export interface ListQueryParams { page?: number; limit?: number; sort?: 'asc' | 'desc'; field?: string; [filterKey: string]: string | number | boolean | undefined; } // Sort model (DataGrid) export interface SortModel { field: string; sort: 'asc' | 'desc'; } ``` ### Mutation Types ```typescript // Create/update payload export interface MutationPayload { id?: string; data: Partial; } // Delete by IDs export interface DeleteByIdsPayload { ids: string[]; } // Autocomplete item export interface AutocompleteItem { id: string; label: string; } // CSV upload response export interface CsvUploadResponse { imported: number; errors?: string[]; } ``` --- ## Redux Interfaces (`types/redux.ts`) ### Notification State ```typescript export interface NotificationState { showNotification: boolean; textNotification: string; typeNotification: 'warn' | 'error' | 'success' | 'info' | ''; } ``` ### Entity Slice State ```typescript // Generic entity state (used by createEntitySlice) export interface EntitySliceState { data: T[]; loading: boolean; count: number; refetch: boolean; notify: NotificationState; } // Legacy entity slice state (for backward compatibility during migration) export interface LegacyEntitySliceState { [entityName: string]: T[] | boolean | number | NotificationState; loading: boolean; count: number; refetch: boolean; notify: NotificationState; } // Slice factory configuration export interface EntitySliceConfig { name: string; endpoint: string; singularName?: string; // For notification messages (e.g., "User" instead of "Users") } ``` ### Core Slice States ```typescript // Auth state export interface AuthState { currentUser: User | null; token: string | null; isFetching: boolean; errorMessage: string | null; loadingMessage: string | null; notify: NotificationState; } // Style state export interface StyleState { darkMode: boolean; bgLayoutColor: string; focusRingColor: string; corners: string; cardsColor: string; } // Main app state export interface MainState { isAsideMobileExpanded: boolean; isAsideLgActive: boolean; } ``` --- ## Form Interfaces (`types/forms.ts`) ### Field Types ```typescript export type FormFieldType = | 'text' | 'email' | 'number' | 'password' | 'textarea' | 'richtext' | 'select' | 'multiselect' | 'date' | 'datetime' | 'boolean' | 'switch' | 'image' | 'file' | 'enum'; ``` ### Field Configuration ```typescript export interface FormFieldConfig { name: string; label: string; type: FormFieldType; required?: boolean; placeholder?: string; itemRef?: string; // Entity for select options showField?: string; // Display field from options options?: EnumOption[]; // Enum select options uploadPath?: string; // File upload path fileSchema?: FileSchema; // File constraints min?: number; max?: number; maxLength?: number; className?: string; } export interface EnumOption { value: string; label: string; } export interface FileSchema { size?: number; // Max bytes formats?: string[]; // Allowed extensions } ``` ### Form Page Configuration ```typescript export interface FormPageConfig { entityName: string; entityTitle: string; mode: 'create' | 'edit'; permission: string; initialValues: TFormData; fields: FormFieldConfig[]; listPath?: string; } export type FormSubmitHandler = (values: T) => Promise; export type FormValues = Record; ``` --- ## Filter Interfaces (`types/filters.ts`) ### Filter Configuration ```typescript export type FilterType = 'text' | 'enum' | 'date' | 'number' | 'boolean'; export interface Filter { label: string; title: string; // Field name type?: FilterType; options?: string[]; // For enum filters number?: boolean; date?: boolean; } export interface FilterItem { id: string; fields: FilterFields; } export interface FilterFields { selectedField: string; filterValue: string; filterValueFrom: string; filterValueTo: string; } // Filter request format for API export type FilterRequest = Record; ``` ### Table Configuration ```typescript export interface TableColumnConfig { field: string; headerName: string; width?: number; minWidth?: number; maxWidth?: number; flex?: number; editable?: boolean; sortable?: boolean; filterable?: boolean; type?: 'string' | 'number' | 'date' | 'dateTime' | 'boolean' | 'singleSelect'; valueOptions?: string[]; renderType?: 'link' | 'boolean' | 'date' | 'datetime' | 'image' | 'actions' | 'relation'; relationField?: string; } export interface TableConfig { entityName: string; columns: TableColumnConfig[]; filters: Filter[]; defaultSort?: { field: string; sort: 'asc' | 'desc' }; perPage?: number; } ``` --- ## Permission Types (`types/permissions.ts`) ### Permission Enum ```typescript export enum Permission { // User permissions READ_USERS = 'READ_USERS', CREATE_USERS = 'CREATE_USERS', UPDATE_USERS = 'UPDATE_USERS', DELETE_USERS = 'DELETE_USERS', // Project permissions READ_PROJECTS = 'READ_PROJECTS', CREATE_PROJECTS = 'CREATE_PROJECTS', UPDATE_PROJECTS = 'UPDATE_PROJECTS', DELETE_PROJECTS = 'DELETE_PROJECTS', // Asset permissions READ_ASSETS = 'READ_ASSETS', CREATE_ASSETS = 'CREATE_ASSETS', UPDATE_ASSETS = 'UPDATE_ASSETS', DELETE_ASSETS = 'DELETE_ASSETS', // ... 15 more entity permission groups } ``` ### Permission Helpers ```typescript export type PermissionString = `${Permission}` | string; // Get CRUD permissions for any entity export const getEntityPermissions = (entityName: string) => { const uppercased = entityName.toUpperCase(); return { read: `READ_${uppercased}` as PermissionString, create: `CREATE_${uppercased}` as PermissionString, update: `UPDATE_${uppercased}` as PermissionString, delete: `DELETE_${uppercased}` as PermissionString, }; }; ``` ### Permission Categories | Entity | Permissions | |--------|-------------| | Users | READ, CREATE, UPDATE, DELETE | | Roles | READ, CREATE, UPDATE, DELETE | | Permissions | READ, CREATE, UPDATE, DELETE | | Projects | READ, CREATE, UPDATE, DELETE | | Project Memberships | READ, CREATE, UPDATE, DELETE | | Assets | READ, CREATE, UPDATE, DELETE | | Asset Variants | READ, CREATE, UPDATE, DELETE | | Tour Pages | READ, CREATE, UPDATE, DELETE | | Page Elements | READ, CREATE, UPDATE, DELETE | | Page Links | READ, CREATE, UPDATE, DELETE | | Transitions | READ, CREATE, UPDATE, DELETE | | Project Audio Tracks | READ, CREATE, UPDATE, DELETE | | Publish Events | READ, CREATE, UPDATE, DELETE | | PWA Caches | READ, CREATE, UPDATE, DELETE | | Presigned URL Requests | READ, CREATE, UPDATE, DELETE | | Access Logs | READ, CREATE, UPDATE, DELETE | | UI Elements | READ, CREATE, UPDATE, DELETE | **Total: 68 permissions (17 entities × 4 CRUD operations)** --- ## Offline/PWA Interfaces (`types/offline.ts`) ### Asset Types ```typescript export type AssetVariantType = | 'thumbnail' | 'preview' | 'webp' | 'mp4_low' | 'mp4_high' | 'original'; export type AssetType = 'image' | 'video' | 'audio' | 'transition' | 'other'; export type PreloadJobStatus = | 'queued' | 'downloading' | 'completed' | 'error' | 'paused'; ``` ### Preload Job ```typescript export interface PreloadJob { id: string; assetId: string; url: string; filename: string; progress: number; status: PreloadJobStatus; bytesLoaded: number; totalBytes: number; pageId?: string; variantType?: AssetVariantType; assetType?: AssetType; error?: string; addedAt: number; startedAt?: number; completedAt?: number; } ``` ### Offline Project ```typescript export type ProjectOfflineStatus = | 'not_downloaded' | 'downloading' | 'downloaded' | 'outdated' | 'error'; export interface OfflineProject { id: string; slug: string; name: string; status: ProjectOfflineStatus; totalAssets: number; downloadedAssets: number; totalSizeBytes: number; downloadedSizeBytes: number; lastSyncedAt?: number; version?: string; } ``` ### Storage Types ```typescript export interface OfflineAsset { id: string; projectId: string; url: string; filename: string; variantType: AssetVariantType; assetType: AssetType; mimeType: string; sizeBytes: number; blob: Blob; downloadedAt: number; } export interface DownloadQueueItem { id: string; projectId: string; assetId: string; url: string; filename: string; status: PreloadJobStatus; priority: number; retryCount: number; bytesLoaded: number; totalBytes: number; addedAt: number; lastAttemptAt?: number; error?: string; } ``` ### Network/Storage Info ```typescript export interface NetworkInfo { isOnline: boolean; effectiveType?: 'slow-2g' | '2g' | '3g' | '4g'; downlink?: number; // Mbps rtt?: number; // ms saveData?: boolean; } export interface StorageQuotaInfo { usage: number; quota: number; percentUsed: number; available: number; canStore: (bytes: number) => boolean; } ``` ### Manifest Types ```typescript export interface OfflineManifest { version: string; projectId: string; projectSlug: string; assets: ManifestAsset[]; totalSizeBytes: number; generatedAt: number; } export interface ManifestAsset { id: string; url: string; filename: string; variantType: AssetVariantType; assetType: AssetType; mimeType: string; sizeBytes: number; pageIds: string[]; } ``` ### Preload State Types ```typescript // Preload orchestrator state export interface PreloadState { isActive: boolean; currentPageId: string | null; queuedAssets: string[]; loadingAssets: string[]; completedAssets: string[]; failedAssets: string[]; totalProgress: number; } // Neighbor graph for preloading export interface NeighborGraph { // Map from pageId to array of connected pageIds with distance connections: Map>; // Get neighbors within a certain depth getNeighbors: (pageId: string, maxDepth: number) => string[]; // Get all assets for a set of pages getAssetsForPages: (pageIds: string[]) => string[]; } ``` ### Event Types ```typescript export interface PreloadStartEvent { jobId: string; assetId: string; url: string; } export interface PreloadProgressEvent { jobId: string; progress: number; bytesLoaded: number; totalBytes: number; } export interface PreloadCompleteEvent { jobId: string; assetId: string; } export interface PreloadErrorEvent { jobId: string; assetId: string; error: string; } export interface ProjectDownloadProgressEvent { projectId: string; progress: number; downloadedAssets: number; totalAssets: number; downloadedBytes: number; totalBytes: number; } export interface ProjectDownloadCompleteEvent { projectId: string; } ``` --- ## Preload Interfaces (`types/preload.ts`) ### Page Types ```typescript export interface PreloadPage { id: string; background_image_url?: string; background_video_url?: string; background_audio_url?: string; } export interface PreloadPageLink { id: string; from_pageId?: string; to_pageId?: string; is_active?: boolean; transition?: { id: string; video_url?: string; }; } export interface PreloadElement { id: string; pageId?: string; element_type?: string; content_json?: string; } ``` ### Asset Info ```typescript export interface PreloadAssetInfo { url: string; pageId: string; assetType: 'image' | 'video' | 'audio' | 'transition' | 'other'; priority: number; } export interface PreloadNeighborInfo { pageId: string; distance: number; } ``` --- ## Runtime Interfaces (`types/runtime.ts`) ### Runtime Types ```typescript export interface RuntimeProject { id: string; name?: string; slug?: string; description?: string; } export interface RuntimePage extends PreloadPage { slug?: string; name?: string; sort_order?: number; ui_schema_json?: string; environment?: 'dev' | 'stage' | 'production'; } export interface RuntimePageLink extends PreloadPageLink { transitionId?: string; direction?: string; external_url?: string; trigger_selector?: string; } export interface RuntimeElement extends PreloadElement { name?: string; sort_order?: number; is_visible?: boolean; x_percent?: number; y_percent?: number; width_percent?: number; height_percent?: number; rotation_deg?: number; style_json?: string; } export interface RuntimeTransition { id: string; name?: string; slug?: string; video_url?: string; duration_sec?: number; supports_reverse?: boolean; } ``` ### Transition State ```typescript export interface TransitionOverlayState { targetPageId: string; transitionName: string; videoUrl: string; isReverse: boolean; durationSec?: number; } ``` --- ## Constructor Interfaces (`types/constructor.ts`) ### Element Types ```typescript export type CanvasElementType = | 'navigation_next' // Forward navigation | 'navigation_prev' // Back navigation | 'spot' // Hotspot | 'description' // Text block | 'tooltip' // Hover tooltip | 'gallery' // Image gallery | 'carousel' // Image carousel | 'logo' // Logo element | 'video_player' // Video player | 'audio_player' // Audio player | 'popup'; // Popup/modal export type NavigationButtonKind = 'forward' | 'back'; ``` ### Canvas Element ```typescript export interface BaseCanvasElement extends ElementStyleProperties, ElementEffectProperties { id: string; type: CanvasElementType | string; label?: string; xPercent?: number; yPercent?: number; iconUrl?: string; appearDelaySec?: number; appearDurationSec?: number | null; } export interface CanvasElement extends BaseCanvasElement { type: CanvasElementType; label: string; xPercent: number; yPercent: number; // Media mediaUrl?: string; mediaAutoplay?: boolean; mediaLoop?: boolean; mediaMuted?: boolean; backgroundImageUrl?: string; videoUrl?: string; audioUrl?: string; // Gallery galleryCards?: GalleryCard[]; galleryHeaderImageUrl?: string; galleryTitle?: string; galleryInfoSpans?: GalleryInfoSpan[]; galleryColumns?: number; galleryTitleFontFamily?: string; galleryCardFontFamily?: string; // Carousel carouselSlides?: CarouselSlide[]; carouselCaptionFontFamily?: string; carouselPrevIconUrl?: string; carouselNextIconUrl?: string; // Tooltip tooltipTitle?: string; tooltipText?: string; tooltipTitleFontFamily?: string; tooltipTextFontFamily?: string; // Description descriptionTitle?: string; descriptionText?: string; descriptionTitleFontSize?: string; descriptionTextFontSize?: string; descriptionTitleFontFamily?: string; descriptionTextFontFamily?: string; descriptionTitleColor?: string; descriptionTextColor?: string; descriptionBackgroundColor?: string; // Navigation navLabel?: string; navType?: NavigationButtonKind; navDisabled?: boolean; /** @deprecated Use targetPageSlug instead */ targetPageId?: string; targetPageSlug?: string; transitionVideoUrl?: string; transitionReverseMode?: 'auto_reverse' | 'separate_video'; reverseVideoUrl?: string; transitionDurationSec?: number; // Gallery Carousel Settings galleryCarouselPrevIconUrl?: string; galleryCarouselNextIconUrl?: string; galleryCarouselBackIconUrl?: string; galleryCarouselBackLabel?: string; galleryCarouselPrevX?: number; galleryCarouselPrevY?: number; galleryCarouselNextX?: number; galleryCarouselNextY?: number; galleryCarouselBackX?: number; galleryCarouselBackY?: number; galleryCarouselPrevWidth?: string; galleryCarouselPrevHeight?: string; galleryCarouselNextWidth?: string; galleryCarouselNextHeight?: string; galleryCarouselBackWidth?: string; galleryCarouselBackHeight?: string; } ``` ### Collection Items ```typescript export interface GalleryCard { id: string; imageUrl: string; title: string; description: string; } export interface GalleryInfoSpan { id: string; text: string; } export interface CarouselSlide { id: string; imageUrl: string; caption: string; } ``` ### Constructor Schema ```typescript export interface ConstructorSchema { elements?: CanvasElement[]; } ``` ### Asset Types ```typescript export interface ConstructorAsset { id: string; name?: string; asset_type?: 'image' | 'video' | 'audio' | 'file'; type?: 'icon' | 'background_image' | 'audio' | 'video' | 'transition' | 'logo' | 'favicon' | 'document' | 'general'; cdn_url?: string | null; storage_key?: string | null; } export interface AssetOption { value: string; label: string; } ``` ### Element Defaults ```typescript export interface UiElementDefault { id: string; element_type?: string; is_active?: boolean; default_settings_json?: string | Record; } export interface NormalizedElementDefault { id: string; element_type: string; name?: string; sort_order?: number; settings: Partial; } ``` ### Editor Props Interfaces ```typescript export interface EditorLayoutProps { elementEditorRef: React.RefObject; position: { x: number; y: number }; isCollapsed: boolean; onToggleCollapse: () => void; onDragStart: (event: React.MouseEvent) => void; } export interface EditorStateProps { title: string; activeTab: 'general' | 'css' | 'effects'; onTabChange: (tab: 'general' | 'css' | 'effects') => void; } export interface EditorElementProps { selectedElement: CanvasElement | null; selectedMenuItem: 'none' | 'background_image' | 'background_video' | 'background_audio' | 'create_transition'; onRemoveElement: () => void; onUpdateElement: (patch: Partial) => void; } export interface EditorBackgroundProps { backgroundImageUrl: string; backgroundVideoUrl: string; backgroundAudioUrl: string; onBackgroundImageChange: (value: string) => void; onBackgroundVideoChange: (value: string) => void; onBackgroundAudioChange: (value: string) => void; } export interface EditorTransitionProps { newTransitionName: string; newTransitionVideoUrl: string; newTransitionSupportsReverse: boolean; isCreatingTransition: boolean; onNewTransitionNameChange: (value: string) => void; onNewTransitionVideoUrlChange: (value: string) => void; onNewTransitionSupportsReverseChange: (value: boolean) => void; onCreateTransition: () => void; } export interface EditorDurationNotesProps { backgroundVideoDurationNote: string; backgroundAudioDurationNote: string; newTransitionDurationNote: string; selectedMediaDurationNote: string; selectedTransitionDurationNote: string; } export interface EditorAssetOptionsProps { backgroundImageAssetOptions: AssetOption[]; videoAssetOptions: AssetOption[]; audioAssetOptions: AssetOption[]; transitionVideoAssetOptions: AssetOption[]; iconAssetOptions: AssetOption[]; imageAssetOptions: AssetOption[]; } export interface EditorNavigationProps { allowedNavigationTypes: Array<'navigation_next' | 'navigation_prev'>; pages: Array<{ id: string; name?: string; slug?: string; sort_order?: number; }>; activePageId: string; onPreviewTransition: (direction: 'forward' | 'back') => void; normalizeNavigationType: ( element: CanvasElement, nextType: 'navigation_next' | 'navigation_prev', ) => CanvasElement; } export interface EditorCollectionOpsProps { galleryCards: { add: () => void; update: (cardId: string, patch: Partial) => void; remove: (cardId: string) => void; }; galleryInfoSpans: { add: () => void; update: (spanId: string, text: string) => void; remove: (spanId: string) => void; }; carouselSlides: { add: () => void; update: (slideId: string, patch: Partial) => void; remove: (slideId: string) => void; }; } export interface EditorMediaUtilsProps { getDuration: (url: string) => number | undefined; } ``` --- ## Presentation Interfaces (`types/presentation.ts`) ### State Types ```typescript export interface TransitionPreviewState { targetPageId: string; videoUrl: string; storageKey: string; isReverse: boolean; } export interface BackgroundState { imageUrl: string; videoUrl: string; audioUrl: string; } export interface NavigationTarget { page: RuntimePage; pageId: string; transitionVideoUrl?: string; isBack: boolean; } ``` ### API Configuration ```typescript export interface RuntimeApiConfig { headers: { 'X-Runtime-Project-Slug'?: string; 'X-Runtime-Environment'?: string; }; } export interface PageDataLoaderResult { project: RuntimeProject | null; pages: RuntimePage[]; isLoading: boolean; error: string; reload: (preservePageId?: string) => Promise; } // Runtime project (duplicate in presentation.ts) export interface RuntimeProject { id: string; name?: string; slug?: string; description?: string; } ``` ### Navigation Types ```typescript export interface NavigableElement { id: string; type: string; targetPageSlug?: string; targetPageId?: string; transitionVideoUrl?: string; navType?: 'forward' | 'back'; navDisabled?: boolean; } ``` ### Transition Types ```typescript export type TransitionPhase = | 'idle' | 'preparing' | 'playing' | 'reversing' | 'completed'; export interface BackgroundTransitionOptions { pendingTransitionComplete: boolean; isBackgroundReady: boolean; transitionVideoRef: React.RefObject; pageSwitch: { clearPreviousBackground: () => void; isSwitching: boolean; isNewBgReady: boolean; previousBgImageUrl: string; }; onCleanup: () => void; } ``` --- ## UI/Menu Interfaces (`interfaces/index.ts`) ### Menu Types ```typescript export type MenuAsideItem = { label: string; icon?: string; href?: string; target?: string; color?: ColorButtonKey; isLogout?: boolean; withDevider?: boolean; menu?: MenuAsideItem[]; permissions?: string | string[]; }; export type MenuNavBarItem = { label?: string; icon?: string; href?: string; target?: string; isDivider?: boolean; isLogout?: boolean; isDesktopNoLabel?: boolean; isToggleLightDark?: boolean; isCurrentUser?: boolean; menu?: MenuNavBarItem[]; }; ``` ### Color Types ```typescript export type ColorKey = | 'white' | 'light' | 'contrast' | 'success' | 'danger' | 'warning' | 'info'; export type ColorButtonKey = | 'white' | 'whiteDark' | 'lightDark' | 'contrast' | 'success' | 'danger' | 'warning' | 'info' | 'void'; export type BgKey = 'purplePink' | 'pinkRed' | 'violet'; export type StyleKey = 'white' | 'basic'; ``` --- ## Style Interfaces (`lib/elementStyles.ts`) ### CSS Style Properties ```typescript export interface ElementStyleProperties { width?: string; height?: string; minWidth?: string; maxWidth?: string; minHeight?: string; maxHeight?: string; margin?: string; padding?: string; gap?: string; fontSize?: string; lineHeight?: string; fontWeight?: string; border?: string; borderRadius?: string; opacity?: string; boxShadow?: string; display?: string; position?: string; justifyContent?: string; alignItems?: string; textAlign?: string; zIndex?: string; backgroundColor?: string; color?: string; fontFamily?: string; } export const ELEMENT_STYLE_PROPS = [ 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'margin', 'padding', 'gap', 'fontSize', 'lineHeight', 'fontWeight', 'border', 'borderRadius', 'boxShadow', 'display', 'position', 'justifyContent', 'alignItems', 'textAlign', 'zIndex', 'backgroundColor', 'color', 'fontFamily', ] as const; ``` --- ## Effect Interfaces (`lib/elementEffects.ts`) ### Animation Types ```typescript export type AppearAnimationType = | '' | 'fade' | 'slide-up' | 'slide-down' | 'slide-left' | 'slide-right' | 'scale'; ``` ### Effect Properties ```typescript export interface ElementEffectProperties { // Appear animation appearAnimation?: AppearAnimationType; appearAnimationDuration?: string; appearAnimationEasing?: string; // Hover effects hoverScale?: string; hoverOpacity?: string; hoverBackgroundColor?: string; hoverColor?: string; hoverBoxShadow?: string; hoverTransitionDuration?: string; // Focus effects focusScale?: string; focusOpacity?: string; focusOutline?: string; focusBoxShadow?: string; // Active/press effects activeScale?: string; activeOpacity?: string; activeBackgroundColor?: string; } export const EFFECT_PROPS = [ 'appearAnimation', 'appearAnimationDuration', 'appearAnimationEasing', 'hoverScale', 'hoverOpacity', 'hoverBackgroundColor', 'hoverColor', 'hoverBoxShadow', 'hoverTransitionDuration', 'focusScale', 'focusOpacity', 'focusOutline', 'focusBoxShadow', 'activeScale', 'activeOpacity', 'activeBackgroundColor', ] as const; ``` --- ## Component Types ### Constructor Component Types (`components/Constructor/types.ts`) ```typescript export type ConstructorInteractionMode = 'edit' | 'interact'; export type EditorMenuItem = | 'none' | 'background_image' | 'background_video' | 'background_audio' | 'create_transition'; export type ElementEditorTab = 'general' | 'css' | 'effects'; export interface Position { x: number; y: number; } export interface CanvasElementProps { element: CanvasElement; isSelected: boolean; isEditMode: boolean; isDisabled?: boolean; canvasElapsedSec: number; preloadedIconUrl: boolean; onClick: (element: CanvasElement) => void; onMouseDown?: (event: React.MouseEvent, elementId: string) => void; } export interface ConstructorCanvasProps { canvasRef: React.RefObject; elements: CanvasElement[]; selectedElementId: string; isEditMode: boolean; isLoading: boolean; canvasElapsedSec: number; preloadedIconUrlMap: Record; background: CanvasBackgroundProps; transitionPreview: boolean; isReverseBuffering: boolean; onElementClick: (element: CanvasElement) => void; onElementMouseDown: (event: React.MouseEvent, elementId: string) => void; onCreatePage: () => void; isCreatingPage: boolean; } ``` ### Element Settings Types (`components/ElementSettings/types.ts`) ```typescript export type ElementSettingsContext = | 'global' // element-type-defaults page | 'project' // project-element-defaults page | 'constructor'; // constructor page export interface StyleSettingsSectionProps { values: ElementStyleProperties; onChange: (prop: keyof ElementStyleProperties, value: string) => void; } export interface NavigationSettingsSectionProps { iconUrl: string; navLabel: string; navType: 'forward' | 'back'; navDisabled: boolean; targetPageId: string; targetPageSlug: string; transitionVideoUrl: string; transitionReverseMode: 'auto_reverse' | 'separate_video'; reverseVideoUrl: string; onChange: (field: string, value: string | boolean) => void; context: ElementSettingsContext; iconAssetOptions?: AssetOption[]; transitionVideoAssetOptions?: AssetOption[]; pageOptions?: { value: string; label: string }[]; } ``` ### DataGrid Column Types (`components/DataGrid/configBuilderFactory.tsx`) ```typescript export interface ColumnMetadata { field: string; headerName: string; type?: 'text' | 'boolean' | 'date' | 'datetime' | 'number' | 'relation' | 'relationMany' | 'singleSelectRelation' | 'image' | 'actions'; editable?: boolean; sortable?: boolean; width?: number; flex?: number; minWidth?: number; entityRef?: string; displayField?: string; renderCell?: (params: GridRenderCellParams) => React.ReactElement; valueFormatter?: (value: unknown) => string; } export interface ColumnBuilderConfig { entityName: string; entityPath?: string; columns: ColumnMetadata[]; updatePermission?: string; } ``` --- ## Hook Interfaces ### usePageSwitch ```typescript export interface SwitchablePage { id: string; background_image_url?: string; background_video_url?: string; background_audio_url?: string; } export interface PreloadCacheProvider { getReadyBlobUrl?: (url: string) => string | null; getCachedBlobUrl?: (url: string) => Promise; preloadedUrls?: Set; } export interface UsePageSwitchOptions { preloadCache?: PreloadCacheProvider; } export interface UsePageSwitchResult { currentBgImageUrl: string; currentBgVideoUrl: string; currentBgAudioUrl: string; previousBgImageUrl: string; isSwitching: boolean; isNewBgReady: boolean; // ... additional methods } ``` ### useTransitionPlayback ```typescript export type ReverseMode = 'none' | 'reverse' | 'separate'; export interface TransitionConfig { videoUrl: string; storageKey?: string; reverseMode: ReverseMode; reverseVideoUrl?: string; durationSec?: number; targetPageId?: string; displayName?: string; } export interface UseTransitionPlaybackOptions { videoRef: RefObject; transition: TransitionConfig | null; onComplete: (targetPageId?: string) => void; onError?: (reason: string) => void; timeouts?: { ... }; features?: { ... }; preload?: { ... }; } export type PlaybackPhase = | 'idle' | 'preparing' | 'playing' | 'reversing' | 'finishing' | 'completed'; export interface UseTransitionPlaybackResult { phase: PlaybackPhase; isBuffering: boolean; isReversing: boolean; cancel: () => void; forceComplete: () => void; } ``` --- ## Type Utilities ### Type Guards ```typescript // Canvas element type guard export function isCanvasElementType(value: string): value is CanvasElementType { return CANVAS_ELEMENT_TYPES.includes(value as CanvasElementType); } ``` ### Normalization Functions ```typescript // Normalize element default from API export function normalizeElementDefault( row: Record, ): NormalizedElementDefault | null; // Build defaults map export function buildElementDefaultsMap( defaults: NormalizedElementDefault[], ): Partial>>; ``` ### Value Helpers (ElementSettings) ```typescript export const clampPercent = (value: string): number; export const parseNullableNumber = (value: string): number | null; export const extractNumericValue = (value: unknown): string; export const normalizeNumberString = (value: string): string; export const toOptionalTrimmed = (value: string): string | undefined; export const toUnitValue = (value: string, unit: 'vw' | 'vh' | 'px'): string | undefined; ``` --- ## Import Patterns ### Central Index Exports The central `types/index.ts` re-exports all core type modules: ```typescript export * from './entities'; export * from './api'; export * from './redux'; export * from './forms'; export * from './filters'; export * from './permissions'; export * from './offline'; export * from './preload'; export * from './runtime'; export * from './constructor'; // Note: presentation.ts is NOT exported from index ``` ### Central Import ```typescript // Import all types from central index import { User, Project, Asset, PaginatedResponse, Permission, PreloadJob, CanvasElement, } from '@/types'; ``` ### Domain-Specific Import ```typescript // Import from specific modules import { CanvasElement, CanvasElementType } from '@/types/constructor'; import { PreloadJob, OfflineProject } from '@/types/offline'; import { RuntimePage, RuntimeTransition } from '@/types/runtime'; ``` ### Component Types Import ```typescript // Import component-specific types import type { ConstructorCanvasProps, ElementEditorPanelProps, } from '@/components/Constructor/types'; ``` --- ## File Inventory | File | LOC | Interfaces | Types | Enums | |------|-----|------------|-------|-------| | `types/entities.ts` | 276 | 18 | 2 | 0 | | `types/api.ts` | 62 | 9 | 0 | 0 | | `types/redux.ts` | 60 | 7 | 0 | 0 | | `types/forms.ts` | 76 | 5 | 2 | 0 | | `types/filters.ts` | 74 | 6 | 2 | 0 | | `types/permissions.ts` | 122 | 0 | 1 | 1 | | `types/offline.ts` | 194 | 16 | 3 | 0 | | `types/preload.ts` | 57 | 5 | 0 | 0 | | `types/runtime.ts` | 80 | 7 | 0 | 0 | | `types/constructor.ts` | 418 | 25 | 2 | 0 | | `types/presentation.ts` | 107 | 9 | 1 | 0 | | `types/index.ts` | 14 | 0 | 0 | 0 | | `interfaces/index.ts` | 122 | 4 | 10 | 0 | | `components/Constructor/types.ts` | 312 | 20 | 3 | 0 | | `components/ElementSettings/types.ts` | 293 | 12 | 1 | 0 | | `lib/elementStyles.ts` | 146 | 1 | 1 | 0 | | `lib/elementEffects.ts` | 267 | 1 | 2 | 0 | | **Total** | **~2,680** | **~145** | **~30** | **1** | --- ## Best Practices ### 1. Use Central Exports ```typescript // Good - import from central index import { User, Project, Asset } from '@/types'; // Avoid - importing from individual files for common types import { User } from '@/types/entities'; import { Project } from '@/types/entities'; ``` ### 2. Extend Base Entity ```typescript // Good - extend BaseEntity export interface NewEntity extends BaseEntity { customField: string; } // Avoid - duplicating id/createdAt/updatedAt export interface NewEntity { id: string; createdAt: string; // ... } ``` ### 3. Use Const Assertions for Enums ```typescript // Good - const assertion for type narrowing export const CANVAS_ELEMENT_TYPES = [...] as const; export type CanvasElementType = (typeof CANVAS_ELEMENT_TYPES)[number]; // Works well with type guards function isCanvasElementType(value: string): value is CanvasElementType { return CANVAS_ELEMENT_TYPES.includes(value as CanvasElementType); } ``` ### 4. Component Props Interfaces ```typescript // Good - clear interface name with Props suffix export interface CanvasElementProps { element: CanvasElement; isSelected: boolean; onClick: (element: CanvasElement) => void; } // Use in component function CanvasElementComponent({ element, isSelected, onClick }: CanvasElementProps) { // ... } ``` ### 5. Generic Types for Reusability ```typescript // Good - generic interfaces export interface EntitySliceState { data: T[]; loading: boolean; count: number; } // Usage type UsersState = EntitySliceState; type ProjectsState = EntitySliceState; ``` --- ## Related Documentation - [types-module.md](./types-module.md) - Older types documentation - [stores-module.md](./stores-module.md) - Redux state types usage - [factories-module.md](./factories-module.md) - Generic type usage in factories - [hooks-module.md](./hooks-module.md) - Hook interface definitions - [components-module.md](./components-module.md) - Component props types