From baef1fca2f2ebda1b3908e74219a1e4e821d9159 Mon Sep 17 00:00:00 2001 From: Dmitri Date: Fri, 27 Mar 2026 09:51:33 +0400 Subject: [PATCH] UI elements settings extention --- backend/README.md | 26 + frontend/README.md | 8 + .../CarouselSettingsSection.tsx | 163 ++ .../ElementSettings/CommonSettingsSection.tsx | 68 + .../DescriptionSettingsSection.tsx | 139 ++ .../ElementSettings/ElementSettingsTabs.tsx | 64 + .../GallerySettingsSection.tsx | 112 + .../ElementSettings/MediaSettingsSection.tsx | 89 + .../NavigationSettingsSection.tsx | 188 ++ .../ElementSettings/StyleSettingsSection.tsx | 232 +++ .../StyleSettingsSectionCompact.tsx | 321 +++ .../TooltipSettingsSection.tsx | 64 + .../src/components/ElementSettings/index.ts | 27 + .../src/components/ElementSettings/types.ts | 235 +++ .../ElementSettings/useElementSettingsForm.ts | 582 ++++++ .../src/components/RuntimePresentation.tsx | 20 +- frontend/src/hooks/usePageNavigation.ts | 7 +- frontend/src/hooks/usePageSwitch.ts | 10 +- frontend/src/hooks/usePreloadOrchestrator.ts | 7 +- frontend/src/hooks/useTransitionPlayback.ts | 5 +- frontend/src/layouts/Authenticated.tsx | 17 +- frontend/src/pages/constructor.tsx | 1806 +++++++++-------- .../src/pages/element-type-defaults/[id].tsx | 1346 ++---------- .../pages/project-element-defaults/[id].tsx | 266 ++- frontend/src/pages/projects/projects-view.tsx | 1 - 25 files changed, 3760 insertions(+), 2043 deletions(-) create mode 100644 frontend/src/components/ElementSettings/CarouselSettingsSection.tsx create mode 100644 frontend/src/components/ElementSettings/CommonSettingsSection.tsx create mode 100644 frontend/src/components/ElementSettings/DescriptionSettingsSection.tsx create mode 100644 frontend/src/components/ElementSettings/ElementSettingsTabs.tsx create mode 100644 frontend/src/components/ElementSettings/GallerySettingsSection.tsx create mode 100644 frontend/src/components/ElementSettings/MediaSettingsSection.tsx create mode 100644 frontend/src/components/ElementSettings/NavigationSettingsSection.tsx create mode 100644 frontend/src/components/ElementSettings/StyleSettingsSection.tsx create mode 100644 frontend/src/components/ElementSettings/StyleSettingsSectionCompact.tsx create mode 100644 frontend/src/components/ElementSettings/TooltipSettingsSection.tsx create mode 100644 frontend/src/components/ElementSettings/index.ts create mode 100644 frontend/src/components/ElementSettings/types.ts create mode 100644 frontend/src/components/ElementSettings/useElementSettingsForm.ts diff --git a/backend/README.md b/backend/README.md index c9aa366..7714caf 100644 --- a/backend/README.md +++ b/backend/README.md @@ -199,6 +199,32 @@ DELETE /api/{entity}/:id # Soft delete record | `permissions` | Granular permissions | | `project_memberships` | Team access per project | +### Element Defaults Hierarchy + +UI elements use a three-tier defaults system: + +``` +element_type_defaults (Global) + │ + │ auto-snapshot on project creation + ▼ +project_element_defaults (Project) + │ + │ applied when creating elements + ▼ +tour_pages.ui_schema_json (Instance) +``` + +1. **Global** (`element_type_defaults`) - Platform-wide defaults for 11 element types (navigation, tooltip, gallery, etc.). Auto-seeded on first API access. + +2. **Project** (`project_element_defaults`) - Per-project overrides. Automatically snapshotted from global when a project is created. Can be customized independently. + +3. **Instance** (`tour_pages.ui_schema_json`) - Page-specific elements with their settings stored inline. Created in constructor with project defaults applied. + +**Additional Endpoints:** +- `POST /api/project-element-defaults/:id/reset` - Reset to current global default +- `GET /api/project-element-defaults/:id/diff` - Compare with global default + ### Publishing Workflow Three-tier environment model for content: `dev` → `stage` → `production` diff --git a/frontend/README.md b/frontend/README.md index 3dba6b0..dd61ca0 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -70,6 +70,7 @@ frontend/src/ ├── components/ # React components (PascalCase) │ ├── Assets/ # Asset management components │ ├── Constructor/ # Tour builder components +│ ├── ElementSettings/ # Shared element settings form components │ ├── UiElements/ # Element type components │ ├── Generic/ # Generic CRUD components │ ├── CardBox.tsx # Card container @@ -214,6 +215,7 @@ dispatch(create({ data: newProject })); | `useOfflineMode` | Detect offline/online status | | `usePWAPreload` | Preload assets for offline | | `useStorageQuota` | Monitor IndexedDB usage | +| `useElementSettingsForm` | Element settings form state (~60 fields) | ## Element Types @@ -243,7 +245,13 @@ Element types follow a three-tier defaults system: The constructor fetches project defaults via `/api/project-element-defaults?projectId=xxx` and merges them when creating new elements. Existing element values in `ui_schema_json` take precedence over defaults. +**Admin Pages:** +- `/element-type-defaults` - Edit global defaults (platform-wide) +- `/project-element-defaults/[id]` - Edit project defaults (with Reset to Global, Diff indicator) + **Key files:** +- `components/ElementSettings/` - Shared form components (tabs, CSS styling, type-specific sections) +- `components/ElementSettings/useElementSettingsForm.ts` - Form state hook (~60 fields) - `types/constructor.ts` - `normalizeElementDefault()`, `buildElementDefaultsMap()` utilities - `pages/constructor.tsx` - Loads project defaults, creates elements with merged settings diff --git a/frontend/src/components/ElementSettings/CarouselSettingsSection.tsx b/frontend/src/components/ElementSettings/CarouselSettingsSection.tsx new file mode 100644 index 0000000..691e34f --- /dev/null +++ b/frontend/src/components/ElementSettings/CarouselSettingsSection.tsx @@ -0,0 +1,163 @@ +/** + * CarouselSettingsSection + * + * Settings for carousel element type. + * Manages carousel slides with images and captions. + */ + +import React from 'react'; +import { mdiPlus, mdiTrashCan } from '@mdi/js'; +import BaseButton from '../BaseButton'; +import CardBox from '../CardBox'; +import FormField from '../FormField'; +import type { CarouselSettingsSectionProps } from './types'; + +const CarouselSettingsSection: React.FC = ({ + carouselPrevIconUrl, + carouselNextIconUrl, + carouselSlides, + onAddSlide, + onRemoveSlide, + onUpdateSlide, + onChange, + context, + iconAssetOptions = [], + imageAssetOptions = [], +}) => { + const isConstructor = context === 'constructor'; + + return ( + +

