@canvas-digital/blog-sdk 2.0.0
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/README.md +110 -0
- package/dist/BlogPlatform.d.ts +40 -0
- package/dist/BlogPlatform.d.ts.map +1 -0
- package/dist/BlogPlatform.js +38 -0
- package/dist/admin/components/BlogAdmin.d.ts +10 -0
- package/dist/admin/components/BlogAdmin.d.ts.map +1 -0
- package/dist/admin/components/BlogAdmin.js +36 -0
- package/dist/admin/components/CategoryManager.d.ts +5 -0
- package/dist/admin/components/CategoryManager.d.ts.map +1 -0
- package/dist/admin/components/CategoryManager.js +43 -0
- package/dist/admin/components/MediaManager.d.ts +7 -0
- package/dist/admin/components/MediaManager.d.ts.map +1 -0
- package/dist/admin/components/MediaManager.js +37 -0
- package/dist/admin/components/PostEditor.d.ts +7 -0
- package/dist/admin/components/PostEditor.d.ts.map +1 -0
- package/dist/admin/components/PostEditor.js +160 -0
- package/dist/admin/components/PostList.d.ts +8 -0
- package/dist/admin/components/PostList.d.ts.map +1 -0
- package/dist/admin/components/PostList.js +32 -0
- package/dist/admin/components/TagManager.d.ts +5 -0
- package/dist/admin/components/TagManager.d.ts.map +1 -0
- package/dist/admin/components/TagManager.js +41 -0
- package/dist/admin/components/editor/RichTextEditor.d.ts +8 -0
- package/dist/admin/components/editor/RichTextEditor.d.ts.map +1 -0
- package/dist/admin/components/editor/RichTextEditor.js +47 -0
- package/dist/admin/components/internal/States.d.ts +16 -0
- package/dist/admin/components/internal/States.d.ts.map +1 -0
- package/dist/admin/components/internal/States.js +15 -0
- package/dist/admin/hooks/useAsync.d.ts +23 -0
- package/dist/admin/hooks/useAsync.d.ts.map +1 -0
- package/dist/admin/hooks/useAsync.js +40 -0
- package/dist/admin/index.d.ts +10 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +9 -0
- package/dist/admin/theme.d.ts +6 -0
- package/dist/admin/theme.d.ts.map +1 -0
- package/dist/admin/theme.js +52 -0
- package/dist/admin/types.d.ts +27 -0
- package/dist/admin/types.d.ts.map +1 -0
- package/dist/admin/types.js +1 -0
- package/dist/blog-client.iife.js +1 -0
- package/dist/client/admin-client.d.ts +33 -0
- package/dist/client/admin-client.d.ts.map +1 -0
- package/dist/client/admin-client.js +153 -0
- package/dist/client/errors.d.ts +14 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +26 -0
- package/dist/client/http.d.ts +12 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/http.js +135 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +6 -0
- package/dist/client/pagination.d.ts +4 -0
- package/dist/client/pagination.d.ts.map +1 -0
- package/dist/client/pagination.js +32 -0
- package/dist/client/public-client.d.ts +23 -0
- package/dist/client/public-client.d.ts.map +1 -0
- package/dist/client/public-client.js +94 -0
- package/dist/client/types.d.ts +115 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +1 -0
- package/dist/components/BlogUploader/BackButton.d.ts +5 -0
- package/dist/components/BlogUploader/BackButton.d.ts.map +1 -0
- package/dist/components/BlogUploader/BackButton.js +27 -0
- package/dist/components/BlogUploader/CommentSection.d.ts +12 -0
- package/dist/components/BlogUploader/CommentSection.d.ts.map +1 -0
- package/dist/components/BlogUploader/CommentSection.js +65 -0
- package/dist/components/BlogUploader/PostCard.d.ts +17 -0
- package/dist/components/BlogUploader/PostCard.d.ts.map +1 -0
- package/dist/components/BlogUploader/PostCard.js +42 -0
- package/dist/components/BlogUploader/PostFormatHero.d.ts +7 -0
- package/dist/components/BlogUploader/PostFormatHero.d.ts.map +1 -0
- package/dist/components/BlogUploader/PostFormatHero.js +47 -0
- package/dist/components/BlogUploader/ReadingProgress.d.ts +9 -0
- package/dist/components/BlogUploader/ReadingProgress.d.ts.map +1 -0
- package/dist/components/BlogUploader/ReadingProgress.js +28 -0
- package/dist/components/BlogUploader/RichText.d.ts +5 -0
- package/dist/components/BlogUploader/RichText.d.ts.map +1 -0
- package/dist/components/BlogUploader/RichText.js +59 -0
- package/dist/components/BlogUploader/ShareRow.d.ts +12 -0
- package/dist/components/BlogUploader/ShareRow.d.ts.map +1 -0
- package/dist/components/BlogUploader/ShareRow.js +13 -0
- package/dist/components/BlogUploader/ViewBeacon.d.ts +12 -0
- package/dist/components/BlogUploader/ViewBeacon.d.ts.map +1 -0
- package/dist/components/BlogUploader/ViewBeacon.js +37 -0
- package/dist/components/BlogUploader/dashboard/BrandSettings.d.ts +28 -0
- package/dist/components/BlogUploader/dashboard/BrandSettings.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/BrandSettings.js +60 -0
- package/dist/components/BlogUploader/dashboard/BrandSwitcher.d.ts +14 -0
- package/dist/components/BlogUploader/dashboard/BrandSwitcher.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/BrandSwitcher.js +25 -0
- package/dist/components/BlogUploader/dashboard/Chips.d.ts +15 -0
- package/dist/components/BlogUploader/dashboard/Chips.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/Chips.js +15 -0
- package/dist/components/BlogUploader/dashboard/CommentsManager.d.ts +19 -0
- package/dist/components/BlogUploader/dashboard/CommentsManager.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/CommentsManager.js +32 -0
- package/dist/components/BlogUploader/dashboard/LogOutButton.d.ts +4 -0
- package/dist/components/BlogUploader/dashboard/LogOutButton.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/LogOutButton.js +22 -0
- package/dist/components/BlogUploader/dashboard/MediaLibrary.d.ts +16 -0
- package/dist/components/BlogUploader/dashboard/MediaLibrary.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/MediaLibrary.js +124 -0
- package/dist/components/BlogUploader/dashboard/MediaPicker.d.ts +19 -0
- package/dist/components/BlogUploader/dashboard/MediaPicker.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/MediaPicker.js +99 -0
- package/dist/components/BlogUploader/dashboard/PostsTable.d.ts +46 -0
- package/dist/components/BlogUploader/dashboard/PostsTable.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/PostsTable.js +109 -0
- package/dist/components/BlogUploader/dashboard/Shell.d.ts +25 -0
- package/dist/components/BlogUploader/dashboard/Shell.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/Shell.js +128 -0
- package/dist/components/BlogUploader/dashboard/TermManager.d.ts +29 -0
- package/dist/components/BlogUploader/dashboard/TermManager.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/TermManager.js +96 -0
- package/dist/components/BlogUploader/dashboard/ToastProvider.d.ts +6 -0
- package/dist/components/BlogUploader/dashboard/ToastProvider.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/ToastProvider.js +5 -0
- package/dist/components/BlogUploader/dashboard/UsersManager.d.ts +32 -0
- package/dist/components/BlogUploader/dashboard/UsersManager.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/UsersManager.js +98 -0
- package/dist/components/BlogUploader/dashboard/charts.d.ts +22 -0
- package/dist/components/BlogUploader/dashboard/charts.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/charts.js +79 -0
- package/dist/components/BlogUploader/dashboard/editor/FormatContentPanel.d.ts +25 -0
- package/dist/components/BlogUploader/dashboard/editor/FormatContentPanel.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/FormatContentPanel.js +62 -0
- package/dist/components/BlogUploader/dashboard/editor/PostEditor.d.ts +95 -0
- package/dist/components/BlogUploader/dashboard/editor/PostEditor.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/PostEditor.js +649 -0
- package/dist/components/BlogUploader/dashboard/editor/RichTextEditor.d.ts +37 -0
- package/dist/components/BlogUploader/dashboard/editor/RichTextEditor.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/RichTextEditor.js +475 -0
- package/dist/components/BlogUploader/dashboard/editor/SeoAnalysisPanel.d.ts +11 -0
- package/dist/components/BlogUploader/dashboard/editor/SeoAnalysisPanel.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/SeoAnalysisPanel.js +31 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastCollapsible.d.ts +12 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastCollapsible.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastCollapsible.js +5 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastInsightsPanel.d.ts +4 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastInsightsPanel.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastInsightsPanel.js +31 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastInternalLinksPanel.d.ts +4 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastInternalLinksPanel.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastInternalLinksPanel.js +35 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSeoAnalysisPanel.d.ts +11 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSeoAnalysisPanel.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSeoAnalysisPanel.js +23 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSeoMetabox.d.ts +50 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSeoMetabox.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSeoMetabox.js +101 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSnippetField.d.ts +22 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSnippetField.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/YoastSnippetField.js +51 -0
- package/dist/components/BlogUploader/dashboard/editor/load.d.ts +10 -0
- package/dist/components/BlogUploader/dashboard/editor/load.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/editor/load.js +141 -0
- package/dist/components/BlogUploader/dashboard/loadTerms.d.ts +13 -0
- package/dist/components/BlogUploader/dashboard/loadTerms.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/loadTerms.js +41 -0
- package/dist/components/BlogUploader/dashboard/seo/BrandSeoActions.d.ts +8 -0
- package/dist/components/BlogUploader/dashboard/seo/BrandSeoActions.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/BrandSeoActions.js +35 -0
- package/dist/components/BlogUploader/dashboard/seo/Ga4Settings.d.ts +9 -0
- package/dist/components/BlogUploader/dashboard/seo/Ga4Settings.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/Ga4Settings.js +40 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectAnalyticsDashboard.d.ts +5 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectAnalyticsDashboard.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectAnalyticsDashboard.js +29 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectDetail.d.ts +10 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectDetail.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectDetail.js +31 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectForm.d.ts +27 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectForm.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectForm.js +79 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectList.d.ts +20 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectList.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/RedirectList.js +33 -0
- package/dist/components/BlogUploader/dashboard/seo/RobotsEditor.d.ts +11 -0
- package/dist/components/BlogUploader/dashboard/seo/RobotsEditor.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/RobotsEditor.js +54 -0
- package/dist/components/BlogUploader/dashboard/seo/SearchConsoleSettings.d.ts +9 -0
- package/dist/components/BlogUploader/dashboard/seo/SearchConsoleSettings.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/SearchConsoleSettings.js +38 -0
- package/dist/components/BlogUploader/dashboard/seo/SeoBrandList.d.ts +28 -0
- package/dist/components/BlogUploader/dashboard/seo/SeoBrandList.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/SeoBrandList.js +29 -0
- package/dist/components/BlogUploader/dashboard/seo/SeoBrandLists.d.ts +17 -0
- package/dist/components/BlogUploader/dashboard/seo/SeoBrandLists.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/SeoBrandLists.js +8 -0
- package/dist/components/BlogUploader/dashboard/seo/shared.d.ts +18 -0
- package/dist/components/BlogUploader/dashboard/seo/shared.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/seo/shared.js +6 -0
- package/dist/components/BlogUploader/dashboard/tools/ExportTool.d.ts +8 -0
- package/dist/components/BlogUploader/dashboard/tools/ExportTool.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/tools/ExportTool.js +78 -0
- package/dist/components/BlogUploader/dashboard/tools/ImportTool.d.ts +7 -0
- package/dist/components/BlogUploader/dashboard/tools/ImportTool.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/tools/ImportTool.js +70 -0
- package/dist/components/BlogUploader/dashboard/upload.d.ts +47 -0
- package/dist/components/BlogUploader/dashboard/upload.d.ts.map +1 -0
- package/dist/components/BlogUploader/dashboard/upload.js +68 -0
- package/dist/components/BlogUploader/site/ArticleDetailHero.d.ts +27 -0
- package/dist/components/BlogUploader/site/ArticleDetailHero.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/ArticleDetailHero.js +13 -0
- package/dist/components/BlogUploader/site/ArticleRailDetails.d.ts +27 -0
- package/dist/components/BlogUploader/site/ArticleRailDetails.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/ArticleRailDetails.js +17 -0
- package/dist/components/BlogUploader/site/BlogIndexView.d.ts +28 -0
- package/dist/components/BlogUploader/site/BlogIndexView.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/BlogIndexView.js +34 -0
- package/dist/components/BlogUploader/site/BrandTableOfContents.d.ts +18 -0
- package/dist/components/BlogUploader/site/BrandTableOfContents.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/BrandTableOfContents.js +15 -0
- package/dist/components/BlogUploader/site/HeroFadeSlide.d.ts +11 -0
- package/dist/components/BlogUploader/site/HeroFadeSlide.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/HeroFadeSlide.js +3 -0
- package/dist/components/BlogUploader/site/HeroMediaVisual.d.ts +23 -0
- package/dist/components/BlogUploader/site/HeroMediaVisual.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/HeroMediaVisual.js +50 -0
- package/dist/components/BlogUploader/site/IndexFeedSection.d.ts +14 -0
- package/dist/components/BlogUploader/site/IndexFeedSection.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/IndexFeedSection.js +12 -0
- package/dist/components/BlogUploader/site/InfinitePostFeed.d.ts +23 -0
- package/dist/components/BlogUploader/site/InfinitePostFeed.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/InfinitePostFeed.js +152 -0
- package/dist/components/BlogUploader/site/SiteMediaHero.d.ts +12 -0
- package/dist/components/BlogUploader/site/SiteMediaHero.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/SiteMediaHero.js +80 -0
- package/dist/components/BlogUploader/site/SiteMotion.d.ts +12 -0
- package/dist/components/BlogUploader/site/SiteMotion.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/SiteMotion.js +27 -0
- package/dist/components/BlogUploader/site/SiteNavAuth.d.ts +8 -0
- package/dist/components/BlogUploader/site/SiteNavAuth.d.ts.map +1 -0
- package/dist/components/BlogUploader/site/SiteNavAuth.js +36 -0
- package/dist/components/admin/AdminPostDetail.d.ts +7 -0
- package/dist/components/admin/AdminPostDetail.d.ts.map +1 -0
- package/dist/components/admin/AdminPostDetail.js +105 -0
- package/dist/components/admin/BrandSettings.d.ts +11 -0
- package/dist/components/admin/BrandSettings.d.ts.map +1 -0
- package/dist/components/admin/BrandSettings.js +71 -0
- package/dist/components/admin/BrandSwitcher.d.ts +3 -0
- package/dist/components/admin/BrandSwitcher.d.ts.map +1 -0
- package/dist/components/admin/BrandSwitcher.js +12 -0
- package/dist/components/admin/CategoryManager.d.ts +2 -0
- package/dist/components/admin/CategoryManager.d.ts.map +1 -0
- package/dist/components/admin/CategoryManager.js +1 -0
- package/dist/components/admin/Chips.d.ts +15 -0
- package/dist/components/admin/Chips.d.ts.map +1 -0
- package/dist/components/admin/Chips.js +15 -0
- package/dist/components/admin/CommentsManager.d.ts +6 -0
- package/dist/components/admin/CommentsManager.d.ts.map +1 -0
- package/dist/components/admin/CommentsManager.js +30 -0
- package/dist/components/admin/Dashboard.d.ts +3 -0
- package/dist/components/admin/Dashboard.d.ts.map +1 -0
- package/dist/components/admin/Dashboard.js +34 -0
- package/dist/components/admin/MediaLibrary.d.ts +4 -0
- package/dist/components/admin/MediaLibrary.d.ts.map +1 -0
- package/dist/components/admin/MediaLibrary.js +119 -0
- package/dist/components/admin/MediaManager.d.ts +2 -0
- package/dist/components/admin/MediaManager.d.ts.map +1 -0
- package/dist/components/admin/MediaManager.js +1 -0
- package/dist/components/admin/MediaPicker.d.ts +10 -0
- package/dist/components/admin/MediaPicker.d.ts.map +1 -0
- package/dist/components/admin/MediaPicker.js +86 -0
- package/dist/components/admin/PostEditor.d.ts +2 -0
- package/dist/components/admin/PostEditor.d.ts.map +1 -0
- package/dist/components/admin/PostEditor.js +1 -0
- package/dist/components/admin/PostList.d.ts +13 -0
- package/dist/components/admin/PostList.d.ts.map +1 -0
- package/dist/components/admin/PostList.js +93 -0
- package/dist/components/admin/PostsTable.d.ts +15 -0
- package/dist/components/admin/PostsTable.d.ts.map +1 -0
- package/dist/components/admin/PostsTable.js +106 -0
- package/dist/components/admin/Shell.d.ts +18 -0
- package/dist/components/admin/Shell.d.ts.map +1 -0
- package/dist/components/admin/Shell.js +99 -0
- package/dist/components/admin/TagManager.d.ts +2 -0
- package/dist/components/admin/TagManager.d.ts.map +1 -0
- package/dist/components/admin/TagManager.js +1 -0
- package/dist/components/admin/TermManager.d.ts +9 -0
- package/dist/components/admin/TermManager.d.ts.map +1 -0
- package/dist/components/admin/TermManager.js +105 -0
- package/dist/components/admin/UsersManager.d.ts +5 -0
- package/dist/components/admin/UsersManager.d.ts.map +1 -0
- package/dist/components/admin/UsersManager.js +96 -0
- package/dist/components/admin/api/mappers.d.ts +37 -0
- package/dist/components/admin/api/mappers.d.ts.map +1 -0
- package/dist/components/admin/api/mappers.js +174 -0
- package/dist/components/admin/charts.d.ts +30 -0
- package/dist/components/admin/charts.d.ts.map +1 -0
- package/dist/components/admin/charts.js +79 -0
- package/dist/components/admin/editor/FormatContentPanel.d.ts +25 -0
- package/dist/components/admin/editor/FormatContentPanel.d.ts.map +1 -0
- package/dist/components/admin/editor/FormatContentPanel.js +62 -0
- package/dist/components/admin/editor/PostEditor.d.ts +5 -0
- package/dist/components/admin/editor/PostEditor.d.ts.map +1 -0
- package/dist/components/admin/editor/PostEditor.js +404 -0
- package/dist/components/admin/editor/RichTextEditor.d.ts +42 -0
- package/dist/components/admin/editor/RichTextEditor.d.ts.map +1 -0
- package/dist/components/admin/editor/RichTextEditor.js +475 -0
- package/dist/components/admin/editor/SeoAnalysisPanel.d.ts +11 -0
- package/dist/components/admin/editor/SeoAnalysisPanel.d.ts.map +1 -0
- package/dist/components/admin/editor/SeoAnalysisPanel.js +31 -0
- package/dist/components/admin/editor/YoastCollapsible.d.ts +12 -0
- package/dist/components/admin/editor/YoastCollapsible.d.ts.map +1 -0
- package/dist/components/admin/editor/YoastCollapsible.js +5 -0
- package/dist/components/admin/editor/YoastInsightsPanel.d.ts +4 -0
- package/dist/components/admin/editor/YoastInsightsPanel.d.ts.map +1 -0
- package/dist/components/admin/editor/YoastInsightsPanel.js +31 -0
- package/dist/components/admin/editor/YoastInternalLinksPanel.d.ts +4 -0
- package/dist/components/admin/editor/YoastInternalLinksPanel.d.ts.map +1 -0
- package/dist/components/admin/editor/YoastInternalLinksPanel.js +35 -0
- package/dist/components/admin/editor/YoastSeoAnalysisPanel.d.ts +11 -0
- package/dist/components/admin/editor/YoastSeoAnalysisPanel.d.ts.map +1 -0
- package/dist/components/admin/editor/YoastSeoAnalysisPanel.js +23 -0
- package/dist/components/admin/editor/YoastSeoMetabox.d.ts +54 -0
- package/dist/components/admin/editor/YoastSeoMetabox.d.ts.map +1 -0
- package/dist/components/admin/editor/YoastSeoMetabox.js +101 -0
- package/dist/components/admin/editor/YoastSnippetField.d.ts +22 -0
- package/dist/components/admin/editor/YoastSnippetField.d.ts.map +1 -0
- package/dist/components/admin/editor/YoastSnippetField.js +51 -0
- package/dist/components/admin/editor/load.d.ts +10 -0
- package/dist/components/admin/editor/load.d.ts.map +1 -0
- package/dist/components/admin/editor/load.js +141 -0
- package/dist/components/admin/index.d.ts +28 -0
- package/dist/components/admin/index.d.ts.map +1 -0
- package/dist/components/admin/index.js +27 -0
- package/dist/components/admin/internal/States.d.ts +16 -0
- package/dist/components/admin/internal/States.d.ts.map +1 -0
- package/dist/components/admin/internal/States.js +15 -0
- package/dist/components/admin/lib/format.d.ts +7 -0
- package/dist/components/admin/lib/format.d.ts.map +1 -0
- package/dist/components/admin/lib/format.js +43 -0
- package/dist/components/admin/lib/publishing.d.ts +8 -0
- package/dist/components/admin/lib/publishing.d.ts.map +1 -0
- package/dist/components/admin/lib/publishing.js +10 -0
- package/dist/components/admin/lib/robots.d.ts +13 -0
- package/dist/components/admin/lib/robots.d.ts.map +1 -0
- package/dist/components/admin/lib/robots.js +22 -0
- package/dist/components/admin/mock/fixtures.d.ts +240 -0
- package/dist/components/admin/mock/fixtures.d.ts.map +1 -0
- package/dist/components/admin/mock/fixtures.js +546 -0
- package/dist/components/admin/mock/store.d.ts +139 -0
- package/dist/components/admin/mock/store.d.ts.map +1 -0
- package/dist/components/admin/mock/store.js +1067 -0
- package/dist/components/admin/seo/Ga4Settings.d.ts +34 -0
- package/dist/components/admin/seo/Ga4Settings.d.ts.map +1 -0
- package/dist/components/admin/seo/Ga4Settings.js +80 -0
- package/dist/components/admin/seo/RedirectAnalyticsDashboard.d.ts +5 -0
- package/dist/components/admin/seo/RedirectAnalyticsDashboard.d.ts.map +1 -0
- package/dist/components/admin/seo/RedirectAnalyticsDashboard.js +24 -0
- package/dist/components/admin/seo/RedirectDetail.d.ts +11 -0
- package/dist/components/admin/seo/RedirectDetail.d.ts.map +1 -0
- package/dist/components/admin/seo/RedirectDetail.js +30 -0
- package/dist/components/admin/seo/RedirectForm.d.ts +29 -0
- package/dist/components/admin/seo/RedirectForm.d.ts.map +1 -0
- package/dist/components/admin/seo/RedirectForm.js +89 -0
- package/dist/components/admin/seo/RedirectList.d.ts +10 -0
- package/dist/components/admin/seo/RedirectList.d.ts.map +1 -0
- package/dist/components/admin/seo/RedirectList.js +28 -0
- package/dist/components/admin/seo/RobotsEditor.d.ts +5 -0
- package/dist/components/admin/seo/RobotsEditor.d.ts.map +1 -0
- package/dist/components/admin/seo/RobotsEditor.js +50 -0
- package/dist/components/admin/seo/SearchConsoleSettings.d.ts +3 -0
- package/dist/components/admin/seo/SearchConsoleSettings.d.ts.map +1 -0
- package/dist/components/admin/seo/SearchConsoleSettings.js +1 -0
- package/dist/components/admin/seo/SeoBrandList.d.ts +3 -0
- package/dist/components/admin/seo/SeoBrandList.d.ts.map +1 -0
- package/dist/components/admin/seo/SeoBrandList.js +1 -0
- package/dist/components/admin/seo/shared.d.ts +17 -0
- package/dist/components/admin/seo/shared.d.ts.map +1 -0
- package/dist/components/admin/seo/shared.js +7 -0
- package/dist/components/admin/tools/ExportTool.d.ts +3 -0
- package/dist/components/admin/tools/ExportTool.d.ts.map +1 -0
- package/dist/components/admin/tools/ExportTool.js +1 -0
- package/dist/components/admin/tools/ImportTool.d.ts +3 -0
- package/dist/components/admin/tools/ImportTool.d.ts.map +1 -0
- package/dist/components/admin/tools/ImportTool.js +1 -0
- package/dist/components/admin/tools/ToolsHub.d.ts +10 -0
- package/dist/components/admin/tools/ToolsHub.d.ts.map +1 -0
- package/dist/components/admin/tools/ToolsHub.js +106 -0
- package/dist/components/admin/tools/index.d.ts +3 -0
- package/dist/components/admin/tools/index.d.ts.map +1 -0
- package/dist/components/admin/tools/index.js +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +2 -0
- package/dist/components/user/BlogCard.d.ts +20 -0
- package/dist/components/user/BlogCard.d.ts.map +1 -0
- package/dist/components/user/BlogCard.js +22 -0
- package/dist/components/user/BlogCategories.d.ts +6 -0
- package/dist/components/user/BlogCategories.d.ts.map +1 -0
- package/dist/components/user/BlogCategories.js +11 -0
- package/dist/components/user/BlogDetail.d.ts +25 -0
- package/dist/components/user/BlogDetail.d.ts.map +1 -0
- package/dist/components/user/BlogDetail.js +90 -0
- package/dist/components/user/BlogList.d.ts +20 -0
- package/dist/components/user/BlogList.d.ts.map +1 -0
- package/dist/components/user/BlogList.js +56 -0
- package/dist/components/user/BlogSearch.d.ts +8 -0
- package/dist/components/user/BlogSearch.d.ts.map +1 -0
- package/dist/components/user/BlogSearch.js +19 -0
- package/dist/components/user/BlogTags.d.ts +6 -0
- package/dist/components/user/BlogTags.d.ts.map +1 -0
- package/dist/components/user/BlogTags.js +11 -0
- package/dist/components/user/FeaturedBlogs.d.ts +8 -0
- package/dist/components/user/FeaturedBlogs.d.ts.map +1 -0
- package/dist/components/user/FeaturedBlogs.js +12 -0
- package/dist/components/user/RelatedBlogs.d.ts +9 -0
- package/dist/components/user/RelatedBlogs.d.ts.map +1 -0
- package/dist/components/user/RelatedBlogs.js +12 -0
- package/dist/components/user/SiteMediaHero.d.ts +14 -0
- package/dist/components/user/SiteMediaHero.d.ts.map +1 -0
- package/dist/components/user/SiteMediaHero.js +43 -0
- package/dist/components/user/detail/ArticleDetailHero.d.ts +30 -0
- package/dist/components/user/detail/ArticleDetailHero.d.ts.map +1 -0
- package/dist/components/user/detail/ArticleDetailHero.js +29 -0
- package/dist/components/user/detail/ArticleRailDetails.d.ts +24 -0
- package/dist/components/user/detail/ArticleRailDetails.d.ts.map +1 -0
- package/dist/components/user/detail/ArticleRailDetails.js +12 -0
- package/dist/components/user/detail/BackButton.d.ts +7 -0
- package/dist/components/user/detail/BackButton.d.ts.map +1 -0
- package/dist/components/user/detail/BackButton.js +18 -0
- package/dist/components/user/detail/CommentSection.d.ts +28 -0
- package/dist/components/user/detail/CommentSection.d.ts.map +1 -0
- package/dist/components/user/detail/CommentSection.js +68 -0
- package/dist/components/user/detail/HeroFadeSlide.d.ts +12 -0
- package/dist/components/user/detail/HeroFadeSlide.d.ts.map +1 -0
- package/dist/components/user/detail/HeroFadeSlide.js +5 -0
- package/dist/components/user/detail/ReadingProgress.d.ts +2 -0
- package/dist/components/user/detail/ReadingProgress.d.ts.map +1 -0
- package/dist/components/user/detail/ReadingProgress.js +20 -0
- package/dist/components/user/detail/ShareRow.d.ts +7 -0
- package/dist/components/user/detail/ShareRow.d.ts.map +1 -0
- package/dist/components/user/detail/ShareRow.js +7 -0
- package/dist/components/user/detail/toc.d.ts +14 -0
- package/dist/components/user/detail/toc.d.ts.map +1 -0
- package/dist/components/user/detail/toc.js +106 -0
- package/dist/components/user/index.d.ts +14 -0
- package/dist/components/user/index.d.ts.map +1 -0
- package/dist/components/user/index.js +13 -0
- package/dist/components/user/internal/States.d.ts +18 -0
- package/dist/components/user/internal/States.d.ts.map +1 -0
- package/dist/components/user/internal/States.js +21 -0
- package/dist/constants/index.d.ts +4 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +3 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useAsync.d.ts +23 -0
- package/dist/hooks/useAsync.d.ts.map +1 -0
- package/dist/hooks/useAsync.js +40 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/lib/access.d.ts +64 -0
- package/dist/lib/access.d.ts.map +1 -0
- package/dist/lib/access.js +127 -0
- package/dist/lib/canonical.d.ts +15 -0
- package/dist/lib/canonical.d.ts.map +1 -0
- package/dist/lib/canonical.js +34 -0
- package/dist/lib/comments.d.ts +9 -0
- package/dist/lib/comments.d.ts.map +1 -0
- package/dist/lib/comments.js +23 -0
- package/dist/lib/dashboard.d.ts +39 -0
- package/dist/lib/dashboard.d.ts.map +1 -0
- package/dist/lib/dashboard.js +157 -0
- package/dist/lib/data-transfer.d.ts +23 -0
- package/dist/lib/data-transfer.d.ts.map +1 -0
- package/dist/lib/data-transfer.js +436 -0
- package/dist/lib/default-author.d.ts +12 -0
- package/dist/lib/default-author.d.ts.map +1 -0
- package/dist/lib/default-author.js +57 -0
- package/dist/lib/format-media.d.ts +6 -0
- package/dist/lib/format-media.d.ts.map +1 -0
- package/dist/lib/format-media.js +18 -0
- package/dist/lib/format.d.ts +7 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +43 -0
- package/dist/lib/hero-post.d.ts +24 -0
- package/dist/lib/hero-post.d.ts.map +1 -0
- package/dist/lib/hero-post.js +95 -0
- package/dist/lib/lexical-build.d.ts +30 -0
- package/dist/lib/lexical-build.d.ts.map +1 -0
- package/dist/lib/lexical-build.js +57 -0
- package/dist/lib/lexical-doc.d.ts +88 -0
- package/dist/lib/lexical-doc.d.ts.map +1 -0
- package/dist/lib/lexical-doc.js +385 -0
- package/dist/lib/lexical-dom.d.ts +19 -0
- package/dist/lib/lexical-dom.d.ts.map +1 -0
- package/dist/lib/lexical-dom.js +181 -0
- package/dist/lib/lexical.d.ts +31 -0
- package/dist/lib/lexical.d.ts.map +1 -0
- package/dist/lib/lexical.js +59 -0
- package/dist/lib/post-categories.d.ts +11 -0
- package/dist/lib/post-categories.d.ts.map +1 -0
- package/dist/lib/post-categories.js +27 -0
- package/dist/lib/post-format.d.ts +39 -0
- package/dist/lib/post-format.d.ts.map +1 -0
- package/dist/lib/post-format.js +82 -0
- package/dist/lib/publishing.d.ts +3 -0
- package/dist/lib/publishing.d.ts.map +1 -0
- package/dist/lib/publishing.js +1 -0
- package/dist/lib/queries.d.ts +49 -0
- package/dist/lib/queries.d.ts.map +1 -0
- package/dist/lib/queries.js +340 -0
- package/dist/lib/redirect-analytics.d.ts +27 -0
- package/dist/lib/redirect-analytics.d.ts.map +1 -0
- package/dist/lib/redirect-analytics.js +147 -0
- package/dist/lib/redirects.d.ts +39 -0
- package/dist/lib/redirects.d.ts.map +1 -0
- package/dist/lib/redirects.js +96 -0
- package/dist/lib/revalidate.d.ts +30 -0
- package/dist/lib/revalidate.d.ts.map +1 -0
- package/dist/lib/revalidate.js +91 -0
- package/dist/lib/robots.d.ts +19 -0
- package/dist/lib/robots.d.ts.map +1 -0
- package/dist/lib/robots.js +38 -0
- package/dist/lib/seo-analysis.d.ts +28 -0
- package/dist/lib/seo-analysis.d.ts.map +1 -0
- package/dist/lib/seo-analysis.js +153 -0
- package/dist/lib/seo-limits.d.ts +19 -0
- package/dist/lib/seo-limits.d.ts.map +1 -0
- package/dist/lib/seo-limits.js +39 -0
- package/dist/lib/seo.d.ts +50 -0
- package/dist/lib/seo.d.ts.map +1 -0
- package/dist/lib/seo.js +238 -0
- package/dist/lib/slug.d.ts +3 -0
- package/dist/lib/slug.d.ts.map +1 -0
- package/dist/lib/slug.js +9 -0
- package/dist/lib/stats.d.ts +71 -0
- package/dist/lib/stats.d.ts.map +1 -0
- package/dist/lib/stats.js +207 -0
- package/dist/lib/tenant-host.d.ts +9 -0
- package/dist/lib/tenant-host.d.ts.map +1 -0
- package/dist/lib/tenant-host.js +15 -0
- package/dist/lib/tenant.d.ts +41 -0
- package/dist/lib/tenant.d.ts.map +1 -0
- package/dist/lib/tenant.js +140 -0
- package/dist/lib/toc.d.ts +16 -0
- package/dist/lib/toc.d.ts.map +1 -0
- package/dist/lib/toc.js +28 -0
- package/dist/lib/yoast-snippet.d.ts +25 -0
- package/dist/lib/yoast-snippet.d.ts.map +1 -0
- package/dist/lib/yoast-snippet.js +79 -0
- package/dist/pages/admin/BlogAdmin.d.ts +9 -0
- package/dist/pages/admin/BlogAdmin.d.ts.map +1 -0
- package/dist/pages/admin/BlogAdmin.js +234 -0
- package/dist/pages/admin/index.d.ts +2 -0
- package/dist/pages/admin/index.d.ts.map +1 -0
- package/dist/pages/admin/index.js +1 -0
- package/dist/pages/index.d.ts +3 -0
- package/dist/pages/index.d.ts.map +1 -0
- package/dist/pages/index.js +2 -0
- package/dist/pages/user/UserBlogPage.d.ts +10 -0
- package/dist/pages/user/UserBlogPage.d.ts.map +1 -0
- package/dist/pages/user/UserBlogPage.js +15 -0
- package/dist/pages/user/index.d.ts +2 -0
- package/dist/pages/user/index.d.ts.map +1 -0
- package/dist/pages/user/index.js +1 -0
- package/dist/services/admin-client.d.ts +33 -0
- package/dist/services/admin-client.d.ts.map +1 -0
- package/dist/services/admin-client.js +154 -0
- package/dist/services/admin-sdk-client.d.ts +285 -0
- package/dist/services/admin-sdk-client.d.ts.map +1 -0
- package/dist/services/admin-sdk-client.js +242 -0
- package/dist/services/errors.d.ts +14 -0
- package/dist/services/errors.d.ts.map +1 -0
- package/dist/services/errors.js +26 -0
- package/dist/services/http.d.ts +24 -0
- package/dist/services/http.d.ts.map +1 -0
- package/dist/services/http.js +159 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +7 -0
- package/dist/services/pagination.d.ts +4 -0
- package/dist/services/pagination.d.ts.map +1 -0
- package/dist/services/pagination.js +32 -0
- package/dist/services/public-client.d.ts +23 -0
- package/dist/services/public-client.d.ts.map +1 -0
- package/dist/services/public-client.js +94 -0
- package/dist/services/types.d.ts +124 -0
- package/dist/services/types.d.ts.map +1 -0
- package/dist/services/types.js +1 -0
- package/dist/style.css +3105 -0
- package/dist/styles/adminTheme.d.ts +6 -0
- package/dist/styles/adminTheme.d.ts.map +1 -0
- package/dist/styles/adminTheme.js +52 -0
- package/dist/styles/index.d.ts +3 -0
- package/dist/styles/index.d.ts.map +1 -0
- package/dist/styles/index.js +2 -0
- package/dist/styles/userTheme.d.ts +8 -0
- package/dist/styles/userTheme.d.ts.map +1 -0
- package/dist/styles/userTheme.js +60 -0
- package/dist/types/admin.d.ts +28 -0
- package/dist/types/admin.d.ts.map +1 -0
- package/dist/types/admin.js +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/user.d.ts +41 -0
- package/dist/types/user.d.ts.map +1 -0
- package/dist/types/user.js +1 -0
- package/dist/user/components/BlogCard.d.ts +15 -0
- package/dist/user/components/BlogCard.d.ts.map +1 -0
- package/dist/user/components/BlogCard.js +31 -0
- package/dist/user/components/BlogCategories.d.ts +6 -0
- package/dist/user/components/BlogCategories.d.ts.map +1 -0
- package/dist/user/components/BlogCategories.js +11 -0
- package/dist/user/components/BlogDetail.d.ts +7 -0
- package/dist/user/components/BlogDetail.d.ts.map +1 -0
- package/dist/user/components/BlogDetail.js +19 -0
- package/dist/user/components/BlogList.d.ts +8 -0
- package/dist/user/components/BlogList.d.ts.map +1 -0
- package/dist/user/components/BlogList.js +18 -0
- package/dist/user/components/BlogSearch.d.ts +8 -0
- package/dist/user/components/BlogSearch.d.ts.map +1 -0
- package/dist/user/components/BlogSearch.js +19 -0
- package/dist/user/components/BlogTags.d.ts +6 -0
- package/dist/user/components/BlogTags.d.ts.map +1 -0
- package/dist/user/components/BlogTags.js +11 -0
- package/dist/user/components/FeaturedBlogs.d.ts +8 -0
- package/dist/user/components/FeaturedBlogs.d.ts.map +1 -0
- package/dist/user/components/FeaturedBlogs.js +12 -0
- package/dist/user/components/RelatedBlogs.d.ts +9 -0
- package/dist/user/components/RelatedBlogs.d.ts.map +1 -0
- package/dist/user/components/RelatedBlogs.js +12 -0
- package/dist/user/components/internal/States.d.ts +18 -0
- package/dist/user/components/internal/States.d.ts.map +1 -0
- package/dist/user/components/internal/States.js +16 -0
- package/dist/user/hooks/useAsync.d.ts +21 -0
- package/dist/user/hooks/useAsync.d.ts.map +1 -0
- package/dist/user/hooks/useAsync.js +40 -0
- package/dist/user/index.d.ts +11 -0
- package/dist/user/index.d.ts.map +1 -0
- package/dist/user/index.js +10 -0
- package/dist/user/sanitize.d.ts +2 -0
- package/dist/user/sanitize.d.ts.map +1 -0
- package/dist/user/sanitize.js +120 -0
- package/dist/user/theme.d.ts +8 -0
- package/dist/user/theme.d.ts.map +1 -0
- package/dist/user/theme.js +60 -0
- package/dist/user/types.d.ts +41 -0
- package/dist/user/types.d.ts.map +1 -0
- package/dist/user/types.js +1 -0
- package/dist/utils/env.d.ts +16 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +57 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/sanitize.d.ts +2 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +120 -0
- package/package.json +63 -0
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import dynamic from 'next/dynamic';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { useRouter } from 'next/navigation';
|
|
6
|
+
import Link from 'next/link';
|
|
7
|
+
import { toast } from 'react-toastify';
|
|
8
|
+
import { MediaPicker } from '../MediaPicker';
|
|
9
|
+
import { uploadImages } from '../upload';
|
|
10
|
+
import { savePost, trashPosts, duplicatePost } from '../../../app/(dashboard)/_actions/posts';
|
|
11
|
+
import { saveTerm, searchLinkTargets, updateMedia } from '../../../app/(dashboard)/_actions/library';
|
|
12
|
+
import { publishBlockers } from '../../../lib/publishing';
|
|
13
|
+
import { resolveMetaDescription, resolveSeoTitle } from '../../../lib/yoast-snippet';
|
|
14
|
+
import { seoFieldLengthErrors } from '../../../lib/seo-limits';
|
|
15
|
+
import { validateCanonicalOverride } from '../../../lib/canonical';
|
|
16
|
+
import { formatDateTime, formatDate } from '../../../lib/format';
|
|
17
|
+
import { useSeoSummary } from './SeoAnalysisPanel';
|
|
18
|
+
import { YoastSeoMetabox } from './YoastSeoMetabox';
|
|
19
|
+
import { postPath, slugify } from '../../../lib/slug';
|
|
20
|
+
import { FormatContentPanel } from './FormatContentPanel';
|
|
21
|
+
const RichTextEditor = dynamic(() => import('./RichTextEditor').then((mod) => mod.RichTextEditor), {
|
|
22
|
+
ssr: false,
|
|
23
|
+
loading: () => (_jsxs("div", { className: "ed", "aria-busy": "true", "aria-label": "Loading editor", children: [_jsxs("div", { className: "ed-mode-tabs", children: [_jsx("span", { className: "ed-mode-skeleton" }), _jsx("span", { className: "ed-mode-skeleton" })] }), _jsx("div", { className: "ed-surface", children: _jsx("div", { className: "ed-input prose ed-skeleton" }) })] })),
|
|
24
|
+
});
|
|
25
|
+
const toLocalInput = (iso) => {
|
|
26
|
+
const d = iso ? new Date(iso) : new Date(Date.now() + 60 * 60 * 1000);
|
|
27
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
28
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
29
|
+
};
|
|
30
|
+
const sinceLabel = (at) => {
|
|
31
|
+
if (!at)
|
|
32
|
+
return '';
|
|
33
|
+
const secs = Math.round((Date.now() - at.getTime()) / 1000);
|
|
34
|
+
if (secs < 10)
|
|
35
|
+
return 'just now';
|
|
36
|
+
if (secs < 60)
|
|
37
|
+
return `${secs}s ago`;
|
|
38
|
+
if (secs < 3600)
|
|
39
|
+
return `${Math.round(secs / 60)}m ago`;
|
|
40
|
+
return formatDateTime(at);
|
|
41
|
+
};
|
|
42
|
+
const ScoreDot = ({ tone }) => (_jsx("span", { className: `yoast-dot yoast-dot-${tone}`, "aria-hidden": "true" }));
|
|
43
|
+
export const PostEditor = ({ post, options }) => {
|
|
44
|
+
const router = useRouter();
|
|
45
|
+
const [form, setForm] = useState(post);
|
|
46
|
+
const [newTags, setNewTags] = useState([]);
|
|
47
|
+
const [tagOptions, setTagOptions] = useState(options.tags);
|
|
48
|
+
const [tagDraft, setTagDraft] = useState('');
|
|
49
|
+
const [newCategory, setNewCategory] = useState('');
|
|
50
|
+
const [categoryOptions, setCategoryOptions] = useState(options.categories);
|
|
51
|
+
const [categoryTab, setCategoryTab] = useState('all');
|
|
52
|
+
const [authorOptions, setAuthorOptions] = useState(options.authors);
|
|
53
|
+
const [pickersBusy, setPickersBusy] = useState(false);
|
|
54
|
+
const [slugEditing, setSlugEditing] = useState(false);
|
|
55
|
+
const [dirty, setDirty] = useState(false);
|
|
56
|
+
const [busy, setBusy] = useState(null);
|
|
57
|
+
const [error, setError] = useState(null);
|
|
58
|
+
const [fieldErrors, setFieldErrors] = useState({});
|
|
59
|
+
const [savedAt, setSavedAt] = useState(post.updatedAt ? new Date(post.updatedAt) : null);
|
|
60
|
+
const [, forceTick] = useState(0);
|
|
61
|
+
const [scheduleWhen, setScheduleWhen] = useState('');
|
|
62
|
+
const [showSchedule, setShowSchedule] = useState(Boolean(post.scheduledFor));
|
|
63
|
+
const [showStatus, setShowStatus] = useState(false);
|
|
64
|
+
const [showVisibility, setShowVisibility] = useState(false);
|
|
65
|
+
const [hydrated, setHydrated] = useState(false);
|
|
66
|
+
const [previewUrl, setPreviewUrl] = useState(null);
|
|
67
|
+
const [previewLoading, setPreviewLoading] = useState(false);
|
|
68
|
+
const contentRef = useRef(undefined);
|
|
69
|
+
const [words, setWords] = useState(0);
|
|
70
|
+
const [contentText, setContentText] = useState('');
|
|
71
|
+
const [contentHtml, setContentHtml] = useState(post.contentHtml || '');
|
|
72
|
+
const [pickerFor, setPickerFor] = useState(null);
|
|
73
|
+
const [featuredBusy, setFeaturedBusy] = useState(false);
|
|
74
|
+
const [featuredDrag, setFeaturedDrag] = useState(false);
|
|
75
|
+
const [featuredAltBusy, setFeaturedAltBusy] = useState(false);
|
|
76
|
+
const [featuredAltSaved, setFeaturedAltSaved] = useState(false);
|
|
77
|
+
const featuredFileRef = useRef(null);
|
|
78
|
+
const featuredAltRef = useRef(null);
|
|
79
|
+
const inlineResolver = useRef(null);
|
|
80
|
+
const set = (key, value) => {
|
|
81
|
+
setForm((f) => ({ ...f, [key]: value }));
|
|
82
|
+
setDirty(true);
|
|
83
|
+
};
|
|
84
|
+
const setSeo = (key, value) => {
|
|
85
|
+
setForm((f) => ({ ...f, seo: { ...f.seo, [key]: value } }));
|
|
86
|
+
setDirty(true);
|
|
87
|
+
};
|
|
88
|
+
const setFormatMeta = (key, value) => {
|
|
89
|
+
setForm((f) => ({ ...f, formatMeta: { ...f.formatMeta, [key]: value } }));
|
|
90
|
+
setDirty(true);
|
|
91
|
+
};
|
|
92
|
+
/* The publish checklist is the server's list, not a second opinion. */
|
|
93
|
+
const blockers = useMemo(() => publishBlockers({
|
|
94
|
+
postFormat: form.postFormat,
|
|
95
|
+
featuredImage: form.featuredImage?.id ?? null,
|
|
96
|
+
category: form.category,
|
|
97
|
+
authors: form.authors,
|
|
98
|
+
excerpt: form.excerpt,
|
|
99
|
+
seo: { metaDescription: form.seo.metaDescription },
|
|
100
|
+
formatMeta: {
|
|
101
|
+
videoMediaId: form.formatMeta.videoMedia?.id ?? null,
|
|
102
|
+
audioMediaId: form.formatMeta.audioMedia?.id ?? null,
|
|
103
|
+
quoteText: form.formatMeta.quoteText,
|
|
104
|
+
quoteSource: form.formatMeta.quoteSource,
|
|
105
|
+
linkUrl: form.formatMeta.linkUrl,
|
|
106
|
+
linkTitle: form.formatMeta.linkTitle,
|
|
107
|
+
galleryIds: form.gallery.map((g) => g.id),
|
|
108
|
+
},
|
|
109
|
+
}), [form.postFormat, form.featuredImage, form.category, form.authors, form.excerpt, form.seo.metaDescription, form.formatMeta, form.gallery]);
|
|
110
|
+
const isLive = form.status === 'published';
|
|
111
|
+
const canPublish = options.canPublish;
|
|
112
|
+
const onContentChange = useCallback((doc, stats) => {
|
|
113
|
+
contentRef.current = doc;
|
|
114
|
+
setWords(stats.words);
|
|
115
|
+
setContentHtml(stats.html || '');
|
|
116
|
+
setDirty(true);
|
|
117
|
+
const parts = [];
|
|
118
|
+
const walk = (nodes) => {
|
|
119
|
+
if (!Array.isArray(nodes))
|
|
120
|
+
return;
|
|
121
|
+
for (const n of nodes) {
|
|
122
|
+
if (!n || typeof n !== 'object')
|
|
123
|
+
continue;
|
|
124
|
+
const node = n;
|
|
125
|
+
if (node.type === 'text' && typeof node.text === 'string')
|
|
126
|
+
parts.push(node.text);
|
|
127
|
+
if (node.children)
|
|
128
|
+
walk(node.children);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
walk(doc?.root?.children);
|
|
132
|
+
setContentText(parts.join(' '));
|
|
133
|
+
}, []);
|
|
134
|
+
const save = useCallback(async (intent, opts = {}) => {
|
|
135
|
+
setBusy(intent);
|
|
136
|
+
setError(null);
|
|
137
|
+
setFieldErrors({});
|
|
138
|
+
const canonicalCheck = validateCanonicalOverride(form.seo.canonicalOverride);
|
|
139
|
+
if (canonicalCheck !== true) {
|
|
140
|
+
setBusy(null);
|
|
141
|
+
setError(canonicalCheck);
|
|
142
|
+
setFieldErrors({ 'seo.canonicalOverride': canonicalCheck });
|
|
143
|
+
document.getElementById('seo-canonical')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const seoLengthErrors = seoFieldLengthErrors(form.seo);
|
|
147
|
+
if (seoLengthErrors.length) {
|
|
148
|
+
setBusy(null);
|
|
149
|
+
setError(seoLengthErrors[0].message);
|
|
150
|
+
const next = {};
|
|
151
|
+
for (const f of seoLengthErrors)
|
|
152
|
+
next[f.field] = f.message;
|
|
153
|
+
setFieldErrors(next);
|
|
154
|
+
document.getElementById('yoast-seo-title')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const res = await savePost({
|
|
158
|
+
id: form.id,
|
|
159
|
+
tenant: form.tenant,
|
|
160
|
+
title: form.title,
|
|
161
|
+
slug: form.slug,
|
|
162
|
+
excerpt: form.excerpt,
|
|
163
|
+
content: contentRef.current,
|
|
164
|
+
featuredImage: form.featuredImage?.id ?? null,
|
|
165
|
+
category: form.category,
|
|
166
|
+
tags: form.tags,
|
|
167
|
+
newTags,
|
|
168
|
+
authors: form.authors,
|
|
169
|
+
scheduledFor: intent === 'schedule' ? new Date(scheduleWhen).toISOString() : form.scheduledFor,
|
|
170
|
+
publishedAt: form.publishedAt,
|
|
171
|
+
seo: {
|
|
172
|
+
metaTitle: form.seo.metaTitle,
|
|
173
|
+
metaDescription: form.seo.metaDescription,
|
|
174
|
+
focusKeyphrase: form.seo.focusKeyphrase,
|
|
175
|
+
breadcrumbTitle: form.seo.breadcrumbTitle,
|
|
176
|
+
canonicalOverride: form.seo.canonicalOverride,
|
|
177
|
+
noindex: form.seo.noindex || form.visibility === 'private',
|
|
178
|
+
nofollow: form.seo.nofollow,
|
|
179
|
+
ogImage: form.seo.ogImage?.id ?? null,
|
|
180
|
+
ogTitle: form.seo.ogTitle,
|
|
181
|
+
ogDescription: form.seo.ogDescription,
|
|
182
|
+
schemaType: form.seo.schemaType,
|
|
183
|
+
},
|
|
184
|
+
postFormat: form.postFormat,
|
|
185
|
+
formatMeta: {
|
|
186
|
+
videoMediaId: form.formatMeta.videoMedia?.id ?? null,
|
|
187
|
+
audioMediaId: form.formatMeta.audioMedia?.id ?? null,
|
|
188
|
+
quoteText: form.formatMeta.quoteText,
|
|
189
|
+
quoteSource: form.formatMeta.quoteSource,
|
|
190
|
+
linkUrl: form.formatMeta.linkUrl,
|
|
191
|
+
linkTitle: form.formatMeta.linkTitle,
|
|
192
|
+
galleryIds: form.gallery.map((g) => g.id),
|
|
193
|
+
},
|
|
194
|
+
template: form.template,
|
|
195
|
+
visibility: form.visibility,
|
|
196
|
+
authorBox: form.authorBox,
|
|
197
|
+
allowComments: form.allowComments,
|
|
198
|
+
showInTableOfContents: form.showInTableOfContents,
|
|
199
|
+
intent,
|
|
200
|
+
});
|
|
201
|
+
setBusy(null);
|
|
202
|
+
if (!res.ok) {
|
|
203
|
+
setError(res.error);
|
|
204
|
+
if (res.fields?.length) {
|
|
205
|
+
const next = {};
|
|
206
|
+
for (const f of res.fields)
|
|
207
|
+
if (f.field)
|
|
208
|
+
next[f.field] = f.message;
|
|
209
|
+
setFieldErrors(next);
|
|
210
|
+
if (next['seo.canonicalOverride']) {
|
|
211
|
+
document.getElementById('seo-canonical')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
212
|
+
}
|
|
213
|
+
else if (next['seo.metaTitle'] || next['seo.metaDescription'] || next['seo.ogTitle'] || next['seo.ogDescription']) {
|
|
214
|
+
document.getElementById('yoast-seo-title')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
const saved = res.data;
|
|
220
|
+
const wasDraft = form.status !== 'published';
|
|
221
|
+
if (intent === 'publish' && saved.status === 'published' && wasDraft) {
|
|
222
|
+
const tenantSlug = options.tenants.find((t) => t.id === form.tenant)?.slug;
|
|
223
|
+
const params = new URLSearchParams({ state: 'live' });
|
|
224
|
+
if (tenantSlug)
|
|
225
|
+
params.set('brand', tenantSlug);
|
|
226
|
+
toast.success('Post published successfully.');
|
|
227
|
+
router.push(`/admin/posts?${params.toString()}`);
|
|
228
|
+
return saved;
|
|
229
|
+
}
|
|
230
|
+
setNewTags([]);
|
|
231
|
+
setForm((f) => ({
|
|
232
|
+
...f,
|
|
233
|
+
id: saved.id,
|
|
234
|
+
slug: saved.slug,
|
|
235
|
+
status: saved.status,
|
|
236
|
+
reviewState: saved.reviewState,
|
|
237
|
+
scheduledFor: saved.scheduledFor,
|
|
238
|
+
publishedAt: saved.publishedAt,
|
|
239
|
+
tags: saved.tagIds,
|
|
240
|
+
}));
|
|
241
|
+
setSavedAt(new Date());
|
|
242
|
+
setDirty(false);
|
|
243
|
+
if (!form.id && saved.id) {
|
|
244
|
+
// A new post gets its own address as soon as it exists, so a refresh or a
|
|
245
|
+
// shared link lands on the post rather than on an empty form.
|
|
246
|
+
window.history.replaceState(null, '', `/admin/posts/${saved.id}`);
|
|
247
|
+
}
|
|
248
|
+
if (!opts.silent)
|
|
249
|
+
router.refresh();
|
|
250
|
+
return saved;
|
|
251
|
+
}, [form, newTags, router, scheduleWhen, options.tenants]);
|
|
252
|
+
const closePreview = useCallback(() => {
|
|
253
|
+
setPreviewUrl(null);
|
|
254
|
+
setPreviewLoading(false);
|
|
255
|
+
}, []);
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (!previewUrl)
|
|
258
|
+
return;
|
|
259
|
+
const html = document.documentElement;
|
|
260
|
+
const body = document.body;
|
|
261
|
+
const prevHtmlOverflow = html.style.overflow;
|
|
262
|
+
const prevBodyOverflow = body.style.overflow;
|
|
263
|
+
html.style.overflow = 'hidden';
|
|
264
|
+
body.style.overflow = 'hidden';
|
|
265
|
+
html.classList.add('ed-preview-open');
|
|
266
|
+
body.classList.add('ed-preview-open');
|
|
267
|
+
return () => {
|
|
268
|
+
html.style.overflow = prevHtmlOverflow;
|
|
269
|
+
body.style.overflow = prevBodyOverflow;
|
|
270
|
+
html.classList.remove('ed-preview-open');
|
|
271
|
+
body.classList.remove('ed-preview-open');
|
|
272
|
+
};
|
|
273
|
+
}, [previewUrl]);
|
|
274
|
+
const openPreview = useCallback(async () => {
|
|
275
|
+
if (!form.title.trim()) {
|
|
276
|
+
setError('Add a title before previewing.');
|
|
277
|
+
document.getElementById('post-title-input')?.focus();
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const brandSlug = options.tenants.find((t) => t.id === form.tenant)?.slug;
|
|
281
|
+
if (!brandSlug) {
|
|
282
|
+
setError('Choose a brand before previewing.');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
setPreviewLoading(true);
|
|
286
|
+
setError(null);
|
|
287
|
+
let slug = form.slug || slugify(form.title);
|
|
288
|
+
if (!form.id || dirty) {
|
|
289
|
+
const saved = await save(isLive ? 'publish' : 'save', { silent: true });
|
|
290
|
+
if (!saved) {
|
|
291
|
+
setPreviewLoading(false);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
slug = saved.slug;
|
|
295
|
+
}
|
|
296
|
+
// In-page preview of this post only — same admin layout, no new tab.
|
|
297
|
+
const href = `/api/preview?slug=${encodeURIComponent(slug)}&tenant=${encodeURIComponent(brandSlug)}`;
|
|
298
|
+
setPreviewUrl(href);
|
|
299
|
+
setPreviewLoading(false);
|
|
300
|
+
}, [form.title, form.slug, form.id, form.tenant, dirty, isLive, save, options.tenants]);
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
if (!previewUrl)
|
|
303
|
+
return;
|
|
304
|
+
const onKey = (e) => {
|
|
305
|
+
if (e.key === 'Escape')
|
|
306
|
+
closePreview();
|
|
307
|
+
};
|
|
308
|
+
window.addEventListener('keydown', onKey);
|
|
309
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
310
|
+
}, [previewUrl, closePreview]);
|
|
311
|
+
const reloadPickersForBrand = useCallback(async (tenantId) => {
|
|
312
|
+
if (!tenantId)
|
|
313
|
+
return;
|
|
314
|
+
setPickersBusy(true);
|
|
315
|
+
try {
|
|
316
|
+
// Fetch via route handler — a Server Action would refresh the RSC tree and
|
|
317
|
+
// remount this editor (wiping draft state) on every brand change.
|
|
318
|
+
const res = await fetch(`/api/dashboard/editor-pickers?tenant=${encodeURIComponent(tenantId)}`, { credentials: 'same-origin' });
|
|
319
|
+
const json = (await res.json());
|
|
320
|
+
if (!res.ok || !json.ok || !json.data) {
|
|
321
|
+
setError(json.error || 'Could not load categories for that brand.');
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const data = json.data;
|
|
325
|
+
setCategoryOptions(data.categories);
|
|
326
|
+
setTagOptions(data.tags);
|
|
327
|
+
setAuthorOptions(data.authors);
|
|
328
|
+
setForm((f) => {
|
|
329
|
+
const keptAuthors = f.authors.filter((id) => data.authors.some((a) => a.id === id));
|
|
330
|
+
const nextAuthors = keptAuthors.length > 0
|
|
331
|
+
? keptAuthors
|
|
332
|
+
: data.defaultAuthorId
|
|
333
|
+
? [data.defaultAuthorId]
|
|
334
|
+
: [];
|
|
335
|
+
return {
|
|
336
|
+
...f,
|
|
337
|
+
category: f.category.filter((id) => data.categories.some((c) => c.id === id)),
|
|
338
|
+
tags: f.tags.filter((id) => data.tags.some((t) => t.id === id)),
|
|
339
|
+
authors: nextAuthors,
|
|
340
|
+
};
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
setError('Could not load categories for that brand.');
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
setPickersBusy(false);
|
|
348
|
+
}
|
|
349
|
+
}, []);
|
|
350
|
+
const onBrandChange = (tenantId) => {
|
|
351
|
+
if (tenantId === form.tenant)
|
|
352
|
+
return;
|
|
353
|
+
setForm((f) => ({ ...f, tenant: tenantId, authors: [], category: [], tags: [] }));
|
|
354
|
+
setDirty(true);
|
|
355
|
+
void reloadPickersForBrand(tenantId);
|
|
356
|
+
};
|
|
357
|
+
useEffect(() => {
|
|
358
|
+
if (!form.id || isLive || !dirty || busy)
|
|
359
|
+
return;
|
|
360
|
+
const id = setTimeout(() => void save('save', { silent: true }), 25000);
|
|
361
|
+
return () => clearTimeout(id);
|
|
362
|
+
}, [form.id, isLive, dirty, busy, save]);
|
|
363
|
+
/* Local clock and relative labels belong on the client — computing them
|
|
364
|
+
during SSR uses the server timezone and Date.now(), which mismatch hydrate. */
|
|
365
|
+
useEffect(() => {
|
|
366
|
+
setHydrated(true);
|
|
367
|
+
setScheduleWhen(toLocalInput(post.scheduledFor));
|
|
368
|
+
// Mount-only: later saves must not wipe a time the editor is still typing.
|
|
369
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
370
|
+
}, []);
|
|
371
|
+
/* Keeps the "saved 2m ago" label honest without a re-render storm. */
|
|
372
|
+
useEffect(() => {
|
|
373
|
+
const id = setInterval(() => forceTick((n) => n + 1), 30000);
|
|
374
|
+
return () => clearInterval(id);
|
|
375
|
+
}, []);
|
|
376
|
+
useEffect(() => {
|
|
377
|
+
const onKey = (e) => {
|
|
378
|
+
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 's') {
|
|
379
|
+
e.preventDefault();
|
|
380
|
+
// ⌘S on a live post is "update", which republishes — same as the button.
|
|
381
|
+
void save(isLive ? 'publish' : 'save');
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
document.addEventListener('keydown', onKey);
|
|
385
|
+
return () => document.removeEventListener('keydown', onKey);
|
|
386
|
+
}, [save, isLive]);
|
|
387
|
+
useEffect(() => {
|
|
388
|
+
const warn = (e) => {
|
|
389
|
+
if (!dirty)
|
|
390
|
+
return;
|
|
391
|
+
e.preventDefault();
|
|
392
|
+
e.returnValue = '';
|
|
393
|
+
};
|
|
394
|
+
window.addEventListener('beforeunload', warn);
|
|
395
|
+
return () => window.removeEventListener('beforeunload', warn);
|
|
396
|
+
}, [dirty]);
|
|
397
|
+
const pickInlineImage = useCallback(() => {
|
|
398
|
+
setPickerFor('inline');
|
|
399
|
+
return new Promise((resolve) => {
|
|
400
|
+
inlineResolver.current = resolve;
|
|
401
|
+
});
|
|
402
|
+
}, []);
|
|
403
|
+
const closePicker = () => {
|
|
404
|
+
if (pickerFor === 'inline')
|
|
405
|
+
inlineResolver.current?.(null);
|
|
406
|
+
inlineResolver.current = null;
|
|
407
|
+
setPickerFor(null);
|
|
408
|
+
};
|
|
409
|
+
const choosePicked = (item) => {
|
|
410
|
+
const ref = { id: item.id, url: item.thumbUrl || item.url, alt: item.alt };
|
|
411
|
+
if (pickerFor === 'featured') {
|
|
412
|
+
set('featuredImage', ref);
|
|
413
|
+
setFeaturedAltSaved(false);
|
|
414
|
+
window.setTimeout(() => featuredAltRef.current?.focus(), 50);
|
|
415
|
+
}
|
|
416
|
+
if (pickerFor === 'og')
|
|
417
|
+
setSeo('ogImage', ref);
|
|
418
|
+
if (pickerFor === 'gallery') {
|
|
419
|
+
setForm((f) => ({
|
|
420
|
+
...f,
|
|
421
|
+
gallery: f.gallery.some((g) => g.id === ref.id) ? f.gallery : [...f.gallery, ref],
|
|
422
|
+
}));
|
|
423
|
+
setDirty(true);
|
|
424
|
+
}
|
|
425
|
+
if (pickerFor === 'inline') {
|
|
426
|
+
inlineResolver.current?.({ id: item.id, url: item.url || item.thumbUrl, alt: item.alt });
|
|
427
|
+
inlineResolver.current = null;
|
|
428
|
+
}
|
|
429
|
+
setPickerFor(null);
|
|
430
|
+
};
|
|
431
|
+
/** Drop or choose a file straight onto the featured-image box — the common
|
|
432
|
+
* case is "this picture, this post", not "browse the library". */
|
|
433
|
+
const uploadFeatured = async (files) => {
|
|
434
|
+
setError(null);
|
|
435
|
+
setFeaturedBusy(true);
|
|
436
|
+
setFeaturedAltSaved(false);
|
|
437
|
+
const res = await uploadImages(files, form.tenant);
|
|
438
|
+
setFeaturedBusy(false);
|
|
439
|
+
if (!res.ok) {
|
|
440
|
+
setError(res.error);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const image = res.images[0];
|
|
444
|
+
if (!image)
|
|
445
|
+
return;
|
|
446
|
+
set('featuredImage', { id: image.id, url: image.thumbnailUrl || image.url, alt: image.alt });
|
|
447
|
+
// Nudge the writer to confirm / edit alt after upload.
|
|
448
|
+
window.setTimeout(() => featuredAltRef.current?.focus(), 50);
|
|
449
|
+
};
|
|
450
|
+
const setFeaturedAltLocal = (alt) => {
|
|
451
|
+
setFeaturedAltSaved(false);
|
|
452
|
+
setForm((f) => f.featuredImage ? { ...f, featuredImage: { ...f.featuredImage, alt } } : f);
|
|
453
|
+
};
|
|
454
|
+
const saveFeaturedAlt = async () => {
|
|
455
|
+
const image = form.featuredImage;
|
|
456
|
+
if (!image)
|
|
457
|
+
return;
|
|
458
|
+
setFeaturedAltBusy(true);
|
|
459
|
+
setError(null);
|
|
460
|
+
const res = await updateMedia(image.id, { alt: image.alt });
|
|
461
|
+
setFeaturedAltBusy(false);
|
|
462
|
+
if (!res.ok) {
|
|
463
|
+
setError(res.error);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
setForm((f) => f.featuredImage ? { ...f, featuredImage: { ...f.featuredImage, alt: res.data.alt } } : f);
|
|
467
|
+
setFeaturedAltSaved(true);
|
|
468
|
+
};
|
|
469
|
+
const addTagFromDraft = () => {
|
|
470
|
+
const name = tagDraft.trim();
|
|
471
|
+
if (!name)
|
|
472
|
+
return;
|
|
473
|
+
const match = tagOptions.find((t) => t.name.toLowerCase() === name.toLowerCase());
|
|
474
|
+
if (match) {
|
|
475
|
+
if (!form.tags.includes(match.id))
|
|
476
|
+
set('tags', [...form.tags, match.id]);
|
|
477
|
+
}
|
|
478
|
+
else if (!newTags.some((t) => t.toLowerCase() === name.toLowerCase())) {
|
|
479
|
+
setNewTags((list) => [...list, name]);
|
|
480
|
+
setDirty(true);
|
|
481
|
+
}
|
|
482
|
+
setTagDraft('');
|
|
483
|
+
};
|
|
484
|
+
const addCategory = async () => {
|
|
485
|
+
const name = newCategory.trim();
|
|
486
|
+
if (!name)
|
|
487
|
+
return;
|
|
488
|
+
const res = await saveTerm({ collection: 'categories', tenant: form.tenant, name });
|
|
489
|
+
if (!res.ok) {
|
|
490
|
+
setError(res.error);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
setCategoryOptions((list) => [...list, { id: res.data.id, name: res.data.name }]);
|
|
494
|
+
set('category', form.category.includes(res.data.id) ? form.category : [...form.category, res.data.id]);
|
|
495
|
+
setNewCategory('');
|
|
496
|
+
};
|
|
497
|
+
const toggleCategory = (id) => {
|
|
498
|
+
set('category', form.category.includes(id) ? form.category.filter((c) => c !== id) : [...form.category, id]);
|
|
499
|
+
};
|
|
500
|
+
const moveToTrash = async () => {
|
|
501
|
+
if (!form.id)
|
|
502
|
+
return;
|
|
503
|
+
if (!window.confirm('Move this post to the trash? It stops being published and can be restored.'))
|
|
504
|
+
return;
|
|
505
|
+
setBusy('save');
|
|
506
|
+
const res = await trashPosts([form.id]);
|
|
507
|
+
setBusy(null);
|
|
508
|
+
if (!res.ok) {
|
|
509
|
+
setError(res.error);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
router.push('/admin/posts?state=trash');
|
|
513
|
+
};
|
|
514
|
+
const searchTargets = useCallback((q) => searchLinkTargets(form.tenant, q), [form.tenant]);
|
|
515
|
+
const activeTenant = options.tenants.find((t) => t.id === form.tenant);
|
|
516
|
+
const categoryNames = form.category
|
|
517
|
+
.map((id) => categoryOptions.find((c) => c.id === id)?.name)
|
|
518
|
+
.filter((n) => Boolean(n));
|
|
519
|
+
const categoryName = categoryNames[0];
|
|
520
|
+
const snippetCtx = useMemo(() => ({
|
|
521
|
+
title: form.title,
|
|
522
|
+
slug: form.slug || slugify(form.title),
|
|
523
|
+
excerpt: form.excerpt,
|
|
524
|
+
siteName: activeTenant?.siteName || activeTenant?.name || '',
|
|
525
|
+
categoryName,
|
|
526
|
+
focusKeyphrase: form.seo.focusKeyphrase,
|
|
527
|
+
contentText,
|
|
528
|
+
date: form.publishedAt
|
|
529
|
+
? formatDate(form.publishedAt)
|
|
530
|
+
: form.scheduledFor
|
|
531
|
+
? formatDate(form.scheduledFor)
|
|
532
|
+
: '',
|
|
533
|
+
}), [
|
|
534
|
+
form.title,
|
|
535
|
+
form.slug,
|
|
536
|
+
form.excerpt,
|
|
537
|
+
form.seo.focusKeyphrase,
|
|
538
|
+
form.publishedAt,
|
|
539
|
+
form.scheduledFor,
|
|
540
|
+
activeTenant,
|
|
541
|
+
categoryName,
|
|
542
|
+
contentText,
|
|
543
|
+
]);
|
|
544
|
+
const seoInput = useMemo(() => ({
|
|
545
|
+
title: form.title,
|
|
546
|
+
slug: form.slug || slugify(form.title),
|
|
547
|
+
excerpt: form.excerpt,
|
|
548
|
+
metaTitle: resolveSeoTitle(form.seo.metaTitle, snippetCtx),
|
|
549
|
+
metaDescription: resolveMetaDescription(form.seo.metaDescription, snippetCtx),
|
|
550
|
+
focusKeyphrase: form.seo.focusKeyphrase,
|
|
551
|
+
contentText,
|
|
552
|
+
contentHtml: form.contentHtml || contentHtml,
|
|
553
|
+
hasFeaturedImage: Boolean(form.featuredImage),
|
|
554
|
+
featuredAlt: form.featuredImage?.alt || '',
|
|
555
|
+
}), [form, contentText, contentHtml, snippetCtx]);
|
|
556
|
+
const seoSummary = useSeoSummary(seoInput);
|
|
557
|
+
const copyToDraft = async () => {
|
|
558
|
+
if (!form.id)
|
|
559
|
+
return;
|
|
560
|
+
setBusy('save');
|
|
561
|
+
const res = await duplicatePost(form.id);
|
|
562
|
+
setBusy(null);
|
|
563
|
+
if (!res.ok) {
|
|
564
|
+
setError(res.error);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
router.push(`/admin/posts/${res.data.id}`);
|
|
568
|
+
};
|
|
569
|
+
const brandSlug = options.tenants.find((t) => t.id === form.tenant)?.slug;
|
|
570
|
+
const liveHref = form.slug ? postPath(form.slug, brandSlug) : null;
|
|
571
|
+
const publishWhenLabel = isLive
|
|
572
|
+
? formatDateTime(form.publishedAt)
|
|
573
|
+
: form.scheduledFor
|
|
574
|
+
? formatDateTime(form.scheduledFor)
|
|
575
|
+
: 'Immediately';
|
|
576
|
+
return (_jsxs(_Fragment, { children: [_jsx("a", { className: "skip-link", href: "#post-editor-main", children: "Skip to editor" }), _jsxs("div", { className: "ed-wp-header", children: [_jsx(Link, { className: "linkish", href: "/admin/posts", children: "\u2190 Posts" }), _jsx("span", { className: "ed-wp-header-meta", suppressHydrationWarning: true, children: dirty ? (_jsx("span", { className: "is-dirty", children: "Unsaved changes" })) : savedAt ? (_jsxs("span", { children: ["Saved ", hydrated ? sinceLabel(savedAt) : formatDateTime(savedAt)] })) : (_jsx("span", { children: "Not saved yet" })) })] }), error ? (_jsx("p", { className: "form-error ed-error", role: "alert", children: error })) : null, _jsxs("div", { className: "ed-layout", children: [_jsxs("div", { className: "ed-main", id: "post-editor-main", children: [_jsx("label", { className: "ed-title-wrap", htmlFor: "post-title-input", children: _jsx("span", { className: "screen-reader", children: "Add title" }) }), _jsx("input", { id: "post-title-input", className: "ed-title", value: form.title, placeholder: "Add title", "aria-label": "Post title", onChange: (e) => {
|
|
577
|
+
const title = e.target.value;
|
|
578
|
+
setForm((f) => ({
|
|
579
|
+
...f,
|
|
580
|
+
title,
|
|
581
|
+
// Until the post exists, the slug tracks the title the way a
|
|
582
|
+
// writer expects. Once it is published, changing it is a
|
|
583
|
+
// deliberate act — it writes a 301 — so it stops following.
|
|
584
|
+
slug: !f.id && !slugEditing ? slugify(title) : f.slug,
|
|
585
|
+
}));
|
|
586
|
+
setDirty(true);
|
|
587
|
+
} }), _jsxs("div", { className: "ed-permalink", children: [_jsx("span", { className: "lbl", children: "Permalink" }), slugEditing ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "mono", children: "/blog/" }), _jsx("input", { className: "fld fld-inline mono", value: form.slug, onChange: (e) => set('slug', e.target.value), onBlur: () => set('slug', slugify(form.slug)), "aria-label": "URL slug" }), _jsx("button", { className: "btn btn-tiny", onClick: () => setSlugEditing(false), children: "OK" })] })) : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "mono ed-permalink-value", children: ["/blog/", form.slug || slugify(form.title) || '…'] }), _jsx("button", { className: "btn btn-tiny", onClick: () => setSlugEditing(true), children: "Edit" }), isLive && liveHref ? (_jsx("a", { className: "btn btn-tiny", href: liveHref, target: "_blank", rel: "noreferrer", children: "View \u2197" })) : null] }))] }), _jsx("div", { className: form.postFormat === 'quote' || form.postFormat === 'link' ? 'ed-compact' : undefined, children: _jsx(RichTextEditor, { initialHtml: post.contentHtml, onChange: onContentChange, onPickImage: pickInlineImage, searchTargets: searchTargets }) }), _jsx(FormatContentPanel, { format: form.postFormat, meta: form.formatMeta, tenant: form.tenant, featuredImage: form.featuredImage, gallery: form.gallery, onMetaChange: setFormatMeta, onFeaturedPick: () => setPickerFor('featured'), onFeaturedClear: () => set('featuredImage', null), onGalleryAdd: () => setPickerFor('gallery'), onGalleryRemove: (id) => set('gallery', form.gallery.filter((g) => g.id !== id)) }), _jsxs("div", { className: "box", children: [_jsxs("div", { className: "box-head", children: [_jsx("h3", { children: "Excerpt" }), _jsxs("span", { className: "box-hint", children: [form.excerpt.length, "/300"] })] }), _jsx("div", { className: "box-body", children: _jsx("textarea", { className: "fld", rows: 3, maxLength: 300, value: form.excerpt, placeholder: "One or two sentences. Used on index pages and as the meta description fallback.", onChange: (e) => set('excerpt', e.target.value) }) })] }), _jsx(YoastSeoMetabox, { title: form.title, slug: form.slug, excerpt: form.excerpt, visibility: form.visibility, featuredImage: form.featuredImage, seo: form.seo, seoInput: seoInput, siteName: activeTenant?.siteName || activeTenant?.name, titleTemplate: activeTenant?.titleTemplate, categoryName: categoryName, contentText: contentText, contentHtml: form.contentHtml || contentHtml, publishedAt: form.publishedAt, scheduledFor: form.scheduledFor, fieldErrors: fieldErrors, onSeoChange: setSeo, onSlugChange: (slug) => set('slug', slug), onSlugBlur: () => set('slug', slugify(form.slug || form.title)), onPickOgImage: () => setPickerFor('og'), onClearFieldError: (field) => setFieldErrors((prev) => {
|
|
588
|
+
if (!prev[field])
|
|
589
|
+
return prev;
|
|
590
|
+
const next = { ...prev };
|
|
591
|
+
delete next[field];
|
|
592
|
+
return next;
|
|
593
|
+
}), onSetFieldError: (field, message) => setFieldErrors((prev) => ({ ...prev, [field]: message })) })] }), _jsxs("aside", { className: "ed-side", "aria-label": "Post settings", children: [_jsxs("div", { className: "box box-publish", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Publish" }) }), _jsxs("div", { className: "box-body stack", children: [_jsxs("div", { className: "btn-row", children: [_jsx("button", { className: "btn", disabled: !!busy, onClick: () => void save(isLive ? 'publish' : 'save'), children: busy === 'save' || (busy === 'publish' && isLive) ? 'Saving…' : isLive ? 'Update' : 'Save Draft' }), _jsx("button", { type: "button", className: "btn", disabled: !!busy || previewLoading, title: "Preview this post in the current page", onClick: () => void openPreview(), children: previewLoading || busy === 'save' || busy === 'publish' ? 'Preparing…' : 'Preview' })] }), _jsxs("div", { className: "facts wp-facts", children: [_jsxs("div", { className: "fact-row", children: [_jsx("span", { className: "fact-lbl", children: "Status:" }), _jsxs("span", { className: "fact-val", children: [isLive ? 'Published' : 'Draft', ' ', _jsx("button", { type: "button", className: "linkish", onClick: () => setShowStatus((v) => !v), children: "Edit" })] })] }), showStatus ? (_jsx("div", { className: "fact-edit", children: _jsxs("select", { className: "fld fld-sm", value: isLive ? 'published' : 'draft', onChange: (e) => {
|
|
594
|
+
if (e.target.value === 'published')
|
|
595
|
+
void save('publish');
|
|
596
|
+
else if (isLive)
|
|
597
|
+
void save('unpublish');
|
|
598
|
+
}, children: [_jsx("option", { value: "draft", children: "Draft" }), _jsx("option", { value: "published", children: "Published" })] }) })) : null, _jsxs("div", { className: "fact-row", children: [_jsx("span", { className: "fact-lbl", children: "Visibility:" }), _jsxs("span", { className: "fact-val", children: [form.visibility === 'private' ? 'Private' : 'Public', ' ', _jsx("button", { type: "button", className: "linkish", onClick: () => setShowVisibility((v) => !v), children: "Edit" })] })] }), showVisibility ? (_jsx("div", { className: "fact-edit", children: _jsxs("select", { className: "fld fld-sm", value: form.visibility, onChange: (e) => set('visibility', e.target.value), children: [_jsx("option", { value: "public", children: "Public" }), _jsx("option", { value: "private", children: "Private" })] }) })) : null, _jsxs("div", { className: "fact-row", children: [_jsx("span", { className: "fact-lbl", children: form.scheduledFor && !isLive ? 'Schedule:' : 'Publish:' }), _jsxs("span", { className: "fact-val", suppressHydrationWarning: true, children: [hydrated ? publishWhenLabel : form.scheduledFor || form.publishedAt ? formatDateTime(form.scheduledFor || form.publishedAt) : 'Immediately', ' ', _jsx("button", { type: "button", className: "linkish", onClick: () => setShowSchedule((v) => !v), children: "Edit" })] })] })] }), showSchedule ? (_jsxs("div", { className: "stack", children: [_jsx("input", { type: "datetime-local", className: "fld", value: scheduleWhen, onChange: (e) => setScheduleWhen(e.target.value), "aria-label": "Publish date and time" }), _jsxs("div", { className: "btn-row", children: [_jsx("button", { className: "btn btn-primary btn-tiny", disabled: !!busy || blockers.length > 0 || !canPublish, onClick: () => void save('schedule'), children: "OK" }), _jsx("button", { className: "btn btn-tiny", onClick: () => setShowSchedule(false), children: "Cancel" }), form.scheduledFor ? (_jsx("button", { className: "btn btn-tiny", disabled: !!busy, onClick: () => void save('unschedule'), children: "Clear" })) : null] })] })) : null, _jsxs("div", { className: "seo-summary publish-seo-summary wp-publish-scores", children: [_jsxs("div", { className: "fact-row", children: [_jsx("span", { className: "fact-lbl", children: "SEO analysis:" }), _jsxs("span", { className: "fact-val", children: [_jsx(ScoreDot, { tone: seoSummary.seoTone }), " ", seoSummary.seoLabel] })] }), _jsxs("div", { className: "fact-row", children: [_jsx("span", { className: "fact-lbl", children: "Readability:" }), _jsxs("span", { className: "fact-val", children: [_jsx(ScoreDot, { tone: seoSummary.readTone }), " ", seoSummary.readLabel] })] })] }), blockers.length ? (_jsxs("p", { className: "note publish-blockers-note", children: ["Before publishing: needs ", blockers.join(', '), "."] })) : null, _jsxs("div", { className: "publish-foot", children: [_jsxs("div", { className: "publish-foot-left", children: [form.id ? (_jsx("button", { type: "button", className: "linkish", disabled: !!busy, onClick: () => void copyToDraft(), children: "Copy to a new draft" })) : null, form.id ? (_jsx("button", { type: "button", className: "btn-danger-link", onClick: () => void moveToTrash(), disabled: !!busy, children: "Move to Trash" })) : null] }), !isLive && canPublish ? (_jsx("button", { className: "btn btn-primary", disabled: !!busy || blockers.length > 0, onClick: () => void save('publish'), children: busy === 'publish' ? 'Publishing…' : 'Publish' })) : null] })] })] }), _jsxs("div", { className: "box", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Format" }) }), _jsx("div", { className: "box-body", children: _jsx("div", { className: "pick-list pick-list-auto", children: [
|
|
599
|
+
['standard', 'Standard'],
|
|
600
|
+
['video', 'Video'],
|
|
601
|
+
['audio', 'Audio'],
|
|
602
|
+
['quote', 'Quote'],
|
|
603
|
+
['gallery', 'Gallery'],
|
|
604
|
+
['image', 'Image'],
|
|
605
|
+
['link', 'Link'],
|
|
606
|
+
].map(([value, label]) => (_jsxs("label", { className: "pick-row", children: [_jsx("input", { type: "radio", name: "postFormat", checked: form.postFormat === value, onChange: () => set('postFormat', value) }), label] }, value))) }) })] }), _jsxs("div", { className: "box", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Categories" }) }), _jsxs("div", { className: "box-body stack", children: [_jsxs("div", { className: "yoast-preview-modes wp-cat-tabs", role: "tablist", "aria-label": "Category list", children: [_jsx("button", { type: "button", role: "tab", className: `yoast-preview-mode${categoryTab === 'all' ? ' is-active' : ''}`, "aria-selected": categoryTab === 'all', onClick: () => setCategoryTab('all'), children: "All Categories" }), _jsx("button", { type: "button", role: "tab", className: `yoast-preview-mode${categoryTab === 'popular' ? ' is-active' : ''}`, "aria-selected": categoryTab === 'popular', onClick: () => setCategoryTab('popular'), children: "Most Used" })] }), _jsx("div", { className: "pick-list", children: (categoryTab === 'popular' ? categoryOptions.slice(0, 8) : categoryOptions).length === 0 ? (_jsx("p", { className: "note", children: "No categories in this brand yet." })) : ((categoryTab === 'popular' ? categoryOptions.slice(0, 8) : categoryOptions).map((c) => (_jsxs("label", { className: "pick-row", children: [_jsx("input", { type: "checkbox", checked: form.category.includes(c.id), onChange: () => toggleCategory(c.id) }), c.name] }, c.id)))) }), form.category.length > 1 ? (_jsxs("p", { className: "note", children: [form.category.length, " selected \u00B7 first selected is used for breadcrumbs."] })) : null, _jsx("button", { type: "button", className: "yoast-link-btn", onClick: () => document.getElementById('new-category-input')?.focus(), children: "+ Add New Category" }), _jsxs("div", { className: "btn-row", children: [_jsx("input", { id: "new-category-input", className: "fld", placeholder: "New category", value: newCategory, onChange: (e) => setNewCategory(e.target.value), onKeyDown: (e) => {
|
|
607
|
+
if (e.key === 'Enter') {
|
|
608
|
+
e.preventDefault();
|
|
609
|
+
void addCategory();
|
|
610
|
+
}
|
|
611
|
+
} }), _jsx("button", { className: "btn", onClick: () => void addCategory(), disabled: !newCategory.trim(), children: "Add" })] })] })] }), _jsxs("div", { className: "box", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Tags" }) }), _jsxs("div", { className: "box-body stack", children: [_jsxs("div", { className: "tokens", children: [form.tags.map((id) => {
|
|
612
|
+
const tag = tagOptions.find((t) => t.id === id);
|
|
613
|
+
return (_jsxs("span", { className: "token", children: [tag?.name ?? 'tag', _jsx("button", { type: "button", "aria-label": `Remove ${tag?.name ?? 'tag'}`, onClick: () => set('tags', form.tags.filter((t) => t !== id)), children: "\u2715" })] }, id));
|
|
614
|
+
}), newTags.map((name) => (_jsxs("span", { className: "token token-new", children: [name, _jsx("button", { type: "button", "aria-label": `Remove ${name}`, onClick: () => setNewTags((list) => list.filter((t) => t !== name)), children: "\u2715" })] }, name)))] }), _jsxs("div", { className: "btn-row", children: [_jsx("input", { className: "fld", placeholder: "Add a tag and press Enter", value: tagDraft, list: "tag-suggestions", onChange: (e) => setTagDraft(e.target.value), onKeyDown: (e) => {
|
|
615
|
+
if (e.key === 'Enter' || e.key === ',') {
|
|
616
|
+
e.preventDefault();
|
|
617
|
+
addTagFromDraft();
|
|
618
|
+
}
|
|
619
|
+
} }), _jsx("datalist", { id: "tag-suggestions", children: tagOptions.map((t) => (_jsx("option", { value: t.name }, t.id))) }), _jsx("button", { className: "btn", onClick: addTagFromDraft, disabled: !tagDraft.trim(), children: "Add" })] }), tagOptions.filter((t) => !form.tags.includes(t.id)).length ? (_jsxs("div", { className: "tag-most-used", children: [_jsx("p", { className: "note", children: "Most used" }), _jsx("div", { className: "tag-cloud", children: tagOptions
|
|
620
|
+
.filter((t) => !form.tags.includes(t.id))
|
|
621
|
+
.slice(0, 12)
|
|
622
|
+
.map((t) => (_jsx("button", { type: "button", className: "tag-cloud-item", onClick: () => set('tags', [...form.tags, t.id]), children: t.name }, t.id))) })] })) : null, _jsx("p", { className: "note", children: "New tags are created when the post is saved." })] })] }), _jsxs("div", { className: "box", children: [_jsxs("div", { className: "box-head", children: [_jsx("h3", { children: "Featured image" }), _jsx("span", { className: "box-hint", children: "optional" })] }), _jsxs("div", { className: `box-body drop-target${featuredDrag ? ' is-dragging' : ''}`, onDragOver: (e) => {
|
|
623
|
+
e.preventDefault();
|
|
624
|
+
setFeaturedDrag(true);
|
|
625
|
+
}, onDragLeave: () => setFeaturedDrag(false), onDrop: (e) => {
|
|
626
|
+
e.preventDefault();
|
|
627
|
+
setFeaturedDrag(false);
|
|
628
|
+
if (e.dataTransfer.files?.length)
|
|
629
|
+
void uploadFeatured(e.dataTransfer.files);
|
|
630
|
+
}, children: [_jsx("input", { ref: featuredFileRef, type: "file", accept: "image/jpeg,image/png,image/webp,image/avif,image/gif", hidden: true, onChange: (e) => {
|
|
631
|
+
if (e.target.files?.length)
|
|
632
|
+
void uploadFeatured(e.target.files);
|
|
633
|
+
e.target.value = '';
|
|
634
|
+
} }), form.featuredImage ? (_jsxs("div", { className: "stack", children: [_jsx("img", { className: "feat", src: form.featuredImage.url, alt: form.featuredImage.alt || '' }), _jsxs("label", { className: "fld-row feat-alt", children: [_jsxs("span", { className: "fld-lbl", children: ["Alt text", ' ', _jsx("em", { className: "fld-em", children: "recommended for accessibility" })] }), _jsx("textarea", { ref: featuredAltRef, className: "fld", rows: 2, value: form.featuredImage.alt, placeholder: "Describe the image for screen readers and SEO\u2026", onChange: (e) => setFeaturedAltLocal(e.target.value), onBlur: () => {
|
|
635
|
+
if (form.featuredImage)
|
|
636
|
+
void saveFeaturedAlt();
|
|
637
|
+
}, "aria-describedby": "feat-alt-hint" }), _jsx("span", { id: "feat-alt-hint", className: "note feat-alt-foot", children: featuredAltBusy
|
|
638
|
+
? 'Saving alt text…'
|
|
639
|
+
: featuredAltSaved
|
|
640
|
+
? 'Alt text saved to the media library.'
|
|
641
|
+
: 'Saved on the image itself — used on the public post and in SEO.' })] }), _jsxs("div", { className: "btn-row", children: [_jsx("button", { type: "button", className: "btn btn-tiny", disabled: featuredAltBusy, onClick: () => void saveFeaturedAlt(), children: featuredAltBusy ? 'Saving…' : 'Save alt text' }), _jsx("button", { type: "button", className: "btn btn-tiny", onClick: () => setPickerFor('featured'), children: "Replace image" }), _jsx("button", { type: "button", className: "btn btn-tiny", disabled: featuredBusy, onClick: () => featuredFileRef.current?.click(), children: featuredBusy ? 'Uploading…' : 'Upload' }), _jsx("button", { type: "button", className: "btn btn-tiny", onClick: () => {
|
|
642
|
+
set('featuredImage', null);
|
|
643
|
+
setFeaturedAltSaved(false);
|
|
644
|
+
}, children: "Remove featured image" })] })] })) : (_jsxs("div", { className: "stack", children: [_jsx("button", { type: "button", className: "btn btn-block", onClick: () => setPickerFor('featured'), children: "Set featured image" }), _jsxs("button", { type: "button", className: "dropzone-cta", disabled: featuredBusy, onClick: () => featuredFileRef.current?.click(), children: [_jsx("span", { className: "dropzone-icon", "aria-hidden": "true", children: "\u2B06" }), _jsx("span", { children: featuredBusy ? 'Uploading…' : 'Or drop an image here to upload' }), _jsx("span", { className: "note", children: "JPEG, PNG, WebP or AVIF \u00B7 up to 12 MB \u00B7 add alt text after upload" })] })] }))] })] }), _jsxs("div", { className: "box", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Post Attributes" }) }), _jsx("div", { className: "box-body stack", children: _jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Template" }), _jsx("select", { className: "fld", value: form.template, onChange: (e) => set('template', e.target.value), children: _jsx("option", { value: "default", children: "Default template" }) })] }) })] }), _jsxs("div", { className: "box", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Authors" }) }), _jsxs("div", { className: "box-body stack", children: [_jsx("div", { className: "pick-list", children: authorOptions.length === 0 ? (_jsx("p", { className: "note", children: pickersBusy
|
|
645
|
+
? 'Loading authors…'
|
|
646
|
+
: `No authors for ${options.tenants.find((t) => t.id === form.tenant)?.name ?? 'this brand'} yet — create them on the Authors screen for this brand.` })) : (authorOptions.map((a) => (_jsxs("label", { className: "pick-row", children: [_jsx("input", { type: "checkbox", checked: form.authors.includes(a.id), onChange: (e) => set('authors', e.target.checked
|
|
647
|
+
? [...form.authors, a.id]
|
|
648
|
+
: form.authors.filter((x) => x !== a.id)) }), _jsxs("span", { children: [a.name, options.defaultAuthorId === a.id ? (_jsx("span", { className: "box-hint", children: " \u00B7 you" })) : null] })] }, a.id)))) }), _jsx("p", { className: "note", children: "You are selected by default. Add or remove others anytime." }), _jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Author Box" }), _jsx("select", { className: "fld", value: form.authorBox, onChange: (e) => set('authorBox', e.target.value), children: _jsx("option", { value: "default", children: "Default Author Box" }) })] })] })] }), _jsxs("div", { className: "box", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Discussion" }) }), _jsxs("div", { className: "box-body stack", children: [_jsxs("label", { className: "pick-row", children: [_jsx("input", { type: "checkbox", checked: form.allowComments, onChange: (e) => set('allowComments', e.target.checked) }), _jsx("span", { children: "Allow comments on this post" })] }), _jsx("p", { className: "note", children: "When checked, readers can leave comments on the published article. Uncheck to close the discussion without deleting existing comments." })] })] }), _jsxs("div", { className: "box", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Table of Contents" }) }), _jsxs("div", { className: "box-body stack", children: [_jsxs("label", { className: "pick-row", children: [_jsx("input", { type: "checkbox", checked: form.showInTableOfContents, onChange: (e) => set('showInTableOfContents', e.target.checked) }), _jsx("span", { children: "Show Table of Contents on this post" })] }), _jsx("p", { className: "note", children: "When checked, the published article shows a right-side Table of Contents listing other published posts from this brand. Leave unchecked to hide the card." })] })] }), options.tenants.length > 1 ? (_jsxs("div", { className: "box", children: [_jsx("div", { className: "box-head", children: _jsx("h3", { children: "Brand" }) }), _jsxs("div", { className: "box-body", children: [_jsx("select", { className: "fld", value: form.tenant, disabled: Boolean(form.id) || pickersBusy, onChange: (e) => onBrandChange(e.target.value), "aria-label": "Brand", children: options.tenants.map((t) => (_jsx("option", { value: t.id, children: t.name }, t.id))) }), form.id ? (_jsx("p", { className: "note", children: "A published URL belongs to its brand, so this is fixed once the post exists." })) : null] })] })) : null] })] }), _jsx(MediaPicker, { open: pickerFor !== null, tenant: form.tenant, onClose: closePicker, onSelect: choosePicked, title: pickerFor === 'featured' ? 'Featured image' : pickerFor === 'og' ? 'Social share image' : pickerFor === 'gallery' ? 'Add to gallery' : 'Insert image' }), previewUrl ? (_jsxs("div", { className: "ed-preview", role: "dialog", "aria-modal": "true", "aria-label": "Post preview", children: [_jsxs("div", { className: "ed-preview-bar", children: [_jsxs("div", { className: "ed-preview-bar-left", children: [_jsx("span", { className: "ed-preview-badge", children: "Preview" }), _jsx("span", { className: "ed-preview-title", children: form.title || 'Untitled' })] }), _jsxs("div", { className: "ed-preview-bar-right", children: [_jsx("button", { type: "button", className: "btn btn-tiny", onClick: () => void openPreview(), children: "Refresh" }), _jsx("button", { type: "button", className: "btn btn-primary btn-tiny", onClick: closePreview, children: "Back to editor" })] })] }), _jsx("iframe", { className: "ed-preview-frame", src: previewUrl, title: "Post preview" }, previewUrl)] })) : null] }));
|
|
649
|
+
};
|