39948-vm/frontend/docs/interfaces-module.md
2026-07-03 16:11:24 +02:00

40 KiB
Raw Blame History

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:

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>;