Carousel settings

+ +
+ {isConstructor ? ( + <> + + + + + + + + ) : ( + <> + + + onChange('carouselPrevIconUrl', event.target.value) + } + /> + + + + onChange('carouselNextIconUrl', event.target.value) + } + /> + + + )} +
+ +
+

Carousel slides

+ +
+ +
+ {carouselSlides.length === 0 ? ( +

No slides yet.

+ ) : ( + carouselSlides.map((slide, index) => ( + +
+

Slide {index + 1}

+ onRemoveSlide(slide.id)} + /> +
+
+ {isConstructor ? ( + + + + ) : ( + + + onUpdateSlide(slide.id, 'imageUrl', event.target.value) + } + /> + + )} + + + onUpdateSlide(slide.id, 'caption', event.target.value) + } + /> + +
+
+ )) + )} +
+
+ ); +}; + +export default CarouselSettingsSection; diff --git a/frontend/src/components/ElementSettings/CommonSettingsSection.tsx b/frontend/src/components/ElementSettings/CommonSettingsSection.tsx new file mode 100644 index 0000000..f774333 --- /dev/null +++ b/frontend/src/components/ElementSettings/CommonSettingsSection.tsx @@ -0,0 +1,68 @@ +/** + * CommonSettingsSection + * + * Common settings fields shared by all element types. + * Label, position, and appear timing. + */ + +import React from 'react'; +import FormField from '../FormField'; +import type { CommonSettingsSectionProps } from './types'; + +const CommonSettingsSection: React.FC = ({ + label, + xPercent, + yPercent, + appearDelaySec, + appearDurationSec, + onChange, + showPosition = true, +}) => { + return ( +
+ + onChange('label', event.target.value)} + /> + + + {showPosition && ( +
+ + onChange('xPercent', event.target.value)} + /> + + + onChange('yPercent', event.target.value)} + /> + +
+ )} + +
+ + onChange('appearDelaySec', event.target.value)} + /> + + + + onChange('appearDurationSec', event.target.value) + } + placeholder='Leave empty for none' + /> + +
+
+ ); +}; + +export default CommonSettingsSection; diff --git a/frontend/src/components/ElementSettings/DescriptionSettingsSection.tsx b/frontend/src/components/ElementSettings/DescriptionSettingsSection.tsx new file mode 100644 index 0000000..385c762 --- /dev/null +++ b/frontend/src/components/ElementSettings/DescriptionSettingsSection.tsx @@ -0,0 +1,139 @@ +/** + * DescriptionSettingsSection + * + * Settings for description element type. + */ + +import React from 'react'; +import FormField from '../FormField'; +import type { DescriptionSettingsSectionProps } from './types'; + +const DescriptionSettingsSection: React.FC = ({ + iconUrl, + descriptionTitle, + descriptionText, + descriptionTitleFontSize, + descriptionTextFontSize, + descriptionTitleFontFamily, + descriptionTextFontFamily, + descriptionTitleColor, + descriptionTextColor, + descriptionBackgroundColor, + onChange, + context, + iconAssetOptions = [], +}) => { + const isConstructor = context === 'constructor'; + + return ( +
+ {isConstructor ? ( + + + + ) : ( + + onChange('iconUrl', event.target.value)} + /> + + )} + + + onChange('descriptionTitle', event.target.value)} + /> + + + +