40 KiB
40 KiB
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 typesfrontend/src/components/*/types.ts- Component-specific typesfrontend/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:
export interface BaseEntity {
id: string;
createdAt?: string;
updatedAt?: string;
}
Core Entities
User
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
export interface Project extends BaseEntity {
name: string;
slug?: string;
description?: string;
logo_url?: string;
favicon_url?: string;
og_image_url?: string;
}
Asset
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
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
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
// Paginated list response
export interface PaginatedResponse<T> {
rows: T[];
count: number;
}
// API error structure
export interface ApiError {
message?: string;
statusCode?: number;
errors?: Record<string, { _errors: string[] }>;
}
Request Types
// 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
// Create/update payload
export interface MutationPayload<T> {
id?: string;
data: Partial<T>;
}
// 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
export interface NotificationState {
showNotification: boolean;
textNotification: string;
typeNotification: 'warn' | 'error' | 'success' | 'info' | '';
}
Entity Slice State
// Generic entity state (used by createEntitySlice)
export interface EntitySliceState<T> {
data: T[];
loading: boolean;
count: number;
refetch: boolean;
notify: NotificationState;
}
// Legacy entity slice state (for backward compatibility during migration)
export interface LegacyEntitySliceState<T> {
[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
// 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
export type FormFieldType =
| 'text' | 'email' | 'number' | 'password'
| 'textarea' | 'richtext'
| 'select' | 'multiselect'
| 'date' | 'datetime'
| 'boolean' | 'switch'
| 'image' | 'file' | 'enum';
Field Configuration
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
export interface FormPageConfig<TFormData> {
entityName: string;
entityTitle: string;
mode: 'create' | 'edit';
permission: string;
initialValues: TFormData;
fields: FormFieldConfig[];
listPath?: string;
}
export type FormSubmitHandler<T> = (values: T) => Promise<void>;
export type FormValues = Record<string, unknown>;
Filter Interfaces (types/filters.ts)
Filter Configuration
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<string, string | string[]>;
Table Configuration
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
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
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
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
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
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
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
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
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
// 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<string, Array<{ pageId: string; distance: number }>>;
// 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
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
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
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
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
export interface TransitionOverlayState {
targetPageId: string;
transitionName: string;
videoUrl: string;
isReverse: boolean;
durationSec?: number;
}
Constructor Interfaces (types/constructor.ts)
Element Types
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
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
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
export interface ConstructorSchema {
elements?: CanvasElement[];
}
Asset Types
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
export interface UiElementDefault {
id: string;
element_type?: string;
is_active?: boolean;
default_settings_json?: string | Record<string, unknown>;
}
export interface NormalizedElementDefault {
id: string;
element_type: string;
name?: string;
sort_order?: number;
settings: Partial<CanvasElement>;
}
Editor Props Interfaces
export interface EditorLayoutProps {
elementEditorRef: React.RefObject<HTMLDivElement | null>;
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<CanvasElement>) => 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<GalleryCard>) => 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<CarouselSlide>) => void;
remove: (slideId: string) => void;
};
}
export interface EditorMediaUtilsProps {
getDuration: (url: string) => number | undefined;
}
Presentation Interfaces (types/presentation.ts)
State Types
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
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<void>;
}
// Runtime project (duplicate in presentation.ts)
export interface RuntimeProject {
id: string;
name?: string;
slug?: string;
description?: string;
}
Navigation Types
export interface NavigableElement {
id: string;
type: string;
targetPageSlug?: string;
targetPageId?: string;
transitionVideoUrl?: string;
navType?: 'forward' | 'back';
navDisabled?: boolean;
}
Transition Types
export type TransitionPhase =
| 'idle'
| 'preparing'
| 'playing'
| 'reversing'
| 'completed';
export interface BackgroundTransitionOptions {
pendingTransitionComplete: boolean;
isBackgroundReady: boolean;
transitionVideoRef: React.RefObject<HTMLVideoElement | null>;
pageSwitch: {
clearPreviousBackground: () => void;
isSwitching: boolean;
isNewBgReady: boolean;
previousBgImageUrl: string;
};
onCleanup: () => void;
}
UI/Menu Interfaces (interfaces/index.ts)
Menu Types
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
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
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
export type AppearAnimationType =
| '' | 'fade' | 'slide-up' | 'slide-down'
| 'slide-left' | 'slide-right' | 'scale';
Effect Properties
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)
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<HTMLDivElement>;
elements: CanvasElement[];
selectedElementId: string;
isEditMode: boolean;
isLoading: boolean;
canvasElapsedSec: number;
preloadedIconUrlMap: Record<string, boolean>;
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)
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)
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
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<string | null>;
preloadedUrls?: Set<string>;
}
export interface UsePageSwitchOptions {
preloadCache?: PreloadCacheProvider;
}
export interface UsePageSwitchResult {
currentBgImageUrl: string;
currentBgVideoUrl: string;
currentBgAudioUrl: string;
previousBgImageUrl: string;
isSwitching: boolean;
isNewBgReady: boolean;
// ... additional methods
}
useTransitionPlayback
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<HTMLVideoElement | null>;
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
// Canvas element type guard
export function isCanvasElementType(value: string): value is CanvasElementType {
return CANVAS_ELEMENT_TYPES.includes(value as CanvasElementType);
}
Normalization Functions
// Normalize element default from API
export function normalizeElementDefault(
row: Record<string, unknown>,
): NormalizedElementDefault | null;
// Build defaults map
export function buildElementDefaultsMap(
defaults: NormalizedElementDefault[],
): Partial<Record<CanvasElementType, Partial<CanvasElement>>>;
Value Helpers (ElementSettings)
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:
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
// Import all types from central index
import {
User,
Project,
Asset,
PaginatedResponse,
Permission,
PreloadJob,
CanvasElement,
} from '@/types';
Domain-Specific Import
// 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
// 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
// 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
// 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
// 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
// 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
// Good - generic interfaces
export interface EntitySliceState<T> {
data: T[];
loading: boolean;
count: number;
}
// Usage
type UsersState = EntitySliceState<User>;
type ProjectsState = EntitySliceState<Project>;
Related Documentation
- types-module.md - Older types documentation
- stores-module.md - Redux state types usage
- factories-module.md - Generic type usage in factories
- hooks-module.md - Hook interface definitions
- components-module.md - Component props types