@actuate-media/cms-admin 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +44 -42
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/lib/search.test.js +10 -10
- package/dist/__tests__/lib/search.test.js.map +1 -1
- package/dist/__tests__/lib/utils.test.js.map +1 -1
- package/dist/__tests__/router/match-route.test.js.map +1 -1
- package/dist/__tests__/router/strip-base.test.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/components/Breadcrumbs.d.ts.map +1 -1
- package/dist/components/Breadcrumbs.js +2 -4
- package/dist/components/Breadcrumbs.js.map +1 -1
- package/dist/components/CommandPalette.d.ts.map +1 -1
- package/dist/components/CommandPalette.js +7 -3
- package/dist/components/CommandPalette.js.map +1 -1
- package/dist/components/ContentOverviewChart.d.ts.map +1 -1
- package/dist/components/ContentOverviewChart.js.map +1 -1
- package/dist/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/ErrorBoundary.js.map +1 -1
- package/dist/components/FocalPointPicker.d.ts.map +1 -1
- package/dist/components/FocalPointPicker.js +4 -2
- package/dist/components/FocalPointPicker.js.map +1 -1
- package/dist/components/FolderTree.d.ts.map +1 -1
- package/dist/components/FolderTree.js +18 -10
- package/dist/components/FolderTree.js.map +1 -1
- package/dist/components/LivePreview.d.ts +1 -1
- package/dist/components/LivePreview.d.ts.map +1 -1
- package/dist/components/LivePreview.js +6 -2
- package/dist/components/LivePreview.js.map +1 -1
- package/dist/components/LocaleProvider.d.ts.map +1 -1
- package/dist/components/LocaleProvider.js.map +1 -1
- package/dist/components/LocaleSwitcher.d.ts.map +1 -1
- package/dist/components/LocaleSwitcher.js +1 -1
- package/dist/components/LocaleSwitcher.js.map +1 -1
- package/dist/components/MediaPickerModal.d.ts.map +1 -1
- package/dist/components/MediaPickerModal.js.map +1 -1
- package/dist/components/PresenceIndicator.d.ts.map +1 -1
- package/dist/components/PresenceIndicator.js +5 -2
- package/dist/components/PresenceIndicator.js.map +1 -1
- package/dist/components/SEOPanel.d.ts +1 -1
- package/dist/components/SEOPanel.d.ts.map +1 -1
- package/dist/components/SEOPanel.js +110 -24
- package/dist/components/SEOPanel.js.map +1 -1
- package/dist/components/SEOPerformance.d.ts.map +1 -1
- package/dist/components/SEOPerformance.js +2 -2
- package/dist/components/SEOPerformance.js.map +1 -1
- package/dist/components/ThemeProvider.d.ts.map +1 -1
- package/dist/components/ThemeProvider.js.map +1 -1
- package/dist/components/TipTapEditor.d.ts.map +1 -1
- package/dist/components/TipTapEditor.js +5 -1
- package/dist/components/TipTapEditor.js.map +1 -1
- package/dist/components/VersionHistory.d.ts +1 -1
- package/dist/components/VersionHistory.d.ts.map +1 -1
- package/dist/components/VersionHistory.js +1 -1
- package/dist/components/VersionHistory.js.map +1 -1
- package/dist/components/ui/Avatar.d.ts.map +1 -1
- package/dist/components/ui/Avatar.js.map +1 -1
- package/dist/components/ui/Badge.d.ts.map +1 -1
- package/dist/components/ui/Badge.js.map +1 -1
- package/dist/components/ui/Button.d.ts.map +1 -1
- package/dist/components/ui/Button.js.map +1 -1
- package/dist/components/ui/CommandPalette.d.ts.map +1 -1
- package/dist/components/ui/CommandPalette.js +8 -2
- package/dist/components/ui/CommandPalette.js.map +1 -1
- package/dist/components/ui/ConfirmDialog.d.ts.map +1 -1
- package/dist/components/ui/ConfirmDialog.js.map +1 -1
- package/dist/components/ui/DataTable.d.ts.map +1 -1
- package/dist/components/ui/DataTable.js +1 -3
- package/dist/components/ui/DataTable.js.map +1 -1
- package/dist/components/ui/EmptyState.d.ts.map +1 -1
- package/dist/components/ui/EmptyState.js +1 -1
- package/dist/components/ui/EmptyState.js.map +1 -1
- package/dist/components/ui/Modal.d.ts.map +1 -1
- package/dist/components/ui/Modal.js.map +1 -1
- package/dist/components/ui/Pagination.d.ts +1 -1
- package/dist/components/ui/Pagination.d.ts.map +1 -1
- package/dist/components/ui/Pagination.js +7 -2
- package/dist/components/ui/Pagination.js.map +1 -1
- package/dist/components/ui/SearchInput.d.ts.map +1 -1
- package/dist/components/ui/SearchInput.js.map +1 -1
- package/dist/components/ui/Skeleton.d.ts.map +1 -1
- package/dist/components/ui/Skeleton.js.map +1 -1
- package/dist/components/ui/Toast.d.ts.map +1 -1
- package/dist/components/ui/Toast.js.map +1 -1
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js.map +1 -1
- package/dist/fields/ArrayField.d.ts.map +1 -1
- package/dist/fields/ArrayField.js +1 -1
- package/dist/fields/ArrayField.js.map +1 -1
- package/dist/fields/BlockBuilderField.d.ts.map +1 -1
- package/dist/fields/BlockBuilderField.js +7 -7
- package/dist/fields/BlockBuilderField.js.map +1 -1
- package/dist/fields/DateField.d.ts.map +1 -1
- package/dist/fields/DateField.js +1 -1
- package/dist/fields/DateField.js.map +1 -1
- package/dist/fields/FieldRenderer.d.ts.map +1 -1
- package/dist/fields/FieldRenderer.js.map +1 -1
- package/dist/fields/GroupField.d.ts.map +1 -1
- package/dist/fields/GroupField.js +1 -1
- package/dist/fields/GroupField.js.map +1 -1
- package/dist/fields/MediaField.d.ts.map +1 -1
- package/dist/fields/MediaField.js +1 -1
- package/dist/fields/MediaField.js.map +1 -1
- package/dist/fields/NavBuilderField.d.ts.map +1 -1
- package/dist/fields/NavBuilderField.js +2 -5
- package/dist/fields/NavBuilderField.js.map +1 -1
- package/dist/fields/NumberField.d.ts +1 -1
- package/dist/fields/NumberField.d.ts.map +1 -1
- package/dist/fields/NumberField.js +2 -2
- package/dist/fields/NumberField.js.map +1 -1
- package/dist/fields/RelationshipField.d.ts.map +1 -1
- package/dist/fields/RelationshipField.js +7 -3
- package/dist/fields/RelationshipField.js.map +1 -1
- package/dist/fields/RichTextField.d.ts +1 -1
- package/dist/fields/RichTextField.d.ts.map +1 -1
- package/dist/fields/RichTextField.js +2 -2
- package/dist/fields/RichTextField.js.map +1 -1
- package/dist/fields/SelectField.d.ts.map +1 -1
- package/dist/fields/SelectField.js +9 -7
- package/dist/fields/SelectField.js.map +1 -1
- package/dist/fields/SlugField.d.ts.map +1 -1
- package/dist/fields/SlugField.js +1 -1
- package/dist/fields/SlugField.js.map +1 -1
- package/dist/fields/TextField.d.ts +1 -1
- package/dist/fields/TextField.d.ts.map +1 -1
- package/dist/fields/TextField.js +2 -2
- package/dist/fields/TextField.js.map +1 -1
- package/dist/fields/ToggleField.d.ts.map +1 -1
- package/dist/fields/ToggleField.js +1 -1
- package/dist/fields/ToggleField.js.map +1 -1
- package/dist/fields/block-types.d.ts.map +1 -1
- package/dist/fields/block-types.js +28 -8
- package/dist/fields/block-types.js.map +1 -1
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js.map +1 -1
- package/dist/hooks/useBuilderState.d.ts.map +1 -1
- package/dist/hooks/useBuilderState.js.map +1 -1
- package/dist/hooks/useContentLock.d.ts.map +1 -1
- package/dist/hooks/useContentLock.js.map +1 -1
- package/dist/hooks/useDebounce.js.map +1 -1
- package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -1
- package/dist/hooks/useKeyboardShortcuts.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout/Header.d.ts.map +1 -1
- package/dist/layout/Header.js.map +1 -1
- package/dist/layout/Layout.d.ts.map +1 -1
- package/dist/layout/Layout.js.map +1 -1
- package/dist/layout/Sidebar.d.ts +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +5 -8
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/lib/search.js +3 -5
- package/dist/lib/search.js.map +1 -1
- package/dist/lib/useApiData.d.ts.map +1 -1
- package/dist/lib/useApiData.js.map +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +1 -3
- package/dist/router/index.js.map +1 -1
- package/dist/views/CollectionList.d.ts.map +1 -1
- package/dist/views/CollectionList.js +56 -17
- package/dist/views/CollectionList.js.map +1 -1
- package/dist/views/Dashboard.d.ts.map +1 -1
- package/dist/views/Dashboard.js +26 -13
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/DocumentEdit.d.ts +1 -1
- package/dist/views/DocumentEdit.d.ts.map +1 -1
- package/dist/views/DocumentEdit.js +33 -15
- package/dist/views/DocumentEdit.js.map +1 -1
- package/dist/views/ForgotPassword.d.ts.map +1 -1
- package/dist/views/ForgotPassword.js.map +1 -1
- package/dist/views/FormEditor.d.ts.map +1 -1
- package/dist/views/FormEditor.js +8 -2
- package/dist/views/FormEditor.js.map +1 -1
- package/dist/views/FormSubmissions.d.ts.map +1 -1
- package/dist/views/FormSubmissions.js +6 -6
- package/dist/views/FormSubmissions.js.map +1 -1
- package/dist/views/Forms.d.ts.map +1 -1
- package/dist/views/Forms.js.map +1 -1
- package/dist/views/Login.d.ts.map +1 -1
- package/dist/views/Login.js +5 -2
- package/dist/views/Login.js.map +1 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +39 -19
- package/dist/views/MediaBrowser.js.map +1 -1
- package/dist/views/PageEditor.d.ts.map +1 -1
- package/dist/views/PageEditor.js.map +1 -1
- package/dist/views/Pages.d.ts.map +1 -1
- package/dist/views/Pages.js +20 -10
- package/dist/views/Pages.js.map +1 -1
- package/dist/views/PostEditor.d.ts.map +1 -1
- package/dist/views/PostEditor.js.map +1 -1
- package/dist/views/Posts.d.ts.map +1 -1
- package/dist/views/Posts.js +13 -7
- package/dist/views/Posts.js.map +1 -1
- package/dist/views/Redirects.d.ts.map +1 -1
- package/dist/views/Redirects.js +17 -5
- package/dist/views/Redirects.js.map +1 -1
- package/dist/views/ResetPassword.d.ts.map +1 -1
- package/dist/views/ResetPassword.js.map +1 -1
- package/dist/views/SEO.d.ts.map +1 -1
- package/dist/views/SEO.js +40 -17
- package/dist/views/SEO.js.map +1 -1
- package/dist/views/ScriptTagEditor.d.ts.map +1 -1
- package/dist/views/ScriptTagEditor.js +2 -1
- package/dist/views/ScriptTagEditor.js.map +1 -1
- package/dist/views/ScriptTags.d.ts.map +1 -1
- package/dist/views/ScriptTags.js.map +1 -1
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +38 -11
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/SetupWizard.d.ts.map +1 -1
- package/dist/views/SetupWizard.js.map +1 -1
- package/dist/views/Users.d.ts.map +1 -1
- package/dist/views/Users.js +5 -3
- package/dist/views/Users.js.map +1 -1
- package/dist/views/page-builder/AIBlockAssist.d.ts.map +1 -1
- package/dist/views/page-builder/AIBlockAssist.js +1 -1
- package/dist/views/page-builder/AIBlockAssist.js.map +1 -1
- package/dist/views/page-builder/AIGenerateDialog.d.ts.map +1 -1
- package/dist/views/page-builder/AIGenerateDialog.js +4 -1
- package/dist/views/page-builder/AIGenerateDialog.js.map +1 -1
- package/dist/views/page-builder/BlockEditor.d.ts.map +1 -1
- package/dist/views/page-builder/BlockEditor.js +1 -1
- package/dist/views/page-builder/BlockEditor.js.map +1 -1
- package/dist/views/page-builder/BlockPicker.d.ts.map +1 -1
- package/dist/views/page-builder/BlockPicker.js.map +1 -1
- package/dist/views/page-builder/BottomBar.d.ts.map +1 -1
- package/dist/views/page-builder/BottomBar.js.map +1 -1
- package/dist/views/page-builder/BuilderToolbar.d.ts.map +1 -1
- package/dist/views/page-builder/BuilderToolbar.js.map +1 -1
- package/dist/views/page-builder/ContextPanel.d.ts.map +1 -1
- package/dist/views/page-builder/ContextPanel.js +4 -1
- package/dist/views/page-builder/ContextPanel.js.map +1 -1
- package/dist/views/page-builder/DesignScore.d.ts.map +1 -1
- package/dist/views/page-builder/DesignScore.js.map +1 -1
- package/dist/views/page-builder/NodeSettings.d.ts.map +1 -1
- package/dist/views/page-builder/NodeSettings.js +1 -1
- package/dist/views/page-builder/NodeSettings.js.map +1 -1
- package/dist/views/page-builder/PageBuilder.d.ts +1 -1
- package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
- package/dist/views/page-builder/PageBuilder.js +4 -2
- package/dist/views/page-builder/PageBuilder.js.map +1 -1
- package/dist/views/page-builder/PageSettings.d.ts.map +1 -1
- package/dist/views/page-builder/PageSettings.js.map +1 -1
- package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
- package/dist/views/page-builder/PageTemplates.js.map +1 -1
- package/dist/views/page-builder/SEOPanel.d.ts.map +1 -1
- package/dist/views/page-builder/SEOPanel.js +1 -3
- package/dist/views/page-builder/SEOPanel.js.map +1 -1
- package/dist/views/page-builder/SavedSections.d.ts.map +1 -1
- package/dist/views/page-builder/SavedSections.js +3 -7
- package/dist/views/page-builder/SavedSections.js.map +1 -1
- package/dist/views/page-builder/TemplatePicker.d.ts.map +1 -1
- package/dist/views/page-builder/TemplatePicker.js.map +1 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/CodePreview.js +1 -5
- package/dist/views/page-builder/block-renderers/CodePreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js +4 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/FormPreview.js +2 -2
- package/dist/views/page-builder/block-renderers/FormPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +1 -3
- package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/HeroPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/ImagePreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/TextPreview.js +2 -6
- package/dist/views/page-builder/block-renderers/TextPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/VideoPreview.js +2 -5
- package/dist/views/page-builder/block-renderers/VideoPreview.js.map +1 -1
- package/dist/views/page-builder/block-renderers/index.d.ts.map +1 -1
- package/dist/views/page-builder/block-renderers/index.js.map +1 -1
- package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/BlockRenderer.js +1 -5
- package/dist/views/page-builder/canvas/BlockRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.js.map +1 -1
- package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/ColumnRenderer.js +1 -5
- package/dist/views/page-builder/canvas/ColumnRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/ContainerRenderer.js +1 -5
- package/dist/views/page-builder/canvas/ContainerRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/RowRenderer.js +1 -5
- package/dist/views/page-builder/canvas/RowRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/SectionRenderer.js +1 -5
- package/dist/views/page-builder/canvas/SectionRenderer.js.map +1 -1
- package/dist/views/page-builder/canvas/index.d.ts.map +1 -1
- package/dist/views/page-builder/canvas/index.js.map +1 -1
- package/package.json +2 -2
- package/src/AdminRoot.tsx +263 -191
- package/src/__tests__/lib/search.test.ts +60 -69
- package/src/__tests__/lib/utils.test.ts +12 -12
- package/src/__tests__/router/match-route.test.ts +24 -26
- package/src/__tests__/router/strip-base.test.ts +15 -15
- package/src/components/Breadcrumbs.tsx +27 -24
- package/src/components/CommandPalette.tsx +115 -99
- package/src/components/ContentOverviewChart.tsx +19 -14
- package/src/components/ErrorBoundary.tsx +13 -13
- package/src/components/FocalPointPicker.tsx +31 -20
- package/src/components/FolderTree.tsx +172 -139
- package/src/components/LivePreview.tsx +68 -41
- package/src/components/LocaleProvider.tsx +26 -20
- package/src/components/LocaleSwitcher.tsx +9 -11
- package/src/components/MediaPickerModal.tsx +46 -45
- package/src/components/PresenceIndicator.tsx +30 -27
- package/src/components/SEOPanel.tsx +378 -228
- package/src/components/SEOPerformance.tsx +52 -30
- package/src/components/ThemeProvider.tsx +46 -46
- package/src/components/TipTapEditor.tsx +60 -64
- package/src/components/VersionHistory.tsx +63 -52
- package/src/components/ui/Avatar.tsx +8 -8
- package/src/components/ui/Badge.tsx +7 -5
- package/src/components/ui/Button.tsx +24 -13
- package/src/components/ui/CommandPalette.tsx +56 -42
- package/src/components/ui/ConfirmDialog.tsx +14 -14
- package/src/components/ui/DataTable.tsx +37 -39
- package/src/components/ui/EmptyState.tsx +9 -11
- package/src/components/ui/Modal.tsx +21 -15
- package/src/components/ui/Pagination.tsx +34 -19
- package/src/components/ui/SearchInput.tsx +17 -7
- package/src/components/ui/Skeleton.tsx +7 -7
- package/src/components/ui/Toast.tsx +29 -22
- package/src/components/ui/index.ts +24 -24
- package/src/fields/ArrayField.tsx +43 -25
- package/src/fields/BlockBuilderField.tsx +80 -99
- package/src/fields/DateField.tsx +20 -12
- package/src/fields/FieldRenderer.tsx +34 -34
- package/src/fields/GroupField.tsx +8 -10
- package/src/fields/MediaField.tsx +8 -10
- package/src/fields/NavBuilderField.tsx +24 -25
- package/src/fields/NumberField.tsx +21 -14
- package/src/fields/RelationshipField.tsx +105 -91
- package/src/fields/RichTextField.tsx +16 -12
- package/src/fields/SelectField.tsx +42 -34
- package/src/fields/SlugField.tsx +29 -17
- package/src/fields/TextField.tsx +24 -16
- package/src/fields/ToggleField.tsx +7 -9
- package/src/fields/block-types.ts +50 -24
- package/src/fields/index.ts +17 -17
- package/src/hooks/useBuilderState.ts +260 -221
- package/src/hooks/useContentLock.ts +23 -20
- package/src/hooks/useDebounce.ts +7 -7
- package/src/hooks/useKeyboardShortcuts.ts +16 -16
- package/src/index.ts +69 -58
- package/src/layout/Header.tsx +21 -20
- package/src/layout/Layout.tsx +22 -24
- package/src/layout/Sidebar.tsx +107 -72
- package/src/lib/api.ts +34 -34
- package/src/lib/search.ts +30 -34
- package/src/lib/useApiData.ts +65 -62
- package/src/lib/utils.ts +3 -3
- package/src/router/index.ts +33 -35
- package/src/styles/build-input.css +2 -2
- package/src/styles/tailwind.css +1 -1
- package/src/styles/theme.css +26 -2
- package/src/views/CollectionList.tsx +275 -121
- package/src/views/Dashboard.tsx +164 -117
- package/src/views/DocumentEdit.tsx +298 -253
- package/src/views/ForgotPassword.tsx +27 -23
- package/src/views/FormEditor.tsx +165 -99
- package/src/views/FormSubmissions.tsx +261 -117
- package/src/views/Forms.tsx +56 -26
- package/src/views/Login.tsx +107 -84
- package/src/views/MediaBrowser.tsx +717 -523
- package/src/views/PageEditor.tsx +44 -46
- package/src/views/Pages.tsx +312 -149
- package/src/views/PostEditor.tsx +57 -51
- package/src/views/Posts.tsx +206 -74
- package/src/views/Redirects.tsx +173 -117
- package/src/views/ResetPassword.tsx +43 -32
- package/src/views/SEO.tsx +607 -160
- package/src/views/ScriptTagEditor.tsx +69 -69
- package/src/views/ScriptTags.tsx +54 -42
- package/src/views/Settings.tsx +430 -220
- package/src/views/SetupWizard.tsx +69 -46
- package/src/views/Users.tsx +154 -120
- package/src/views/page-builder/AIBlockAssist.tsx +21 -25
- package/src/views/page-builder/AIGenerateDialog.tsx +134 -127
- package/src/views/page-builder/BlockEditor.tsx +94 -96
- package/src/views/page-builder/BlockPicker.tsx +73 -88
- package/src/views/page-builder/BottomBar.tsx +15 -11
- package/src/views/page-builder/BuilderToolbar.tsx +32 -29
- package/src/views/page-builder/ContextPanel.tsx +57 -57
- package/src/views/page-builder/DesignScore.tsx +52 -59
- package/src/views/page-builder/NodeSettings.tsx +59 -59
- package/src/views/page-builder/PageBuilder.tsx +156 -155
- package/src/views/page-builder/PageSettings.tsx +16 -15
- package/src/views/page-builder/PageTemplates.tsx +23 -17
- package/src/views/page-builder/SEOPanel.tsx +90 -111
- package/src/views/page-builder/SavedSections.tsx +99 -105
- package/src/views/page-builder/TemplatePicker.tsx +44 -48
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +11 -13
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +13 -15
- package/src/views/page-builder/block-renderers/CodePreview.tsx +16 -16
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +20 -23
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +5 -5
- package/src/views/page-builder/block-renderers/FormPreview.tsx +9 -13
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +22 -28
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +17 -30
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +12 -12
- package/src/views/page-builder/block-renderers/TextPreview.tsx +22 -22
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +13 -18
- package/src/views/page-builder/block-renderers/index.ts +17 -17
- package/src/views/page-builder/canvas/BlockRenderer.tsx +19 -23
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +17 -20
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +22 -26
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +20 -24
- package/src/views/page-builder/canvas/RowRenderer.tsx +19 -23
- package/src/views/page-builder/canvas/SectionRenderer.tsx +30 -34
- package/src/views/page-builder/canvas/index.ts +2 -2
|
@@ -1,75 +1,81 @@
|
|
|
1
|
-
'use client'
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useRef, useEffect, useCallback } from 'react'
|
|
4
|
-
import { Monitor, Tablet, Smartphone, RefreshCw, ExternalLink, X } from 'lucide-react'
|
|
5
|
-
import { cmsApi } from '../lib/api.js'
|
|
3
|
+
import { useState, useRef, useEffect, useCallback } from 'react'
|
|
4
|
+
import { Monitor, Tablet, Smartphone, RefreshCw, ExternalLink, X } from 'lucide-react'
|
|
5
|
+
import { cmsApi } from '../lib/api.js'
|
|
6
6
|
|
|
7
7
|
interface LivePreviewProps {
|
|
8
|
-
collection: string
|
|
9
|
-
documentId?: string
|
|
10
|
-
previewUrl?: string
|
|
11
|
-
values: Record<string, unknown
|
|
12
|
-
onClose: () => void
|
|
8
|
+
collection: string
|
|
9
|
+
documentId?: string
|
|
10
|
+
previewUrl?: string
|
|
11
|
+
values: Record<string, unknown>
|
|
12
|
+
onClose: () => void
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
type Viewport = 'desktop' | 'tablet' | 'mobile'
|
|
15
|
+
type Viewport = 'desktop' | 'tablet' | 'mobile'
|
|
16
16
|
|
|
17
17
|
const VIEWPORT_WIDTHS: Record<Viewport, string> = {
|
|
18
18
|
desktop: '100%',
|
|
19
19
|
tablet: '768px',
|
|
20
20
|
mobile: '375px',
|
|
21
|
-
}
|
|
21
|
+
}
|
|
22
22
|
|
|
23
|
-
export function LivePreview({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
export function LivePreview({
|
|
24
|
+
collection,
|
|
25
|
+
documentId,
|
|
26
|
+
previewUrl,
|
|
27
|
+
values,
|
|
28
|
+
onClose,
|
|
29
|
+
}: LivePreviewProps) {
|
|
30
|
+
const iframeRef = useRef<HTMLIFrameElement>(null)
|
|
31
|
+
const [viewport, setViewport] = useState<Viewport>('desktop')
|
|
32
|
+
const [loading, setLoading] = useState(true)
|
|
33
|
+
const [previewSrc, setPreviewSrc] = useState<string | null>(null)
|
|
28
34
|
|
|
29
35
|
useEffect(() => {
|
|
30
36
|
if (!documentId || !previewUrl) {
|
|
31
|
-
setPreviewSrc(null)
|
|
32
|
-
return
|
|
37
|
+
setPreviewSrc(null)
|
|
38
|
+
return
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
async function fetchToken() {
|
|
36
42
|
const res = await cmsApi<{ token: string }>('/preview/token', {
|
|
37
43
|
method: 'POST',
|
|
38
44
|
body: JSON.stringify({ collection, documentId }),
|
|
39
|
-
})
|
|
45
|
+
})
|
|
40
46
|
if (res.data?.token) {
|
|
41
|
-
const sep = previewUrl!.includes('?') ? '&' : '?'
|
|
42
|
-
setPreviewSrc(`${previewUrl}${sep}preview=true#token=${res.data.token}`)
|
|
47
|
+
const sep = previewUrl!.includes('?') ? '&' : '?'
|
|
48
|
+
setPreviewSrc(`${previewUrl}${sep}preview=true#token=${res.data.token}`)
|
|
43
49
|
}
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
fetchToken()
|
|
47
|
-
}, [collection, documentId, previewUrl])
|
|
52
|
+
fetchToken()
|
|
53
|
+
}, [collection, documentId, previewUrl])
|
|
48
54
|
|
|
49
55
|
useEffect(() => {
|
|
50
|
-
if (!iframeRef.current?.contentWindow || !previewSrc) return
|
|
51
|
-
let targetOrigin: string
|
|
56
|
+
if (!iframeRef.current?.contentWindow || !previewSrc) return
|
|
57
|
+
let targetOrigin: string
|
|
52
58
|
try {
|
|
53
|
-
targetOrigin = new URL(previewSrc).origin
|
|
59
|
+
targetOrigin = new URL(previewSrc).origin
|
|
54
60
|
} catch {
|
|
55
|
-
targetOrigin = window.location.origin
|
|
61
|
+
targetOrigin = window.location.origin
|
|
56
62
|
}
|
|
57
63
|
iframeRef.current.contentWindow.postMessage(
|
|
58
64
|
{ type: 'actuate-preview-update', data: values },
|
|
59
65
|
targetOrigin,
|
|
60
|
-
)
|
|
61
|
-
}, [values, previewSrc])
|
|
66
|
+
)
|
|
67
|
+
}, [values, previewSrc])
|
|
62
68
|
|
|
63
69
|
const handleRefresh = useCallback(() => {
|
|
64
70
|
if (iframeRef.current) {
|
|
65
|
-
setLoading(true)
|
|
66
|
-
iframeRef.current.src = iframeRef.current.src
|
|
71
|
+
setLoading(true)
|
|
72
|
+
iframeRef.current.src = iframeRef.current.src
|
|
67
73
|
}
|
|
68
|
-
}, [])
|
|
74
|
+
}, [])
|
|
69
75
|
|
|
70
76
|
const handleOpenExternal = useCallback(() => {
|
|
71
|
-
if (previewSrc) window.open(previewSrc, '_blank')
|
|
72
|
-
}, [previewSrc])
|
|
77
|
+
if (previewSrc) window.open(previewSrc, '_blank')
|
|
78
|
+
}, [previewSrc])
|
|
73
79
|
|
|
74
80
|
if (!previewSrc) {
|
|
75
81
|
return (
|
|
@@ -83,14 +89,20 @@ export function LivePreview({ collection, documentId, previewUrl, values, onClos
|
|
|
83
89
|
Close Preview
|
|
84
90
|
</button>
|
|
85
91
|
</div>
|
|
86
|
-
)
|
|
92
|
+
)
|
|
87
93
|
}
|
|
88
94
|
|
|
89
95
|
return (
|
|
90
96
|
<div className="flex flex-col h-full border-l border-gray-200 bg-white">
|
|
91
97
|
<div className="flex items-center justify-between px-3 py-2 border-b border-gray-200 bg-gray-50">
|
|
92
98
|
<div className="flex items-center gap-1">
|
|
93
|
-
{(
|
|
99
|
+
{(
|
|
100
|
+
[
|
|
101
|
+
['desktop', Monitor],
|
|
102
|
+
['tablet', Tablet],
|
|
103
|
+
['mobile', Smartphone],
|
|
104
|
+
] as const
|
|
105
|
+
).map(([vp, Icon]) => (
|
|
94
106
|
<button
|
|
95
107
|
key={vp}
|
|
96
108
|
onClick={() => setViewport(vp)}
|
|
@@ -102,20 +114,35 @@ export function LivePreview({ collection, documentId, previewUrl, values, onClos
|
|
|
102
114
|
))}
|
|
103
115
|
</div>
|
|
104
116
|
<div className="flex items-center gap-1">
|
|
105
|
-
<button
|
|
117
|
+
<button
|
|
118
|
+
onClick={handleRefresh}
|
|
119
|
+
className="p-1.5 rounded text-gray-400 hover:text-gray-600"
|
|
120
|
+
title="Refresh"
|
|
121
|
+
>
|
|
106
122
|
<RefreshCw className="w-4 h-4" />
|
|
107
123
|
</button>
|
|
108
|
-
<button
|
|
124
|
+
<button
|
|
125
|
+
onClick={handleOpenExternal}
|
|
126
|
+
className="p-1.5 rounded text-gray-400 hover:text-gray-600"
|
|
127
|
+
title="Open in new tab"
|
|
128
|
+
>
|
|
109
129
|
<ExternalLink className="w-4 h-4" />
|
|
110
130
|
</button>
|
|
111
|
-
<button
|
|
131
|
+
<button
|
|
132
|
+
onClick={onClose}
|
|
133
|
+
className="p-1.5 rounded text-gray-400 hover:text-gray-600"
|
|
134
|
+
title="Close"
|
|
135
|
+
>
|
|
112
136
|
<X className="w-4 h-4" />
|
|
113
137
|
</button>
|
|
114
138
|
</div>
|
|
115
139
|
</div>
|
|
116
140
|
|
|
117
141
|
<div className="flex-1 overflow-auto flex justify-center bg-gray-100 p-4">
|
|
118
|
-
<div
|
|
142
|
+
<div
|
|
143
|
+
style={{ width: VIEWPORT_WIDTHS[viewport], maxWidth: '100%', transition: 'width 0.3s' }}
|
|
144
|
+
className="relative"
|
|
145
|
+
>
|
|
119
146
|
{loading && (
|
|
120
147
|
<div className="absolute inset-0 flex items-center justify-center bg-white/80 z-10 rounded-lg">
|
|
121
148
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" />
|
|
@@ -132,5 +159,5 @@ export function LivePreview({ collection, documentId, previewUrl, values, onClos
|
|
|
132
159
|
</div>
|
|
133
160
|
</div>
|
|
134
161
|
</div>
|
|
135
|
-
)
|
|
162
|
+
)
|
|
136
163
|
}
|
|
@@ -1,51 +1,57 @@
|
|
|
1
|
-
'use client'
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'
|
|
3
|
+
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'
|
|
4
4
|
|
|
5
5
|
interface LocaleContextValue {
|
|
6
|
-
activeLocale: string
|
|
7
|
-
setLocale: (locale: string) => void
|
|
8
|
-
locales: Array<{ code: string; label: string; dir?: 'ltr' | 'rtl' }
|
|
6
|
+
activeLocale: string
|
|
7
|
+
setLocale: (locale: string) => void
|
|
8
|
+
locales: Array<{ code: string; label: string; dir?: 'ltr' | 'rtl' }>
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const LocaleContext = createContext<LocaleContextValue>({
|
|
12
12
|
activeLocale: 'en',
|
|
13
13
|
setLocale: () => {},
|
|
14
14
|
locales: [],
|
|
15
|
-
})
|
|
15
|
+
})
|
|
16
16
|
|
|
17
17
|
export function useLocale() {
|
|
18
|
-
return useContext(LocaleContext)
|
|
18
|
+
return useContext(LocaleContext)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const STORAGE_KEY = 'actuate-locale'
|
|
21
|
+
const STORAGE_KEY = 'actuate-locale'
|
|
22
22
|
|
|
23
23
|
export interface LocaleProviderProps {
|
|
24
|
-
config: {
|
|
25
|
-
|
|
24
|
+
config: {
|
|
25
|
+
i18n?: {
|
|
26
|
+
defaultLocale: string
|
|
27
|
+
locales: Array<{ code: string; label: string; dir?: 'ltr' | 'rtl' }>
|
|
28
|
+
fallbackLocale?: string
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
children: ReactNode
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
export function LocaleProvider({ config, children }: LocaleProviderProps) {
|
|
29
|
-
const locales = config.i18n?.locales ?? []
|
|
30
|
-
const defaultLocale = config.i18n?.defaultLocale ?? 'en'
|
|
35
|
+
const locales = config.i18n?.locales ?? []
|
|
36
|
+
const defaultLocale = config.i18n?.defaultLocale ?? 'en'
|
|
31
37
|
|
|
32
|
-
const [activeLocale, setActiveLocale] = useState(defaultLocale)
|
|
38
|
+
const [activeLocale, setActiveLocale] = useState(defaultLocale)
|
|
33
39
|
|
|
34
40
|
useEffect(() => {
|
|
35
|
-
const stored = localStorage.getItem(STORAGE_KEY)
|
|
41
|
+
const stored = localStorage.getItem(STORAGE_KEY)
|
|
36
42
|
if (stored && locales.some((l) => l.code === stored)) {
|
|
37
|
-
setActiveLocale(stored)
|
|
43
|
+
setActiveLocale(stored)
|
|
38
44
|
}
|
|
39
|
-
}, [locales])
|
|
45
|
+
}, [locales])
|
|
40
46
|
|
|
41
47
|
const setLocale = useCallback((locale: string) => {
|
|
42
|
-
setActiveLocale(locale)
|
|
43
|
-
localStorage.setItem(STORAGE_KEY, locale)
|
|
44
|
-
}, [])
|
|
48
|
+
setActiveLocale(locale)
|
|
49
|
+
localStorage.setItem(STORAGE_KEY, locale)
|
|
50
|
+
}, [])
|
|
45
51
|
|
|
46
52
|
return (
|
|
47
53
|
<LocaleContext.Provider value={{ activeLocale, setLocale, locales }}>
|
|
48
54
|
{children}
|
|
49
55
|
</LocaleContext.Provider>
|
|
50
|
-
)
|
|
56
|
+
)
|
|
51
57
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
'use client'
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { Globe, Check, ChevronDown } from 'lucide-react'
|
|
4
|
-
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
|
5
|
-
import { useLocale } from './LocaleProvider.js'
|
|
3
|
+
import { Globe, Check, ChevronDown } from 'lucide-react'
|
|
4
|
+
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
|
5
|
+
import { useLocale } from './LocaleProvider.js'
|
|
6
6
|
|
|
7
7
|
export function LocaleSwitcher() {
|
|
8
|
-
const { activeLocale, setLocale, locales } = useLocale()
|
|
8
|
+
const { activeLocale, setLocale, locales } = useLocale()
|
|
9
9
|
|
|
10
|
-
if (locales.length < 2) return null
|
|
10
|
+
if (locales.length < 2) return null
|
|
11
11
|
|
|
12
|
-
const activeLabel = locales.find((l) => l.code === activeLocale)?.label ?? activeLocale
|
|
12
|
+
const activeLabel = locales.find((l) => l.code === activeLocale)?.label ?? activeLocale
|
|
13
13
|
|
|
14
14
|
return (
|
|
15
15
|
<DropdownMenu.Root>
|
|
@@ -39,13 +39,11 @@ export function LocaleSwitcher() {
|
|
|
39
39
|
className="flex items-center justify-between gap-2 px-3 py-2 text-sm hover:bg-[var(--accent)] rounded cursor-pointer outline-none"
|
|
40
40
|
>
|
|
41
41
|
<span>{locale.label}</span>
|
|
42
|
-
{locale.code === activeLocale && (
|
|
43
|
-
<Check className="w-4 h-4 text-[var(--primary)]" />
|
|
44
|
-
)}
|
|
42
|
+
{locale.code === activeLocale && <Check className="w-4 h-4 text-[var(--primary)]" />}
|
|
45
43
|
</DropdownMenu.Item>
|
|
46
44
|
))}
|
|
47
45
|
</DropdownMenu.Content>
|
|
48
46
|
</DropdownMenu.Portal>
|
|
49
47
|
</DropdownMenu.Root>
|
|
50
|
-
)
|
|
48
|
+
)
|
|
51
49
|
}
|
|
@@ -1,76 +1,74 @@
|
|
|
1
|
-
'use client'
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useRef } from 'react'
|
|
4
|
-
import { X, Upload, Search, ImageIcon, Loader2 } from 'lucide-react'
|
|
5
|
-
import { toast } from 'sonner'
|
|
6
|
-
import { cmsApi } from '../lib/api.js'
|
|
7
|
-
import { useApiData } from '../lib/useApiData.js'
|
|
3
|
+
import { useState, useRef } from 'react'
|
|
4
|
+
import { X, Upload, Search, ImageIcon, Loader2 } from 'lucide-react'
|
|
5
|
+
import { toast } from 'sonner'
|
|
6
|
+
import { cmsApi } from '../lib/api.js'
|
|
7
|
+
import { useApiData } from '../lib/useApiData.js'
|
|
8
8
|
|
|
9
9
|
interface MediaItem {
|
|
10
|
-
id: string
|
|
11
|
-
filename: string
|
|
12
|
-
storageKey: string
|
|
13
|
-
mimeType: string
|
|
14
|
-
fileSize: number
|
|
15
|
-
width?: number
|
|
16
|
-
height?: number
|
|
10
|
+
id: string
|
|
11
|
+
filename: string
|
|
12
|
+
storageKey: string
|
|
13
|
+
mimeType: string
|
|
14
|
+
fileSize: number
|
|
15
|
+
width?: number
|
|
16
|
+
height?: number
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface MediaPickerModalProps {
|
|
20
|
-
open: boolean
|
|
21
|
-
onClose: () => void
|
|
22
|
-
onSelect: (url: string, alt?: string) => void
|
|
23
|
-
accept?: string
|
|
20
|
+
open: boolean
|
|
21
|
+
onClose: () => void
|
|
22
|
+
onSelect: (url: string, alt?: string) => void
|
|
23
|
+
accept?: string
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function MediaPickerModal({ open, onClose, onSelect, accept }: MediaPickerModalProps) {
|
|
27
|
-
const [tab, setTab] = useState<'library' | 'upload'>('library')
|
|
28
|
-
const [search, setSearch] = useState('')
|
|
29
|
-
const [uploading, setUploading] = useState(false)
|
|
30
|
-
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
27
|
+
const [tab, setTab] = useState<'library' | 'upload'>('library')
|
|
28
|
+
const [search, setSearch] = useState('')
|
|
29
|
+
const [uploading, setUploading] = useState(false)
|
|
30
|
+
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
31
31
|
|
|
32
32
|
const { data, loading, refetch } = useApiData<{ data: { items: MediaItem[] } }>(
|
|
33
33
|
open ? `/media?pageSize=50${search ? `&search=${encodeURIComponent(search)}` : ''}` : null,
|
|
34
|
-
)
|
|
34
|
+
)
|
|
35
35
|
|
|
36
|
-
const items = (data as any)?.data?.items ?? (data as any)?.items ?? []
|
|
37
|
-
const imageItems = items.filter((item: MediaItem) =>
|
|
38
|
-
item.mimeType.startsWith('image/'),
|
|
39
|
-
);
|
|
36
|
+
const items = (data as any)?.data?.items ?? (data as any)?.items ?? []
|
|
37
|
+
const imageItems = items.filter((item: MediaItem) => item.mimeType.startsWith('image/'))
|
|
40
38
|
|
|
41
39
|
async function handleUpload(files: FileList | null) {
|
|
42
|
-
if (!files || files.length === 0) return
|
|
43
|
-
setUploading(true)
|
|
40
|
+
if (!files || files.length === 0) return
|
|
41
|
+
setUploading(true)
|
|
44
42
|
|
|
45
|
-
const file = files[0]
|
|
46
|
-
const formData = new FormData()
|
|
47
|
-
formData.append('file', file)
|
|
43
|
+
const file = files[0]!
|
|
44
|
+
const formData = new FormData()
|
|
45
|
+
formData.append('file', file)
|
|
48
46
|
|
|
49
47
|
const res = await cmsApi<MediaItem>('/media/upload', {
|
|
50
48
|
method: 'POST',
|
|
51
49
|
body: formData,
|
|
52
|
-
})
|
|
50
|
+
})
|
|
53
51
|
|
|
54
52
|
if (res.error) {
|
|
55
|
-
toast.error(res.error)
|
|
53
|
+
toast.error(res.error)
|
|
56
54
|
} else if (res.data) {
|
|
57
|
-
const mediaItem = res.data as any
|
|
58
|
-
const url = mediaItem.storageKey ?? ''
|
|
59
|
-
toast.success(`Uploaded ${file.name}`)
|
|
60
|
-
onSelect(url, file.name)
|
|
61
|
-
onClose()
|
|
55
|
+
const mediaItem = res.data as any
|
|
56
|
+
const url = mediaItem.storageKey ?? ''
|
|
57
|
+
toast.success(`Uploaded ${file.name}`)
|
|
58
|
+
onSelect(url, file.name)
|
|
59
|
+
onClose()
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
setUploading(false)
|
|
65
|
-
if (fileInputRef.current) fileInputRef.current.value = ''
|
|
62
|
+
setUploading(false)
|
|
63
|
+
if (fileInputRef.current) fileInputRef.current.value = ''
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
function handleSelectItem(item: MediaItem) {
|
|
69
|
-
onSelect(item.storageKey, item.filename)
|
|
70
|
-
onClose()
|
|
67
|
+
onSelect(item.storageKey, item.filename)
|
|
68
|
+
onClose()
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
if (!open) return null
|
|
71
|
+
if (!open) return null
|
|
74
72
|
|
|
75
73
|
return (
|
|
76
74
|
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
@@ -78,7 +76,10 @@ export function MediaPickerModal({ open, onClose, onSelect, accept }: MediaPicke
|
|
|
78
76
|
<div className="relative bg-white rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col mx-4">
|
|
79
77
|
<div className="flex items-center justify-between px-4 py-3 border-b border-gray-200">
|
|
80
78
|
<h2 className="text-lg font-semibold text-gray-900">Insert Image</h2>
|
|
81
|
-
<button
|
|
79
|
+
<button
|
|
80
|
+
onClick={onClose}
|
|
81
|
+
className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors"
|
|
82
|
+
>
|
|
82
83
|
<X className="w-5 h-5 text-gray-500" />
|
|
83
84
|
</button>
|
|
84
85
|
</div>
|
|
@@ -179,5 +180,5 @@ export function MediaPickerModal({ open, onClose, onSelect, accept }: MediaPicke
|
|
|
179
180
|
</div>
|
|
180
181
|
</div>
|
|
181
182
|
</div>
|
|
182
|
-
)
|
|
183
|
+
)
|
|
183
184
|
}
|
|
@@ -1,53 +1,56 @@
|
|
|
1
|
-
'use client'
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState, useRef } from 'react'
|
|
3
|
+
import { useEffect, useState, useRef } from 'react'
|
|
4
4
|
|
|
5
5
|
interface PresenceUser {
|
|
6
|
-
userId: string
|
|
7
|
-
name: string
|
|
8
|
-
connectedAt: string
|
|
6
|
+
userId: string
|
|
7
|
+
name: string
|
|
8
|
+
connectedAt: string
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
interface PresenceIndicatorProps {
|
|
12
|
-
documentId: string
|
|
13
|
-
currentUserId?: string
|
|
12
|
+
documentId: string
|
|
13
|
+
currentUserId?: string
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const COLORS = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899']
|
|
16
|
+
const COLORS = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899']
|
|
17
17
|
|
|
18
18
|
export function PresenceIndicator({ documentId, currentUserId }: PresenceIndicatorProps) {
|
|
19
|
-
const [users, setUsers] = useState<PresenceUser[]>([])
|
|
20
|
-
const eventSourceRef = useRef<EventSource | null>(null)
|
|
21
|
-
const heartbeatRef = useRef<ReturnType<typeof setInterval>>(undefined)
|
|
19
|
+
const [users, setUsers] = useState<PresenceUser[]>([])
|
|
20
|
+
const eventSourceRef = useRef<EventSource | null>(null)
|
|
21
|
+
const heartbeatRef = useRef<ReturnType<typeof setInterval>>(undefined)
|
|
22
22
|
|
|
23
23
|
useEffect(() => {
|
|
24
|
-
if (!documentId) return
|
|
24
|
+
if (!documentId) return
|
|
25
25
|
|
|
26
|
-
const es = new EventSource(`/api/cms/presence/${documentId}`)
|
|
27
|
-
eventSourceRef.current = es
|
|
26
|
+
const es = new EventSource(`/api/cms/presence/${documentId}`)
|
|
27
|
+
eventSourceRef.current = es
|
|
28
28
|
|
|
29
29
|
es.addEventListener('presence', (event) => {
|
|
30
30
|
try {
|
|
31
|
-
const data = JSON.parse(event.data) as PresenceUser[]
|
|
32
|
-
setUsers(data.filter(u => u.userId !== currentUserId))
|
|
31
|
+
const data = JSON.parse(event.data) as PresenceUser[]
|
|
32
|
+
setUsers(data.filter((u) => u.userId !== currentUserId))
|
|
33
33
|
} catch {}
|
|
34
|
-
})
|
|
34
|
+
})
|
|
35
35
|
|
|
36
36
|
es.onerror = () => {
|
|
37
|
-
es.close()
|
|
38
|
-
}
|
|
37
|
+
es.close()
|
|
38
|
+
}
|
|
39
39
|
|
|
40
40
|
heartbeatRef.current = setInterval(() => {
|
|
41
|
-
fetch(`/api/cms/presence/${documentId}/heartbeat`, {
|
|
42
|
-
|
|
41
|
+
fetch(`/api/cms/presence/${documentId}/heartbeat`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
credentials: 'include',
|
|
44
|
+
}).catch(() => {})
|
|
45
|
+
}, 30_000)
|
|
43
46
|
|
|
44
47
|
return () => {
|
|
45
|
-
es.close()
|
|
46
|
-
if (heartbeatRef.current) clearInterval(heartbeatRef.current)
|
|
47
|
-
}
|
|
48
|
-
}, [documentId, currentUserId])
|
|
48
|
+
es.close()
|
|
49
|
+
if (heartbeatRef.current) clearInterval(heartbeatRef.current)
|
|
50
|
+
}
|
|
51
|
+
}, [documentId, currentUserId])
|
|
49
52
|
|
|
50
|
-
if (users.length === 0) return null
|
|
53
|
+
if (users.length === 0) return null
|
|
51
54
|
|
|
52
55
|
return (
|
|
53
56
|
<div className="flex items-center gap-1" title={`${users.length} other editor(s) active`}>
|
|
@@ -67,5 +70,5 @@ export function PresenceIndicator({ documentId, currentUserId }: PresenceIndicat
|
|
|
67
70
|
</div>
|
|
68
71
|
)}
|
|
69
72
|
</div>
|
|
70
|
-
)
|
|
73
|
+
)
|
|
71
74
|
}
|