@actuate-media/cms-admin 0.7.3 → 0.8.1
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 +95 -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.d.ts.map +1 -1
- package/dist/lib/api.js +33 -4
- 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 +39 -16
- 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 +94 -3
- 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 +25 -3
- 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 +302 -177
- 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 +58 -30
- 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 +7 -1
- 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 +589 -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 +258 -81
- 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 +164 -146
- 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
package/src/views/Pages.tsx
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Plus,
|
|
5
|
+
Search,
|
|
6
|
+
Trash2,
|
|
7
|
+
SlidersHorizontal,
|
|
8
|
+
Pencil,
|
|
9
|
+
ArrowUpDown,
|
|
10
|
+
ArrowUp,
|
|
11
|
+
ArrowDown,
|
|
12
|
+
Loader2,
|
|
13
|
+
AlertTriangle,
|
|
14
|
+
GripVertical,
|
|
15
|
+
FolderInput,
|
|
16
|
+
} from 'lucide-react'
|
|
17
|
+
import { useState, useMemo, useCallback } from 'react'
|
|
18
|
+
import { toast } from 'sonner'
|
|
19
|
+
import { sortByRelevance, type SortConfig, toggleSort } from '../lib/search.js'
|
|
20
|
+
import { useApiData } from '../lib/useApiData.js'
|
|
21
|
+
import { cmsApi } from '../lib/api.js'
|
|
22
|
+
import { FolderTree, type FolderSelection } from '../components/FolderTree.js'
|
|
23
|
+
|
|
24
|
+
type PageSortKey = 'title' | 'author' | 'template' | 'status' | 'date'
|
|
12
25
|
|
|
13
26
|
export interface PagesProps {
|
|
14
|
-
onNavigate?: (path: string) => void
|
|
27
|
+
onNavigate?: (path: string) => void
|
|
15
28
|
}
|
|
16
29
|
|
|
17
30
|
const COLOR_PALETTE = [
|
|
@@ -23,177 +36,195 @@ const COLOR_PALETTE = [
|
|
|
23
36
|
{ bg: 'bg-teal-100', text: 'text-teal-700' },
|
|
24
37
|
{ bg: 'bg-indigo-100', text: 'text-indigo-700' },
|
|
25
38
|
{ bg: 'bg-rose-100', text: 'text-rose-700' },
|
|
26
|
-
]
|
|
39
|
+
]
|
|
27
40
|
|
|
28
41
|
function hashString(s: string): number {
|
|
29
|
-
let hash = 0
|
|
42
|
+
let hash = 0
|
|
30
43
|
for (let i = 0; i < s.length; i++) {
|
|
31
|
-
hash = ((hash << 5) - hash + s.charCodeAt(i)) | 0
|
|
44
|
+
hash = ((hash << 5) - hash + s.charCodeAt(i)) | 0
|
|
32
45
|
}
|
|
33
|
-
return Math.abs(hash)
|
|
46
|
+
return Math.abs(hash)
|
|
34
47
|
}
|
|
35
48
|
|
|
36
|
-
const folderColorCache: Record<string, (typeof COLOR_PALETTE)[0]> = {}
|
|
49
|
+
const folderColorCache: Record<string, (typeof COLOR_PALETTE)[0]> = {}
|
|
37
50
|
|
|
38
51
|
function getFolderColor(name: string) {
|
|
39
52
|
if (!folderColorCache[name]) {
|
|
40
|
-
folderColorCache[name] = COLOR_PALETTE[hashString(name) % COLOR_PALETTE.length]
|
|
53
|
+
folderColorCache[name] = COLOR_PALETTE[hashString(name) % COLOR_PALETTE.length]!
|
|
41
54
|
}
|
|
42
|
-
return folderColorCache[name]
|
|
55
|
+
return folderColorCache[name]!
|
|
43
56
|
}
|
|
44
57
|
|
|
45
58
|
function computeSeoScore(data: Record<string, unknown> | null | undefined): number {
|
|
46
|
-
if (!data) return 0
|
|
47
|
-
let score = 0
|
|
48
|
-
if (data.metaTitle || data.seoTitle) score += 25
|
|
49
|
-
if (data.metaDescription || data.seoDescription) score += 25
|
|
50
|
-
if (data.canonical) score += 25
|
|
51
|
-
if (data.schemaType) score += 25
|
|
52
|
-
return score
|
|
59
|
+
if (!data) return 0
|
|
60
|
+
let score = 0
|
|
61
|
+
if (data.metaTitle || data.seoTitle) score += 25
|
|
62
|
+
if (data.metaDescription || data.seoDescription) score += 25
|
|
63
|
+
if (data.canonical) score += 25
|
|
64
|
+
if (data.schemaType) score += 25
|
|
65
|
+
return score
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
function textValue(value: unknown): string {
|
|
56
|
-
return String(value ?? '')
|
|
69
|
+
return String(value ?? '')
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
function SeoScoreBadge({ score }: { score: number }) {
|
|
60
|
-
const color = score >= 80 ? 'bg-green-500' : score >= 60 ? 'bg-amber-500' : 'bg-red-500'
|
|
73
|
+
const color = score >= 80 ? 'bg-green-500' : score >= 60 ? 'bg-amber-500' : 'bg-red-500'
|
|
61
74
|
return (
|
|
62
75
|
<div className="flex items-center gap-1.5">
|
|
63
76
|
<span className={`w-2.5 h-2.5 rounded-full ${color}`} />
|
|
64
77
|
<span className="text-xs text-gray-600">{score}</span>
|
|
65
78
|
</div>
|
|
66
|
-
)
|
|
79
|
+
)
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
function FolderBadge({ name }: { name: string }) {
|
|
70
|
-
const colors = getFolderColor(name)
|
|
83
|
+
const colors = getFolderColor(name)
|
|
71
84
|
return (
|
|
72
|
-
<span
|
|
85
|
+
<span
|
|
86
|
+
className={`inline-flex px-2 py-0.5 rounded text-xs font-medium ${colors.bg} ${colors.text}`}
|
|
87
|
+
>
|
|
73
88
|
{name}
|
|
74
89
|
</span>
|
|
75
|
-
)
|
|
90
|
+
)
|
|
76
91
|
}
|
|
77
92
|
|
|
78
93
|
function buildApiUrl(folderSel: FolderSelection): string {
|
|
79
|
-
const base = '/collections/pages?pageSize=100'
|
|
94
|
+
const base = '/collections/pages?pageSize=100'
|
|
80
95
|
if (folderSel.type === 'smart') {
|
|
81
|
-
if (folderSel.smart === 'recent') return `${base}&sort=updatedAt&order=desc&pageSize=20
|
|
82
|
-
if (folderSel.smart === 'uncategorized') return `${base}&folderId=none
|
|
83
|
-
return base
|
|
96
|
+
if (folderSel.smart === 'recent') return `${base}&sort=updatedAt&order=desc&pageSize=20`
|
|
97
|
+
if (folderSel.smart === 'uncategorized') return `${base}&folderId=none`
|
|
98
|
+
return base
|
|
84
99
|
}
|
|
85
|
-
return `${base}&folderId=${folderSel.folderId}
|
|
100
|
+
return `${base}&folderId=${folderSel.folderId}`
|
|
86
101
|
}
|
|
87
102
|
|
|
88
103
|
export function Pages({ onNavigate }: PagesProps) {
|
|
89
|
-
const [folderSel, setFolderSel] = useState<FolderSelection>({ type: 'smart', smart: 'all' })
|
|
90
|
-
const [sidebarOpen, setSidebarOpen] = useState(true)
|
|
104
|
+
const [folderSel, setFolderSel] = useState<FolderSelection>({ type: 'smart', smart: 'all' })
|
|
105
|
+
const [sidebarOpen, setSidebarOpen] = useState(true)
|
|
91
106
|
|
|
92
|
-
const apiUrl = useMemo(() => buildApiUrl(folderSel), [folderSel])
|
|
93
|
-
const { data, loading, error, refetch } = useApiData<{ docs: any[]; total: number }>(apiUrl)
|
|
107
|
+
const apiUrl = useMemo(() => buildApiUrl(folderSel), [folderSel])
|
|
108
|
+
const { data, loading, error, refetch } = useApiData<{ docs: any[]; total: number }>(apiUrl)
|
|
94
109
|
|
|
95
|
-
const allData = useApiData<{ docs: any[]; total: number }>('/collections/pages?pageSize=1')
|
|
96
|
-
const uncatData = useApiData<{ docs: any[]; total: number }>(
|
|
110
|
+
const allData = useApiData<{ docs: any[]; total: number }>('/collections/pages?pageSize=1')
|
|
111
|
+
const uncatData = useApiData<{ docs: any[]; total: number }>(
|
|
112
|
+
'/collections/pages?pageSize=1&folderId=none',
|
|
113
|
+
)
|
|
97
114
|
|
|
98
|
-
const [searchQuery, setSearchQuery] = useState('')
|
|
99
|
-
const [filterStatus, setFilterStatus] = useState<string>('all')
|
|
100
|
-
const [filterTemplate, setFilterTemplate] = useState<string>('all')
|
|
101
|
-
const [selectedPages, setSelectedPages] = useState<number[]>([])
|
|
102
|
-
const [sortConfig, setSortConfig] = useState<SortConfig<PageSortKey> | null>(null)
|
|
115
|
+
const [searchQuery, setSearchQuery] = useState('')
|
|
116
|
+
const [filterStatus, setFilterStatus] = useState<string>('all')
|
|
117
|
+
const [filterTemplate, setFilterTemplate] = useState<string>('all')
|
|
118
|
+
const [selectedPages, setSelectedPages] = useState<number[]>([])
|
|
119
|
+
const [sortConfig, setSortConfig] = useState<SortConfig<PageSortKey> | null>(null)
|
|
103
120
|
|
|
104
|
-
const pages = data?.docs ?? []
|
|
105
|
-
const totalCount = data?.total ?? pages.length
|
|
121
|
+
const pages = data?.docs ?? []
|
|
122
|
+
const totalCount = data?.total ?? pages.length
|
|
106
123
|
|
|
107
124
|
const filteredAndSorted = useMemo(() => {
|
|
108
125
|
let results = pages.filter((page: any) => {
|
|
109
|
-
const matchesSearch =
|
|
110
|
-
textValue(page.
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
const matchesSearch =
|
|
127
|
+
textValue(page.title).toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
128
|
+
textValue(page.author).toLowerCase().includes(searchQuery.toLowerCase())
|
|
129
|
+
const matchesStatus =
|
|
130
|
+
filterStatus === 'all' ||
|
|
131
|
+
textValue(page.status).toLowerCase() === filterStatus.toLowerCase()
|
|
132
|
+
const matchesTemplate =
|
|
133
|
+
filterTemplate === 'all' ||
|
|
134
|
+
textValue(page.template).toLowerCase() === filterTemplate.toLowerCase()
|
|
135
|
+
return matchesSearch && matchesStatus && matchesTemplate
|
|
136
|
+
})
|
|
115
137
|
|
|
116
138
|
if (searchQuery.trim()) {
|
|
117
|
-
results = sortByRelevance(results, searchQuery, (p: any) => [
|
|
139
|
+
results = sortByRelevance(results, searchQuery, (p: any) => [
|
|
140
|
+
p.title,
|
|
141
|
+
p.author ?? '',
|
|
142
|
+
p.template ?? '',
|
|
143
|
+
])
|
|
118
144
|
} else if (sortConfig) {
|
|
119
145
|
results = [...results].sort((a: any, b: any) => {
|
|
120
|
-
const aVal = a[sortConfig.key] ?? ''
|
|
121
|
-
const bVal = b[sortConfig.key] ?? ''
|
|
122
|
-
const cmp = String(aVal).localeCompare(String(bVal))
|
|
123
|
-
return sortConfig.direction === 'asc' ? cmp : -cmp
|
|
124
|
-
})
|
|
146
|
+
const aVal = a[sortConfig.key] ?? ''
|
|
147
|
+
const bVal = b[sortConfig.key] ?? ''
|
|
148
|
+
const cmp = String(aVal).localeCompare(String(bVal))
|
|
149
|
+
return sortConfig.direction === 'asc' ? cmp : -cmp
|
|
150
|
+
})
|
|
125
151
|
}
|
|
126
|
-
return results
|
|
127
|
-
}, [pages, searchQuery, filterStatus, filterTemplate, sortConfig])
|
|
152
|
+
return results
|
|
153
|
+
}, [pages, searchQuery, filterStatus, filterTemplate, sortConfig])
|
|
128
154
|
|
|
129
155
|
const handleSelectAll = (checked: boolean) => {
|
|
130
|
-
setSelectedPages(checked ? filteredAndSorted.map((p: any) => p.id) : [])
|
|
131
|
-
}
|
|
156
|
+
setSelectedPages(checked ? filteredAndSorted.map((p: any) => p.id) : [])
|
|
157
|
+
}
|
|
132
158
|
|
|
133
159
|
const handleSelectPage = (id: number) => {
|
|
134
|
-
setSelectedPages(prev
|
|
135
|
-
|
|
160
|
+
setSelectedPages((prev) =>
|
|
161
|
+
prev.includes(id) ? prev.filter((pid) => pid !== id) : [...prev, id],
|
|
162
|
+
)
|
|
163
|
+
}
|
|
136
164
|
|
|
137
165
|
const handleBulkDelete = async () => {
|
|
138
166
|
for (const id of selectedPages) {
|
|
139
|
-
await cmsApi(`/collections/pages/${id}`, { method: 'DELETE' })
|
|
167
|
+
await cmsApi(`/collections/pages/${id}`, { method: 'DELETE' })
|
|
140
168
|
}
|
|
141
|
-
toast.success(`${selectedPages.length} pages deleted`)
|
|
142
|
-
setSelectedPages([])
|
|
143
|
-
refetch()
|
|
144
|
-
}
|
|
169
|
+
toast.success(`${selectedPages.length} pages deleted`)
|
|
170
|
+
setSelectedPages([])
|
|
171
|
+
refetch()
|
|
172
|
+
}
|
|
145
173
|
|
|
146
174
|
const handleBulkPublish = async () => {
|
|
147
175
|
for (const id of selectedPages) {
|
|
148
176
|
await cmsApi(`/collections/pages/${id}`, {
|
|
149
177
|
method: 'PUT',
|
|
150
178
|
body: JSON.stringify({ status: 'PUBLISHED' }),
|
|
151
|
-
})
|
|
179
|
+
})
|
|
152
180
|
}
|
|
153
|
-
toast.success(`${selectedPages.length} pages published`)
|
|
154
|
-
setSelectedPages([])
|
|
155
|
-
refetch()
|
|
156
|
-
}
|
|
181
|
+
toast.success(`${selectedPages.length} pages published`)
|
|
182
|
+
setSelectedPages([])
|
|
183
|
+
refetch()
|
|
184
|
+
}
|
|
157
185
|
|
|
158
186
|
const handleBulkUnpublish = async () => {
|
|
159
187
|
for (const id of selectedPages) {
|
|
160
188
|
await cmsApi(`/collections/pages/${id}`, {
|
|
161
189
|
method: 'PUT',
|
|
162
190
|
body: JSON.stringify({ status: 'DRAFT' }),
|
|
163
|
-
})
|
|
191
|
+
})
|
|
164
192
|
}
|
|
165
|
-
toast.success(`${selectedPages.length} pages unpublished`)
|
|
166
|
-
setSelectedPages([])
|
|
167
|
-
refetch()
|
|
168
|
-
}
|
|
193
|
+
toast.success(`${selectedPages.length} pages unpublished`)
|
|
194
|
+
setSelectedPages([])
|
|
195
|
+
refetch()
|
|
196
|
+
}
|
|
169
197
|
|
|
170
198
|
const handleDelete = async (id: number) => {
|
|
171
|
-
await cmsApi(`/collections/pages/${id}`, { method: 'DELETE' })
|
|
172
|
-
toast.success('Page deleted')
|
|
173
|
-
setSelectedPages(prev => prev.filter(pid => pid !== id))
|
|
174
|
-
refetch()
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const handleDropItem = useCallback(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
await cmsApi(`/collections/pages/${id}`, { method: 'DELETE' })
|
|
200
|
+
toast.success('Page deleted')
|
|
201
|
+
setSelectedPages((prev) => prev.filter((pid) => pid !== id))
|
|
202
|
+
refetch()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const handleDropItem = useCallback(
|
|
206
|
+
async (itemId: string, folderId: string | null) => {
|
|
207
|
+
const res = await cmsApi(`/documents/${itemId}/folder`, {
|
|
208
|
+
method: 'PUT',
|
|
209
|
+
body: JSON.stringify({ folderId }),
|
|
210
|
+
})
|
|
211
|
+
if (res.error) {
|
|
212
|
+
toast.error(res.error)
|
|
213
|
+
} else {
|
|
214
|
+
toast.success(folderId ? 'Moved to folder' : 'Removed from folder')
|
|
215
|
+
refetch()
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
[refetch],
|
|
219
|
+
)
|
|
189
220
|
|
|
190
221
|
const handleDragStart = (e: React.DragEvent, id: string) => {
|
|
191
|
-
e.dataTransfer.setData('text/actuate-item-id', id)
|
|
192
|
-
e.dataTransfer.effectAllowed = 'move'
|
|
193
|
-
}
|
|
222
|
+
e.dataTransfer.setData('text/actuate-item-id', id)
|
|
223
|
+
e.dataTransfer.effectAllowed = 'move'
|
|
224
|
+
}
|
|
194
225
|
|
|
195
226
|
function SortHeader({ label, sortKey }: { label: string; sortKey: PageSortKey }) {
|
|
196
|
-
const active = sortConfig?.key === sortKey
|
|
227
|
+
const active = sortConfig?.key === sortKey
|
|
197
228
|
return (
|
|
198
229
|
<button
|
|
199
230
|
type="button"
|
|
@@ -202,12 +233,16 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
202
233
|
>
|
|
203
234
|
{label}
|
|
204
235
|
{active ? (
|
|
205
|
-
sortConfig!.direction === 'asc' ?
|
|
236
|
+
sortConfig!.direction === 'asc' ? (
|
|
237
|
+
<ArrowUp className="w-3 h-3" />
|
|
238
|
+
) : (
|
|
239
|
+
<ArrowDown className="w-3 h-3" />
|
|
240
|
+
)
|
|
206
241
|
) : (
|
|
207
242
|
<ArrowUpDown className="w-3 h-3 text-gray-400" />
|
|
208
243
|
)}
|
|
209
244
|
</button>
|
|
210
|
-
)
|
|
245
|
+
)
|
|
211
246
|
}
|
|
212
247
|
|
|
213
248
|
if (loading) {
|
|
@@ -215,7 +250,7 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
215
250
|
<div className="p-3 pr-6 sm:p-4 sm:pr-8 flex items-center justify-center h-64">
|
|
216
251
|
<Loader2 className="w-6 h-6 animate-spin text-blue-600" />
|
|
217
252
|
</div>
|
|
218
|
-
)
|
|
253
|
+
)
|
|
219
254
|
}
|
|
220
255
|
|
|
221
256
|
return (
|
|
@@ -224,7 +259,12 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
224
259
|
<div className="mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
|
|
225
260
|
<AlertTriangle className="w-5 h-5 text-red-600 shrink-0" />
|
|
226
261
|
<span className="text-sm text-red-800 flex-1">{error}</span>
|
|
227
|
-
<button
|
|
262
|
+
<button
|
|
263
|
+
onClick={refetch}
|
|
264
|
+
className="px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors"
|
|
265
|
+
>
|
|
266
|
+
Retry
|
|
267
|
+
</button>
|
|
228
268
|
</div>
|
|
229
269
|
)}
|
|
230
270
|
|
|
@@ -232,7 +272,7 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
232
272
|
<div className="flex items-center gap-3">
|
|
233
273
|
<button
|
|
234
274
|
type="button"
|
|
235
|
-
onClick={() => setSidebarOpen(prev => !prev)}
|
|
275
|
+
onClick={() => setSidebarOpen((prev) => !prev)}
|
|
236
276
|
className="p-1.5 rounded-lg hover:bg-gray-100 transition-colors"
|
|
237
277
|
title={sidebarOpen ? 'Hide folders' : 'Show folders'}
|
|
238
278
|
>
|
|
@@ -240,7 +280,9 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
240
280
|
</button>
|
|
241
281
|
<div>
|
|
242
282
|
<h1 className="text-xl sm:text-2xl font-semibold text-gray-900">Pages</h1>
|
|
243
|
-
<p className="text-sm text-gray-500">
|
|
283
|
+
<p className="text-sm text-gray-500">
|
|
284
|
+
{totalCount} page{totalCount !== 1 ? 's' : ''}
|
|
285
|
+
</p>
|
|
244
286
|
</div>
|
|
245
287
|
</div>
|
|
246
288
|
<button
|
|
@@ -259,7 +301,10 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
259
301
|
<FolderTree
|
|
260
302
|
scope="pages"
|
|
261
303
|
selected={folderSel}
|
|
262
|
-
onSelect={(sel) => {
|
|
304
|
+
onSelect={(sel) => {
|
|
305
|
+
setFolderSel(sel)
|
|
306
|
+
setSelectedPages([])
|
|
307
|
+
}}
|
|
263
308
|
totalCount={allData.data?.total}
|
|
264
309
|
uncategorizedCount={uncatData.data?.total}
|
|
265
310
|
onDropItem={handleDropItem}
|
|
@@ -272,22 +317,39 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
272
317
|
<div className="p-3 flex flex-col gap-3">
|
|
273
318
|
<div className="relative">
|
|
274
319
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
275
|
-
<input
|
|
320
|
+
<input
|
|
321
|
+
type="text"
|
|
322
|
+
placeholder="Search pages..."
|
|
323
|
+
value={searchQuery}
|
|
324
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
325
|
+
className="w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
326
|
+
/>
|
|
276
327
|
</div>
|
|
277
328
|
<div className="flex flex-col sm:flex-row gap-2">
|
|
278
|
-
<select
|
|
329
|
+
<select
|
|
330
|
+
value={filterStatus}
|
|
331
|
+
onChange={(e) => setFilterStatus(e.target.value)}
|
|
332
|
+
className="flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
333
|
+
>
|
|
279
334
|
<option value="all">All Status</option>
|
|
280
335
|
<option value="published">Published</option>
|
|
281
336
|
<option value="draft">Draft</option>
|
|
282
337
|
</select>
|
|
283
|
-
<select
|
|
338
|
+
<select
|
|
339
|
+
value={filterTemplate}
|
|
340
|
+
onChange={(e) => setFilterTemplate(e.target.value)}
|
|
341
|
+
className="flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
342
|
+
>
|
|
284
343
|
<option value="all">All Templates</option>
|
|
285
344
|
<option value="landing">Landing</option>
|
|
286
345
|
<option value="standard">Standard</option>
|
|
287
346
|
<option value="marketing">Marketing</option>
|
|
288
347
|
<option value="blog">Blog</option>
|
|
289
348
|
</select>
|
|
290
|
-
<button
|
|
349
|
+
<button
|
|
350
|
+
type="button"
|
|
351
|
+
className="flex items-center justify-center gap-2 px-3 py-2 text-sm border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
|
|
352
|
+
>
|
|
291
353
|
<SlidersHorizontal className="w-4 h-4" />
|
|
292
354
|
<span className="hidden sm:inline">More Filters</span>
|
|
293
355
|
</button>
|
|
@@ -298,12 +360,38 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
298
360
|
{selectedPages.length > 0 && (
|
|
299
361
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3 mb-4">
|
|
300
362
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
|
|
301
|
-
<span className="text-sm text-blue-900">
|
|
363
|
+
<span className="text-sm text-blue-900">
|
|
364
|
+
{selectedPages.length} page{selectedPages.length !== 1 ? 's' : ''} selected
|
|
365
|
+
</span>
|
|
302
366
|
<div className="flex items-center gap-2">
|
|
303
|
-
<button
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
367
|
+
<button
|
|
368
|
+
type="button"
|
|
369
|
+
onClick={handleBulkPublish}
|
|
370
|
+
className="flex-1 sm:flex-none px-3 py-1.5 text-sm bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
|
|
371
|
+
>
|
|
372
|
+
Publish
|
|
373
|
+
</button>
|
|
374
|
+
<button
|
|
375
|
+
type="button"
|
|
376
|
+
onClick={handleBulkUnpublish}
|
|
377
|
+
className="flex-1 sm:flex-none px-3 py-1.5 text-sm bg-yellow-600 text-white rounded-lg hover:bg-yellow-700 transition-colors"
|
|
378
|
+
>
|
|
379
|
+
Unpublish
|
|
380
|
+
</button>
|
|
381
|
+
<button
|
|
382
|
+
type="button"
|
|
383
|
+
onClick={handleBulkDelete}
|
|
384
|
+
className="flex-1 sm:flex-none px-3 py-1.5 text-sm bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
|
|
385
|
+
>
|
|
386
|
+
Delete
|
|
387
|
+
</button>
|
|
388
|
+
<button
|
|
389
|
+
type="button"
|
|
390
|
+
onClick={() => setSelectedPages([])}
|
|
391
|
+
className="flex-1 sm:flex-none px-3 py-1.5 text-sm border border-gray-300 bg-white rounded-lg hover:bg-gray-50 transition-colors"
|
|
392
|
+
>
|
|
393
|
+
Cancel
|
|
394
|
+
</button>
|
|
307
395
|
</div>
|
|
308
396
|
</div>
|
|
309
397
|
</div>
|
|
@@ -315,10 +403,15 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
315
403
|
{folderSel.type === 'smart' && folderSel.smart === 'uncategorized'
|
|
316
404
|
? 'No uncategorized pages'
|
|
317
405
|
: folderSel.type === 'folder'
|
|
318
|
-
|
|
319
|
-
|
|
406
|
+
? 'No pages in this folder'
|
|
407
|
+
: 'No pages yet'}
|
|
320
408
|
</p>
|
|
321
|
-
<button
|
|
409
|
+
<button
|
|
410
|
+
onClick={() => onNavigate?.('/pages/new')}
|
|
411
|
+
className="px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
|
412
|
+
>
|
|
413
|
+
Create your first page
|
|
414
|
+
</button>
|
|
322
415
|
</div>
|
|
323
416
|
) : (
|
|
324
417
|
<>
|
|
@@ -328,22 +421,44 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
328
421
|
<thead className="bg-gray-50 border-b border-gray-200 sticky top-0">
|
|
329
422
|
<tr>
|
|
330
423
|
<th className="w-8 px-3 py-2 text-left">
|
|
331
|
-
<input
|
|
424
|
+
<input
|
|
425
|
+
type="checkbox"
|
|
426
|
+
checked={
|
|
427
|
+
selectedPages.length === filteredAndSorted.length &&
|
|
428
|
+
filteredAndSorted.length > 0
|
|
429
|
+
}
|
|
430
|
+
onChange={(e) => handleSelectAll(e.target.checked)}
|
|
431
|
+
className="rounded border-gray-300"
|
|
432
|
+
/>
|
|
332
433
|
</th>
|
|
333
434
|
<th className="w-6 px-1 py-2"></th>
|
|
334
|
-
<th className="px-3 py-2 text-left"
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
<th className="px-3 py-2 text-left text-xs font-medium text-gray-700">
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
<th className="px-3 py-2 text-left
|
|
435
|
+
<th className="px-3 py-2 text-left">
|
|
436
|
+
<SortHeader label="Title" sortKey="title" />
|
|
437
|
+
</th>
|
|
438
|
+
<th className="px-3 py-2 text-left text-xs font-medium text-gray-700">
|
|
439
|
+
Folder
|
|
440
|
+
</th>
|
|
441
|
+
<th className="px-3 py-2 text-left">
|
|
442
|
+
<SortHeader label="Author" sortKey="author" />
|
|
443
|
+
</th>
|
|
444
|
+
<th className="px-3 py-2 text-left text-xs font-medium text-gray-700">
|
|
445
|
+
SEO
|
|
446
|
+
</th>
|
|
447
|
+
<th className="px-3 py-2 text-left">
|
|
448
|
+
<SortHeader label="Status" sortKey="status" />
|
|
449
|
+
</th>
|
|
450
|
+
<th className="px-3 py-2 text-left">
|
|
451
|
+
<SortHeader label="Date" sortKey="date" />
|
|
452
|
+
</th>
|
|
453
|
+
<th className="px-3 py-2 text-left text-xs font-medium text-gray-700">
|
|
454
|
+
Actions
|
|
455
|
+
</th>
|
|
341
456
|
</tr>
|
|
342
457
|
</thead>
|
|
343
458
|
<tbody className="divide-y divide-gray-200">
|
|
344
459
|
{filteredAndSorted.map((page: any) => {
|
|
345
|
-
const seoScore = computeSeoScore(page.data)
|
|
346
|
-
const folderName = page.folder?.name ?? page.folderName
|
|
460
|
+
const seoScore = computeSeoScore(page.data)
|
|
461
|
+
const folderName = page.folder?.name ?? page.folderName
|
|
347
462
|
return (
|
|
348
463
|
<tr
|
|
349
464
|
key={page.id}
|
|
@@ -352,37 +467,66 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
352
467
|
onDragStart={(e) => handleDragStart(e, page.id)}
|
|
353
468
|
>
|
|
354
469
|
<td className="px-3 py-2">
|
|
355
|
-
<input
|
|
470
|
+
<input
|
|
471
|
+
type="checkbox"
|
|
472
|
+
checked={selectedPages.includes(page.id)}
|
|
473
|
+
onChange={() => handleSelectPage(page.id)}
|
|
474
|
+
className="rounded border-gray-300"
|
|
475
|
+
/>
|
|
356
476
|
</td>
|
|
357
477
|
<td className="px-1 py-2 cursor-grab">
|
|
358
478
|
<GripVertical className="w-4 h-4 text-gray-300" />
|
|
359
479
|
</td>
|
|
360
480
|
<td className="px-3 py-2">
|
|
361
|
-
<button
|
|
481
|
+
<button
|
|
482
|
+
type="button"
|
|
483
|
+
onClick={() => onNavigate?.(`/pages/${page.id}`)}
|
|
484
|
+
className="font-medium text-gray-900 hover:text-blue-600 text-sm text-left"
|
|
485
|
+
>
|
|
486
|
+
{page.title}
|
|
487
|
+
</button>
|
|
362
488
|
</td>
|
|
363
489
|
<td className="px-3 py-2">
|
|
364
|
-
{folderName ?
|
|
490
|
+
{folderName ? (
|
|
491
|
+
<FolderBadge name={folderName} />
|
|
492
|
+
) : (
|
|
493
|
+
<span className="text-xs text-gray-400">—</span>
|
|
494
|
+
)}
|
|
365
495
|
</td>
|
|
366
496
|
<td className="px-3 py-2 text-sm text-gray-600">{page.author}</td>
|
|
367
497
|
<td className="px-3 py-2">
|
|
368
498
|
<SeoScoreBadge score={seoScore} />
|
|
369
499
|
</td>
|
|
370
500
|
<td className="px-3 py-2">
|
|
371
|
-
<span
|
|
501
|
+
<span
|
|
502
|
+
className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${page.status === 'Published' || page.status === 'PUBLISHED' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`}
|
|
503
|
+
>
|
|
504
|
+
{page.status}
|
|
505
|
+
</span>
|
|
372
506
|
</td>
|
|
373
507
|
<td className="px-3 py-2 text-sm text-gray-600">{page.date}</td>
|
|
374
508
|
<td className="px-3 py-2">
|
|
375
509
|
<div className="flex items-center gap-2">
|
|
376
|
-
<button
|
|
510
|
+
<button
|
|
511
|
+
type="button"
|
|
512
|
+
onClick={() => onNavigate?.(`/pages/${page.id}`)}
|
|
513
|
+
className="p-1.5 hover:bg-gray-100 rounded transition-colors"
|
|
514
|
+
title="Edit"
|
|
515
|
+
>
|
|
377
516
|
<Pencil className="w-4 h-4 text-gray-600" />
|
|
378
517
|
</button>
|
|
379
|
-
<button
|
|
518
|
+
<button
|
|
519
|
+
type="button"
|
|
520
|
+
onClick={() => handleDelete(page.id)}
|
|
521
|
+
className="p-1.5 hover:bg-gray-100 rounded transition-colors"
|
|
522
|
+
title="Delete"
|
|
523
|
+
>
|
|
380
524
|
<Trash2 className="w-4 h-4 text-red-600" />
|
|
381
525
|
</button>
|
|
382
526
|
</div>
|
|
383
527
|
</td>
|
|
384
528
|
</tr>
|
|
385
|
-
)
|
|
529
|
+
)
|
|
386
530
|
})}
|
|
387
531
|
</tbody>
|
|
388
532
|
</table>
|
|
@@ -392,8 +536,8 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
392
536
|
<div className="md:hidden bg-white rounded-lg border border-gray-200 flex-1 overflow-auto">
|
|
393
537
|
<div className="divide-y divide-gray-200">
|
|
394
538
|
{filteredAndSorted.map((page: any) => {
|
|
395
|
-
const seoScore = computeSeoScore(page.data)
|
|
396
|
-
const folderName = page.folder?.name ?? page.folderName
|
|
539
|
+
const seoScore = computeSeoScore(page.data)
|
|
540
|
+
const folderName = page.folder?.name ?? page.folderName
|
|
397
541
|
return (
|
|
398
542
|
<div
|
|
399
543
|
key={page.id}
|
|
@@ -402,24 +546,43 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
402
546
|
onDragStart={(e) => handleDragStart(e, page.id)}
|
|
403
547
|
>
|
|
404
548
|
<div className="flex items-start gap-3">
|
|
405
|
-
<input
|
|
549
|
+
<input
|
|
550
|
+
type="checkbox"
|
|
551
|
+
checked={selectedPages.includes(page.id)}
|
|
552
|
+
onChange={() => handleSelectPage(page.id)}
|
|
553
|
+
className="rounded border-gray-300 mt-1"
|
|
554
|
+
/>
|
|
406
555
|
<div className="flex-1 min-w-0">
|
|
407
|
-
<button
|
|
556
|
+
<button
|
|
557
|
+
type="button"
|
|
558
|
+
onClick={() => onNavigate?.(`/pages/${page.id}`)}
|
|
559
|
+
className="font-medium text-sm text-gray-900 hover:text-blue-600 block mb-1 text-left"
|
|
560
|
+
>
|
|
561
|
+
{page.title}
|
|
562
|
+
</button>
|
|
408
563
|
<div className="flex flex-wrap items-center gap-2 text-xs text-gray-600 mb-2">
|
|
409
564
|
{folderName && <FolderBadge name={folderName} />}
|
|
410
565
|
<span>{page.author}</span>
|
|
411
566
|
<span>·</span>
|
|
412
567
|
<span>{page.date}</span>
|
|
413
568
|
<SeoScoreBadge score={seoScore} />
|
|
414
|
-
<span
|
|
569
|
+
<span
|
|
570
|
+
className={`px-2 py-0.5 rounded-full text-xs font-medium ${page.status === 'Published' || page.status === 'PUBLISHED' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`}
|
|
571
|
+
>
|
|
572
|
+
{page.status}
|
|
573
|
+
</span>
|
|
415
574
|
</div>
|
|
416
575
|
</div>
|
|
417
|
-
<button
|
|
576
|
+
<button
|
|
577
|
+
type="button"
|
|
578
|
+
onClick={() => handleDelete(page.id)}
|
|
579
|
+
className="p-1.5 hover:bg-gray-100 rounded transition-colors"
|
|
580
|
+
>
|
|
418
581
|
<Trash2 className="w-4 h-4 text-red-600" />
|
|
419
582
|
</button>
|
|
420
583
|
</div>
|
|
421
584
|
</div>
|
|
422
|
-
)
|
|
585
|
+
)
|
|
423
586
|
})}
|
|
424
587
|
</div>
|
|
425
588
|
</div>
|
|
@@ -428,5 +591,5 @@ export function Pages({ onNavigate }: PagesProps) {
|
|
|
428
591
|
</div>
|
|
429
592
|
</div>
|
|
430
593
|
</div>
|
|
431
|
-
)
|
|
594
|
+
)
|
|
432
595
|
}
|