@jhits/plugin-blog 0.0.18 → 0.0.20
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/api/categories.d.ts.map +1 -1
- package/dist/api/categories.js +42 -38
- package/dist/api/handler.d.ts +1 -26
- package/dist/api/handler.d.ts.map +1 -1
- package/dist/api/handler.js +81 -500
- package/dist/api/router.d.ts +0 -5
- package/dist/api/router.d.ts.map +1 -1
- package/dist/api/router.js +8 -35
- package/dist/api/service.d.ts +80 -0
- package/dist/api/service.d.ts.map +1 -0
- package/dist/api/service.js +219 -0
- package/dist/hooks/useAutoSave.d.ts +10 -0
- package/dist/hooks/useAutoSave.d.ts.map +1 -0
- package/dist/hooks/useAutoSave.js +57 -0
- package/dist/hooks/useCategories.d.ts +1 -1
- package/dist/hooks/useCategories.d.ts.map +1 -1
- package/dist/hooks/useCategories.js +15 -46
- package/dist/index.d.ts +24 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -201
- package/dist/init.d.ts +20 -7
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +8 -7
- package/dist/lib/blocks/BlockRenderer.d.ts.map +1 -1
- package/dist/lib/layouts/blocks/ColumnsBlock.d.ts.map +1 -1
- package/dist/lib/layouts/blocks/ColumnsBlock.js +30 -113
- package/dist/lib/layouts/blocks/SectionBlock.d.ts.map +1 -1
- package/dist/lib/layouts/blocks/SectionBlock.js +9 -21
- package/dist/lib/layouts/index.d.ts +3 -3
- package/dist/lib/layouts/index.js +4 -4
- package/dist/lib/mappers/apiMapper.d.ts +10 -0
- package/dist/lib/mappers/apiMapper.d.ts.map +1 -1
- package/dist/lib/mappers/apiMapper.js +47 -32
- package/dist/lib/rich-text/RichTextEditor.d.ts +4 -2
- package/dist/lib/rich-text/RichTextEditor.d.ts.map +1 -1
- package/dist/lib/rich-text/RichTextEditor.js +12 -9
- package/dist/lib/utils/config-resolver.d.ts +28 -0
- package/dist/lib/utils/config-resolver.d.ts.map +1 -0
- package/dist/lib/utils/config-resolver.js +46 -0
- package/dist/lib/utils/tree.d.ts +29 -0
- package/dist/lib/utils/tree.d.ts.map +1 -0
- package/dist/lib/utils/tree.js +129 -0
- package/dist/state/EditorContext.d.ts +3 -25
- package/dist/state/EditorContext.d.ts.map +1 -1
- package/dist/state/EditorContext.js +124 -174
- package/dist/state/reducer.d.ts +1 -5
- package/dist/state/reducer.d.ts.map +1 -1
- package/dist/state/reducer.js +128 -521
- package/dist/state/types.d.ts +12 -1
- package/dist/state/types.d.ts.map +1 -1
- package/dist/types/block.d.ts +9 -0
- package/dist/types/block.d.ts.map +1 -1
- package/dist/types/post.d.ts +17 -1
- package/dist/types/post.d.ts.map +1 -1
- package/dist/views/CanvasEditor/BlockWrapper.d.ts +5 -6
- package/dist/views/CanvasEditor/BlockWrapper.d.ts.map +1 -1
- package/dist/views/CanvasEditor/BlockWrapper.js +56 -264
- package/dist/views/CanvasEditor/CanvasEditorView.d.ts +5 -3
- package/dist/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -1
- package/dist/views/CanvasEditor/CanvasEditorView.js +55 -315
- package/dist/views/CanvasEditor/EditorBody.d.ts +6 -8
- package/dist/views/CanvasEditor/EditorBody.d.ts.map +1 -1
- package/dist/views/CanvasEditor/EditorBody.js +34 -482
- package/dist/views/CanvasEditor/EditorHeader.d.ts.map +1 -1
- package/dist/views/CanvasEditor/EditorHeader.js +27 -63
- package/dist/views/CanvasEditor/LayoutContainer.d.ts.map +1 -1
- package/dist/views/CanvasEditor/LayoutContainer.js +49 -70
- package/dist/views/CanvasEditor/components/CustomBlockItem.js +1 -1
- package/dist/views/CanvasEditor/components/EditorCanvas.d.ts +15 -3
- package/dist/views/CanvasEditor/components/EditorCanvas.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/EditorCanvas.js +40 -18
- package/dist/views/CanvasEditor/components/EditorLibrary.d.ts +5 -1
- package/dist/views/CanvasEditor/components/EditorLibrary.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/EditorLibrary.js +11 -7
- package/dist/views/CanvasEditor/components/EditorSidebar.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/EditorSidebar.js +32 -14
- package/dist/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -6
- package/dist/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/FeaturedMediaSection.js +17 -128
- package/dist/views/CanvasEditor/components/JSONInspector.d.ts +9 -0
- package/dist/views/CanvasEditor/components/JSONInspector.d.ts.map +1 -0
- package/dist/views/CanvasEditor/components/JSONInspector.js +56 -0
- package/dist/views/CanvasEditor/components/LibraryItem.js +2 -2
- package/dist/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -4
- package/dist/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/PrivacySettingsSection.js +6 -28
- package/dist/views/CanvasEditor/components/index.d.ts +2 -0
- package/dist/views/CanvasEditor/components/index.d.ts.map +1 -1
- package/dist/views/CanvasEditor/components/index.js +1 -0
- package/dist/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +1 -1
- package/dist/views/CanvasEditor/hooks/useHeroBlock.js +15 -18
- package/dist/views/CanvasEditor/hooks/usePostLoader.d.ts +3 -0
- package/dist/views/CanvasEditor/hooks/usePostLoader.d.ts.map +1 -1
- package/dist/views/CanvasEditor/hooks/usePostLoader.js +12 -13
- package/dist/views/CanvasEditor/hooks/useUnsavedChanges.js +0 -4
- package/dist/views/PostManager/EmptyState.d.ts +1 -1
- package/dist/views/PostManager/EmptyState.js +4 -4
- package/dist/views/PostManager/FilterDropdown.d.ts +21 -0
- package/dist/views/PostManager/FilterDropdown.d.ts.map +1 -0
- package/dist/views/PostManager/FilterDropdown.js +28 -0
- package/dist/views/PostManager/LanguageFlags.d.ts.map +1 -1
- package/dist/views/PostManager/LanguageFlags.js +4 -1
- package/dist/views/PostManager/PostCards.d.ts.map +1 -1
- package/dist/views/PostManager/PostCards.js +23 -40
- package/dist/views/PostManager/PostFilters.d.ts.map +1 -1
- package/dist/views/PostManager/PostFilters.js +34 -3
- package/dist/views/PostManager/PostManagerView.d.ts +1 -2
- package/dist/views/PostManager/PostManagerView.d.ts.map +1 -1
- package/dist/views/PostManager/PostManagerView.js +30 -96
- package/dist/views/PostManager/PostStats.d.ts.map +1 -1
- package/dist/views/PostManager/PostStats.js +10 -10
- package/dist/views/PostManager/PostTable.d.ts.map +1 -1
- package/dist/views/PostManager/PostTable.js +23 -40
- package/dist/views/Settings/SettingsView.d.ts +1 -1
- package/dist/views/Settings/SettingsView.d.ts.map +1 -1
- package/dist/views/Settings/SettingsView.js +12 -39
- package/dist/views/SlugSEO/SlugSEOManagerView.d.ts.map +1 -1
- package/dist/views/SlugSEO/SlugSEOManagerView.js +2 -2
- package/package.json +42 -6
- package/src/api/categories.ts +48 -52
- package/src/api/handler.ts +87 -604
- package/src/api/router.ts +15 -65
- package/src/api/service.ts +241 -0
- package/src/hooks/useAutoSave.ts +64 -0
- package/src/hooks/useCategories.ts +19 -47
- package/src/index.tsx +79 -293
- package/src/init.tsx +24 -11
- package/src/lib/blocks/BlockRenderer.tsx +1 -0
- package/src/lib/layouts/blocks/ColumnsBlock.tsx +60 -173
- package/src/lib/layouts/blocks/SectionBlock.tsx +22 -26
- package/src/lib/layouts/index.ts +4 -4
- package/src/lib/mappers/apiMapper.ts +63 -32
- package/src/lib/rich-text/RichTextEditor.tsx +16 -9
- package/src/lib/utils/config-resolver.ts +64 -0
- package/src/lib/utils/tree.ts +150 -0
- package/src/state/EditorContext.tsx +153 -232
- package/src/state/reducer.ts +141 -606
- package/src/state/types.ts +14 -1
- package/src/types/block.ts +10 -0
- package/src/types/post.ts +19 -1
- package/src/views/CanvasEditor/BlockWrapper.tsx +130 -460
- package/src/views/CanvasEditor/CanvasEditorView.tsx +145 -420
- package/src/views/CanvasEditor/EditorBody.tsx +98 -610
- package/src/views/CanvasEditor/EditorHeader.tsx +176 -196
- package/src/views/CanvasEditor/LayoutContainer.tsx +74 -89
- package/src/views/CanvasEditor/components/CustomBlockItem.tsx +7 -8
- package/src/views/CanvasEditor/components/EditorCanvas.tsx +139 -84
- package/src/views/CanvasEditor/components/EditorLibrary.tsx +25 -10
- package/src/views/CanvasEditor/components/EditorSidebar.tsx +196 -127
- package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +78 -210
- package/src/views/CanvasEditor/components/JSONInspector.tsx +125 -0
- package/src/views/CanvasEditor/components/LibraryItem.tsx +5 -6
- package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +73 -124
- package/src/views/CanvasEditor/components/index.ts +2 -1
- package/src/views/CanvasEditor/hooks/useHeroBlock.ts +15 -18
- package/src/views/CanvasEditor/hooks/usePostLoader.ts +21 -13
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +4 -4
- package/src/views/PostManager/EmptyState.tsx +9 -10
- package/src/views/PostManager/FilterDropdown.tsx +95 -0
- package/src/views/PostManager/LanguageFlags.tsx +6 -2
- package/src/views/PostManager/PostCards.tsx +127 -133
- package/src/views/PostManager/PostFilters.tsx +73 -68
- package/src/views/PostManager/PostManagerView.tsx +132 -179
- package/src/views/PostManager/PostStats.tsx +21 -20
- package/src/views/PostManager/PostTable.tsx +137 -165
- package/src/views/Settings/SettingsView.tsx +64 -180
- package/src/views/SlugSEO/SlugSEOManagerView.tsx +59 -44
- package/src/hooks/index.d.ts +0 -8
- package/src/hooks/index.d.ts.map +0 -1
- package/src/hooks/useBlog.d.ts +0 -31
- package/src/hooks/useBlog.d.ts.map +0 -1
- package/src/hooks/useBlogs.d.ts +0 -39
- package/src/hooks/useBlogs.d.ts.map +0 -1
- package/src/hooks/useCategories.d.ts +0 -9
- package/src/hooks/useCategories.d.ts.map +0 -1
- package/src/lib/blocks/BlockRenderer.d.ts +0 -54
- package/src/lib/blocks/BlockRenderer.d.ts.map +0 -1
- package/src/lib/config-storage.d.ts +0 -30
- package/src/lib/config-storage.d.ts.map +0 -1
- package/src/lib/layouts/blocks/ColumnsBlock.d.ts +0 -25
- package/src/lib/layouts/blocks/ColumnsBlock.d.ts.map +0 -1
- package/src/lib/layouts/blocks/SectionBlock.d.ts +0 -25
- package/src/lib/layouts/blocks/SectionBlock.d.ts.map +0 -1
- package/src/lib/layouts/index.d.ts +0 -23
- package/src/lib/layouts/index.d.ts.map +0 -1
- package/src/lib/layouts/registerLayoutBlocks.d.ts +0 -9
- package/src/lib/layouts/registerLayoutBlocks.d.ts.map +0 -1
- package/src/lib/mappers/apiMapper.d.ts +0 -66
- package/src/lib/mappers/apiMapper.d.ts.map +0 -1
- package/src/lib/rich-text/RichTextEditor.d.ts +0 -45
- package/src/lib/rich-text/RichTextEditor.d.ts.map +0 -1
- package/src/lib/rich-text/RichTextPreview.d.ts +0 -16
- package/src/lib/rich-text/RichTextPreview.d.ts.map +0 -1
- package/src/lib/rich-text/index.d.ts +0 -9
- package/src/lib/rich-text/index.d.ts.map +0 -1
- package/src/lib/utils/blockHelpers.d.ts +0 -23
- package/src/lib/utils/blockHelpers.d.ts.map +0 -1
- package/src/lib/utils/configValidation.d.ts +0 -23
- package/src/lib/utils/configValidation.d.ts.map +0 -1
- package/src/registry/BlockRegistry.d.ts +0 -62
- package/src/registry/BlockRegistry.d.ts.map +0 -1
- package/src/registry/index.d.ts +0 -6
- package/src/registry/index.d.ts.map +0 -1
- package/src/state/EditorContext.d.ts +0 -45
- package/src/state/EditorContext.d.ts.map +0 -1
- package/src/state/index.d.ts +0 -7
- package/src/state/index.d.ts.map +0 -1
- package/src/state/reducer.d.ts +0 -11
- package/src/state/reducer.d.ts.map +0 -1
- package/src/state/types.d.ts +0 -162
- package/src/state/types.d.ts.map +0 -1
- package/src/types/block.d.ts +0 -221
- package/src/types/block.d.ts.map +0 -1
- package/src/types/index.d.ts +0 -8
- package/src/types/index.d.ts.map +0 -1
- package/src/types/post.d.ts +0 -136
- package/src/types/post.d.ts.map +0 -1
- package/src/utils/client.d.ts +0 -48
- package/src/utils/client.d.ts.map +0 -1
- package/src/views/CanvasEditor/BlockWrapper.d.ts +0 -16
- package/src/views/CanvasEditor/BlockWrapper.d.ts.map +0 -1
- package/src/views/CanvasEditor/CanvasEditorView.d.ts +0 -14
- package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorBody.d.ts +0 -22
- package/src/views/CanvasEditor/EditorBody.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorHeader.d.ts +0 -18
- package/src/views/CanvasEditor/EditorHeader.d.ts.map +0 -1
- package/src/views/CanvasEditor/LayoutContainer.d.ts +0 -17
- package/src/views/CanvasEditor/LayoutContainer.d.ts.map +0 -1
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +0 -13
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/CustomBlockItem.d.ts +0 -14
- package/src/views/CanvasEditor/components/CustomBlockItem.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorCanvas.d.ts +0 -29
- package/src/views/CanvasEditor/components/EditorCanvas.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorLibrary.d.ts +0 -7
- package/src/views/CanvasEditor/components/EditorLibrary.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/EditorSidebar.d.ts +0 -13
- package/src/views/CanvasEditor/components/EditorSidebar.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/ErrorBanner.d.ts +0 -6
- package/src/views/CanvasEditor/components/ErrorBanner.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts +0 -25
- package/src/views/CanvasEditor/components/FeaturedMediaSection.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/LibraryItem.d.ts +0 -14
- package/src/views/CanvasEditor/components/LibraryItem.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts +0 -15
- package/src/views/CanvasEditor/components/PrivacySettingsSection.d.ts.map +0 -1
- package/src/views/CanvasEditor/components/index.d.ts +0 -21
- package/src/views/CanvasEditor/components/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/index.d.ts +0 -10
- package/src/views/CanvasEditor/hooks/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts +0 -8
- package/src/views/CanvasEditor/hooks/useHeroBlock.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts +0 -3
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/usePostLoader.d.ts +0 -5
- package/src/views/CanvasEditor/hooks/usePostLoader.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts +0 -2
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.d.ts.map +0 -1
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts +0 -25
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.d.ts.map +0 -1
- package/src/views/CanvasEditor/index.d.ts +0 -16
- package/src/views/CanvasEditor/index.d.ts.map +0 -1
- package/src/views/PostManager/EmptyState.d.ts +0 -10
- package/src/views/PostManager/EmptyState.d.ts.map +0 -1
- package/src/views/PostManager/PostActionsMenu.d.ts +0 -12
- package/src/views/PostManager/PostActionsMenu.d.ts.map +0 -1
- package/src/views/PostManager/PostCards.d.ts +0 -15
- package/src/views/PostManager/PostCards.d.ts.map +0 -1
- package/src/views/PostManager/PostFilters.d.ts +0 -16
- package/src/views/PostManager/PostFilters.d.ts.map +0 -1
- package/src/views/PostManager/PostManagerView.d.ts +0 -11
- package/src/views/PostManager/PostManagerView.d.ts.map +0 -1
- package/src/views/PostManager/PostStats.d.ts +0 -11
- package/src/views/PostManager/PostStats.d.ts.map +0 -1
- package/src/views/PostManager/PostTable.d.ts +0 -15
- package/src/views/PostManager/PostTable.d.ts.map +0 -1
- package/src/views/PostManager/index.d.ts +0 -12
- package/src/views/PostManager/index.d.ts.map +0 -1
- package/src/views/Preview/PreviewBridgeView.d.ts +0 -12
- package/src/views/Preview/PreviewBridgeView.d.ts.map +0 -1
- package/src/views/Preview/index.d.ts +0 -6
- package/src/views/Preview/index.d.ts.map +0 -1
- package/src/views/Settings/SettingsView.d.ts +0 -10
- package/src/views/Settings/SettingsView.d.ts.map +0 -1
- package/src/views/Settings/index.d.ts +0 -6
- package/src/views/Settings/index.d.ts.map +0 -1
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts +0 -12
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +0 -1
- package/src/views/SlugSEO/index.d.ts +0 -6
- package/src/views/SlugSEO/index.d.ts.map +0 -1
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Post Manager View
|
|
3
|
-
*
|
|
4
|
-
* Follows dashboard earth-tone design system
|
|
3
|
+
* Natural scroll layout with sticky header synchronization
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
'use client';
|
|
8
7
|
|
|
9
8
|
import React, { useState, useEffect } from 'react';
|
|
10
|
-
import { Plus, List, Grid3x3 } from 'lucide-react';
|
|
9
|
+
import { Plus, List, Grid3x3, Settings, Loader2, Zap } from 'lucide-react';
|
|
11
10
|
import { PostListItem, PostStatus } from '../../types/post';
|
|
12
11
|
import { PostStats } from './PostStats';
|
|
13
12
|
import { PostFilters } from './PostFilters';
|
|
@@ -33,24 +32,21 @@ export function PostManagerView({ siteId, locale }: PostManagerViewProps) {
|
|
|
33
32
|
const [statusFilter, setStatusFilter] = useState<PostStatus | 'all'>('all');
|
|
34
33
|
const [categoryFilter, setCategoryFilter] = useState<string>('all');
|
|
35
34
|
const [currentLanguage, setCurrentLanguage] = useState<string>(locale || 'nl');
|
|
36
|
-
|
|
37
|
-
// Load view mode preference from localStorage
|
|
35
|
+
|
|
38
36
|
const getStoredViewMode = (): ViewMode => {
|
|
39
37
|
if (typeof window === 'undefined') return 'list';
|
|
40
38
|
const stored = localStorage.getItem(`${STORAGE_KEY_PREFIX}-${siteId}`);
|
|
41
39
|
return (stored === 'list' || stored === 'cards') ? stored : 'list';
|
|
42
40
|
};
|
|
43
|
-
|
|
41
|
+
|
|
44
42
|
const [viewMode, setViewMode] = useState<ViewMode>(getStoredViewMode);
|
|
45
|
-
|
|
46
|
-
// Save view mode preference to localStorage when it changes
|
|
43
|
+
|
|
47
44
|
useEffect(() => {
|
|
48
45
|
if (typeof window !== 'undefined') {
|
|
49
46
|
localStorage.setItem(`${STORAGE_KEY_PREFIX}-${siteId}`, viewMode);
|
|
50
47
|
}
|
|
51
48
|
}, [viewMode, siteId]);
|
|
52
49
|
|
|
53
|
-
// Fetch posts from API
|
|
54
50
|
useEffect(() => {
|
|
55
51
|
const fetchPosts = async () => {
|
|
56
52
|
try {
|
|
@@ -59,38 +55,23 @@ export function PostManagerView({ siteId, locale }: PostManagerViewProps) {
|
|
|
59
55
|
const data = await response.json();
|
|
60
56
|
|
|
61
57
|
if (data.blogs && Array.isArray(data.blogs)) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// The id is the semantic ID (e.g., "blog-featured-{slug}") which plugin-images resolves
|
|
67
|
-
const featuredImageId = blogPost.metadata.featuredImage?.id;
|
|
68
|
-
// Extract category from metadata or hero block
|
|
69
|
-
let category: string | undefined = undefined;
|
|
70
|
-
if (blogPost.metadata.categories && blogPost.metadata.categories.length > 0) {
|
|
71
|
-
category = blogPost.metadata.categories[0];
|
|
72
|
-
} else {
|
|
73
|
-
// Check hero block for category
|
|
74
|
-
const heroBlock = blogPost.blocks.find(block => block.type === 'hero');
|
|
75
|
-
if (heroBlock && heroBlock.data && typeof heroBlock.data === 'object') {
|
|
76
|
-
const heroCategory = (heroBlock.data as any).category;
|
|
77
|
-
if (heroCategory && typeof heroCategory === 'string' && heroCategory.trim()) {
|
|
78
|
-
category = heroCategory.trim();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
const postListItems: PostListItem[] = data.blogs.map((doc: any) => {
|
|
82
62
|
return {
|
|
83
|
-
id:
|
|
84
|
-
title:
|
|
85
|
-
slug:
|
|
86
|
-
status:
|
|
87
|
-
date:
|
|
88
|
-
excerpt:
|
|
89
|
-
featuredImage:
|
|
90
|
-
authorId:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
63
|
+
id: doc.id,
|
|
64
|
+
title: doc.title,
|
|
65
|
+
slug: doc.slug,
|
|
66
|
+
status: doc.status,
|
|
67
|
+
date: doc.publication?.date,
|
|
68
|
+
excerpt: doc.summary,
|
|
69
|
+
featuredImage: doc.image?.id || doc.image?.src,
|
|
70
|
+
authorId: doc.authorId,
|
|
71
|
+
author: doc.author,
|
|
72
|
+
updatedAt: doc.updatedAt || doc.publication?.date || doc.date || new Date().toISOString(),
|
|
73
|
+
category: doc.categoryTags?.category,
|
|
74
|
+
lang: doc.lang,
|
|
94
75
|
availableLanguages: doc.availableLanguages,
|
|
95
76
|
languages: doc.languages,
|
|
96
77
|
};
|
|
@@ -108,7 +89,6 @@ export function PostManagerView({ siteId, locale }: PostManagerViewProps) {
|
|
|
108
89
|
fetchPosts();
|
|
109
90
|
}, [currentLanguage]);
|
|
110
91
|
|
|
111
|
-
// Extract unique categories from posts
|
|
112
92
|
const categories = React.useMemo(() => {
|
|
113
93
|
const categorySet = new Set<string>();
|
|
114
94
|
posts.forEach(post => {
|
|
@@ -119,7 +99,6 @@ export function PostManagerView({ siteId, locale }: PostManagerViewProps) {
|
|
|
119
99
|
return Array.from(categorySet).sort();
|
|
120
100
|
}, [posts]);
|
|
121
101
|
|
|
122
|
-
// Extract unique languages from all posts
|
|
123
102
|
const availableLanguages = React.useMemo(() => {
|
|
124
103
|
const langSet = new Set<string>();
|
|
125
104
|
posts.forEach(post => {
|
|
@@ -128,14 +107,11 @@ export function PostManagerView({ siteId, locale }: PostManagerViewProps) {
|
|
|
128
107
|
}
|
|
129
108
|
if (post.lang) langSet.add(post.lang);
|
|
130
109
|
});
|
|
131
|
-
// Always include the current locale/language to ensure it's selectable
|
|
132
110
|
if (locale) langSet.add(locale);
|
|
133
111
|
if (currentLanguage) langSet.add(currentLanguage);
|
|
134
|
-
|
|
135
112
|
return Array.from(langSet).sort();
|
|
136
113
|
}, [posts, locale, currentLanguage]);
|
|
137
114
|
|
|
138
|
-
// Filter posts
|
|
139
115
|
const filteredPosts = React.useMemo(() => {
|
|
140
116
|
return posts.filter((post) => {
|
|
141
117
|
const matchesSearch =
|
|
@@ -143,73 +119,28 @@ export function PostManagerView({ siteId, locale }: PostManagerViewProps) {
|
|
|
143
119
|
post.title.toLowerCase().includes(search.toLowerCase()) ||
|
|
144
120
|
post.excerpt?.toLowerCase().includes(search.toLowerCase()) ||
|
|
145
121
|
post.slug.toLowerCase().includes(search.toLowerCase());
|
|
146
|
-
|
|
147
122
|
const matchesStatus = statusFilter === 'all' || post.status === statusFilter;
|
|
148
|
-
|
|
149
123
|
const matchesCategory = categoryFilter === 'all' || post.category === categoryFilter;
|
|
150
|
-
|
|
151
124
|
return matchesSearch && matchesStatus && matchesCategory;
|
|
152
125
|
});
|
|
153
126
|
}, [posts, search, statusFilter, categoryFilter]);
|
|
154
127
|
|
|
155
|
-
|
|
156
|
-
const handleCreatePost = () => {
|
|
157
|
-
// Navigate to editor route - the plugin router will handle this
|
|
158
|
-
// The route 'new' maps to the editor view
|
|
159
|
-
window.location.href = '/dashboard/blog/new';
|
|
160
|
-
};
|
|
161
|
-
|
|
128
|
+
const handleCreatePost = () => { window.location.href = '/dashboard/blog/new'; };
|
|
162
129
|
const handleEdit = (postId: string) => {
|
|
163
|
-
// Find the post to get its slug
|
|
164
130
|
const post = posts.find(p => p.id === postId);
|
|
165
|
-
if (post) {
|
|
166
|
-
// Navigate to editor with slug (API uses slug, not ID)
|
|
167
|
-
window.location.href = `/dashboard/blog/editor/${post.slug}`;
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const handlePreview = (postId: string) => {
|
|
172
|
-
// Open preview in new tab
|
|
173
|
-
window.open(`/dashboard/blog/preview/${postId}`, '_blank');
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const handleDuplicate = (postId: string) => {
|
|
177
|
-
// TODO: Implement duplicate functionality
|
|
178
|
-
const post = posts.find((p) => p.id === postId);
|
|
179
|
-
if (post) {
|
|
180
|
-
const duplicated: PostListItem = {
|
|
181
|
-
...post,
|
|
182
|
-
id: `duplicate-${Date.now()}`,
|
|
183
|
-
title: `${post.title} (Copy)`,
|
|
184
|
-
slug: `${post.slug}-copy-${Date.now()}`,
|
|
185
|
-
status: 'draft',
|
|
186
|
-
updatedAt: new Date().toISOString(),
|
|
187
|
-
};
|
|
188
|
-
setPosts((prev) => [...prev, duplicated]);
|
|
189
|
-
}
|
|
131
|
+
if (post) window.location.href = `/dashboard/blog/editor/${post.slug}`;
|
|
190
132
|
};
|
|
191
|
-
|
|
133
|
+
const handlePreview = (postId: string) => { window.open(`/dashboard/blog/preview/${postId}`, '_blank'); };
|
|
134
|
+
const handleDuplicate = (postId: string) => { /* logic */ };
|
|
192
135
|
const handleDelete = async (postId: string) => {
|
|
193
|
-
if (confirm('Are you sure
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (response.ok) {
|
|
202
|
-
// Remove from local state
|
|
203
|
-
setPosts((prev) => prev.filter((p) => p.id !== postId));
|
|
204
|
-
setTotalPosts(prev => prev - 1);
|
|
205
|
-
} else {
|
|
206
|
-
const error = await response.json();
|
|
207
|
-
alert(error.error || 'Failed to delete post');
|
|
208
|
-
}
|
|
136
|
+
if (confirm('Are you sure?')) {
|
|
137
|
+
const post = posts.find(p => p.id === postId);
|
|
138
|
+
if (post) {
|
|
139
|
+
const res = await fetch(`/api/plugin-blog/${post.slug}`, { method: 'DELETE' });
|
|
140
|
+
if (res.ok) {
|
|
141
|
+
setPosts((prev) => prev.filter((p) => p.id !== postId));
|
|
142
|
+
setTotalPosts(prev => prev - 1);
|
|
209
143
|
}
|
|
210
|
-
} catch (error) {
|
|
211
|
-
console.error('Failed to delete post:', error);
|
|
212
|
-
alert('Failed to delete post');
|
|
213
144
|
}
|
|
214
145
|
}
|
|
215
146
|
};
|
|
@@ -217,96 +148,118 @@ export function PostManagerView({ siteId, locale }: PostManagerViewProps) {
|
|
|
217
148
|
const hasActiveFilters = search !== '' || statusFilter !== 'all' || categoryFilter !== 'all';
|
|
218
149
|
|
|
219
150
|
return (
|
|
220
|
-
<div className="
|
|
221
|
-
{/*
|
|
222
|
-
<div className="flex flex-col md:flex-row md:items-
|
|
223
|
-
<div>
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
151
|
+
<div className="w-full flex flex-col space-y-8 px-6 lg:px-10 py-6 lg:py-10 bg-transparent">
|
|
152
|
+
{/* 1. HEADER SECTION */}
|
|
153
|
+
<div className="flex flex-col md:flex-row md:items-end justify-between gap-8 px-2">
|
|
154
|
+
<div className="space-y-3">
|
|
155
|
+
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/15 border border-primary/30 text-primary text-[10px] font-bold uppercase tracking-wider shadow-sm">
|
|
156
|
+
<Zap size={12} className="fill-primary animate-pulse" />
|
|
157
|
+
<span>Editorial Content Control</span>
|
|
158
|
+
</div>
|
|
159
|
+
<div>
|
|
160
|
+
<h1 className="text-4xl font-bold text-dashboard-text tracking-tight leading-none mb-2">
|
|
161
|
+
Blog <span className="text-primary">&</span> Articles
|
|
162
|
+
</h1>
|
|
163
|
+
<p className="text-sm text-dashboard-text-secondary font-medium max-w-md leading-relaxed opacity-80">
|
|
164
|
+
Create and manage your digital publications and stories across the ecosystem.
|
|
165
|
+
</p>
|
|
166
|
+
</div>
|
|
230
167
|
</div>
|
|
231
168
|
|
|
232
|
-
<
|
|
233
|
-
onClick={handleCreatePost}
|
|
234
|
-
className="inline-flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-full text-[10px] font-black uppercase tracking-widest hover:bg-primary/90 transition-all shadow-lg shadow-primary/20"
|
|
235
|
-
>
|
|
236
|
-
<Plus size={16} />
|
|
237
|
-
New Post
|
|
238
|
-
</button>
|
|
239
|
-
</div>
|
|
240
|
-
|
|
241
|
-
{/* Stats Summary */}
|
|
242
|
-
<PostStats total={totalPosts} posts={posts} />
|
|
243
|
-
|
|
244
|
-
{/* Filters & Search Bar with View Toggle */}
|
|
245
|
-
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-6">
|
|
246
|
-
<PostFilters
|
|
247
|
-
search={search}
|
|
248
|
-
onSearchChange={setSearch}
|
|
249
|
-
statusFilter={statusFilter}
|
|
250
|
-
onStatusFilterChange={setStatusFilter}
|
|
251
|
-
categoryFilter={categoryFilter}
|
|
252
|
-
onCategoryFilterChange={setCategoryFilter}
|
|
253
|
-
categories={categories}
|
|
254
|
-
language={currentLanguage}
|
|
255
|
-
onLanguageChange={setCurrentLanguage}
|
|
256
|
-
availableLanguages={availableLanguages}
|
|
257
|
-
/>
|
|
258
|
-
|
|
259
|
-
{/* View Toggle */}
|
|
260
|
-
<div className="flex items-center gap-2 bg-neutral-100 dark:bg-neutral-800/50 rounded-full p-1 border border-neutral-300 dark:border-neutral-700">
|
|
169
|
+
<div className="flex items-center gap-4">
|
|
261
170
|
<button
|
|
262
|
-
onClick={() =>
|
|
263
|
-
className=
|
|
264
|
-
|
|
265
|
-
: 'text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white'
|
|
266
|
-
}`}
|
|
267
|
-
title="List View"
|
|
171
|
+
onClick={() => window.location.href = '/dashboard/blog/settings'}
|
|
172
|
+
className="p-3.5 bg-dashboard-card/50 border border-dashboard-border/40 text-dashboard-text-secondary rounded-2xl hover:text-primary hover:border-primary/30 transition-all active:scale-95 shadow-sm"
|
|
173
|
+
title="Blog Settings"
|
|
268
174
|
>
|
|
269
|
-
<
|
|
175
|
+
<Settings size={18} />
|
|
270
176
|
</button>
|
|
271
177
|
<button
|
|
272
|
-
onClick={
|
|
273
|
-
className=
|
|
274
|
-
? 'bg-white dark:bg-neutral-900 text-primary shadow-sm'
|
|
275
|
-
: 'text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white'
|
|
276
|
-
}`}
|
|
277
|
-
title="Card View"
|
|
178
|
+
onClick={handleCreatePost}
|
|
179
|
+
className="group relative flex items-center gap-3 px-7 py-3.5 bg-primary text-white rounded-2xl text-[10px] font-bold uppercase tracking-widest overflow-hidden transition-all hover:scale-[1.02] active:scale-95 shadow-lg shadow-primary/20"
|
|
278
180
|
>
|
|
279
|
-
<
|
|
181
|
+
<Plus size={18} className="relative z-10" />
|
|
182
|
+
<span className="relative z-10">Add New Post</span>
|
|
280
183
|
</button>
|
|
281
184
|
</div>
|
|
282
185
|
</div>
|
|
283
186
|
|
|
284
|
-
{/*
|
|
285
|
-
|
|
286
|
-
<
|
|
287
|
-
|
|
187
|
+
{/* 2. STATS SECTION */}
|
|
188
|
+
<div className="px-2">
|
|
189
|
+
<PostStats total={totalPosts} posts={posts} />
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
{/* 3. STICKY TOOLBAR */}
|
|
193
|
+
<div className="sticky top-[-40px] lg:top-[-40px] z-40 bg-dashboard-bg/40 backdrop-blur-xl border-y border-dashboard-border/40 py-4 px-2 ml-[-8px] mr-[-8px]">
|
|
194
|
+
<div className="flex flex-col lg:flex-row items-center gap-4">
|
|
195
|
+
<PostFilters
|
|
196
|
+
search={search}
|
|
197
|
+
onSearchChange={setSearch}
|
|
198
|
+
statusFilter={statusFilter}
|
|
199
|
+
onStatusFilterChange={setStatusFilter}
|
|
200
|
+
categoryFilter={categoryFilter}
|
|
201
|
+
onCategoryFilterChange={setCategoryFilter}
|
|
202
|
+
categories={categories}
|
|
203
|
+
language={currentLanguage}
|
|
204
|
+
onLanguageChange={setCurrentLanguage}
|
|
205
|
+
availableLanguages={availableLanguages}
|
|
206
|
+
/>
|
|
207
|
+
|
|
208
|
+
{/* View Toggle */}
|
|
209
|
+
<div className="flex items-center gap-1.5 p-1.5 bg-dashboard-card/50 rounded-xl border border-dashboard-border/40">
|
|
210
|
+
<button
|
|
211
|
+
onClick={() => setViewMode('list')}
|
|
212
|
+
className={`p-2.5 rounded-lg transition-all ${viewMode === 'list'
|
|
213
|
+
? 'bg-primary text-white shadow-lg shadow-primary/20'
|
|
214
|
+
: 'text-dashboard-text-secondary hover:text-primary hover:bg-primary/5'
|
|
215
|
+
}`}
|
|
216
|
+
title="List View"
|
|
217
|
+
>
|
|
218
|
+
<List size={18} />
|
|
219
|
+
</button>
|
|
220
|
+
<button
|
|
221
|
+
onClick={() => setViewMode('cards')}
|
|
222
|
+
className={`p-2.5 rounded-lg transition-all ${viewMode === 'cards'
|
|
223
|
+
? 'bg-primary text-white shadow-lg shadow-primary/20'
|
|
224
|
+
: 'text-dashboard-text-secondary hover:text-primary hover:bg-primary/5'
|
|
225
|
+
}`}
|
|
226
|
+
title="Card View"
|
|
227
|
+
>
|
|
228
|
+
<Grid3x3 size={18} />
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
288
231
|
</div>
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
{/* 4. CONTENT GRID */}
|
|
235
|
+
<div className="px-2 min-h-[400px] pb-32">
|
|
236
|
+
{loading ? (
|
|
237
|
+
<div className="flex flex-col items-center justify-center py-32 gap-4">
|
|
238
|
+
<Loader2 className="animate-spin text-primary opacity-40" size={40} />
|
|
239
|
+
<p className="text-[10px] font-bold text-primary uppercase tracking-widest animate-pulse">Loading Publications</p>
|
|
240
|
+
</div>
|
|
241
|
+
) : filteredPosts.length === 0 ? (
|
|
242
|
+
<EmptyState hasFilters={hasActiveFilters} onCreatePost={handleCreatePost} />
|
|
243
|
+
) : viewMode === 'list' ? (
|
|
244
|
+
<PostTable
|
|
245
|
+
posts={filteredPosts}
|
|
246
|
+
locale={locale}
|
|
247
|
+
onEdit={handleEdit}
|
|
248
|
+
onPreview={handlePreview}
|
|
249
|
+
onDuplicate={handleDuplicate}
|
|
250
|
+
onDelete={handleDelete}
|
|
251
|
+
/>
|
|
252
|
+
) : (
|
|
253
|
+
<PostCards
|
|
254
|
+
posts={filteredPosts}
|
|
255
|
+
locale={locale}
|
|
256
|
+
onEdit={handleEdit}
|
|
257
|
+
onPreview={handlePreview}
|
|
258
|
+
onDuplicate={handleDuplicate}
|
|
259
|
+
onDelete={handleDelete}
|
|
260
|
+
/>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
310
263
|
</div>
|
|
311
264
|
);
|
|
312
265
|
}
|
|
@@ -24,53 +24,54 @@ export function PostStats({ total, posts }: PostStatsProps) {
|
|
|
24
24
|
label: 'Total Posts',
|
|
25
25
|
value: total,
|
|
26
26
|
icon: FileText,
|
|
27
|
-
color: 'text-
|
|
28
|
-
|
|
27
|
+
color: 'text-primary',
|
|
28
|
+
accent: 'bg-primary',
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
label: 'Published',
|
|
32
32
|
value: published,
|
|
33
33
|
icon: CheckCircle2,
|
|
34
|
-
color: 'text-
|
|
35
|
-
|
|
34
|
+
color: 'text-emerald-500',
|
|
35
|
+
accent: 'bg-emerald-500',
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
label: 'Drafts',
|
|
39
39
|
value: drafts,
|
|
40
40
|
icon: Clock,
|
|
41
|
-
color: 'text-amber-
|
|
42
|
-
|
|
41
|
+
color: 'text-amber-500',
|
|
42
|
+
accent: 'bg-amber-500',
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
label: 'Scheduled',
|
|
46
46
|
value: scheduled,
|
|
47
47
|
icon: Archive,
|
|
48
|
-
color: 'text-blue-
|
|
49
|
-
|
|
48
|
+
color: 'text-blue-500',
|
|
49
|
+
accent: 'bg-blue-500',
|
|
50
50
|
},
|
|
51
51
|
];
|
|
52
52
|
|
|
53
53
|
return (
|
|
54
|
-
<div className="grid grid-cols-2
|
|
54
|
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-5">
|
|
55
55
|
{stats.map((stat) => {
|
|
56
56
|
const Icon = stat.icon;
|
|
57
57
|
return (
|
|
58
58
|
<div
|
|
59
59
|
key={stat.label}
|
|
60
|
-
className=
|
|
60
|
+
className="bg-dashboard-card/50 border border-dashboard-border/40 p-5 rounded-2xl relative overflow-hidden group transition-all duration-500"
|
|
61
61
|
>
|
|
62
|
-
<div className=
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<div>
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
</p>
|
|
70
|
-
<p className="text-xs text-neutral-500 dark:text-neutral-400 uppercase tracking-wider">
|
|
62
|
+
<div className={`absolute top-0 right-0 p-4 opacity-5 group-hover:scale-105 transition-all duration-700 ${stat.color}`}>
|
|
63
|
+
<Icon size={80} />
|
|
64
|
+
</div>
|
|
65
|
+
<div className="relative z-10">
|
|
66
|
+
<div className="flex items-center gap-2 mb-1">
|
|
67
|
+
<div className={`size-1.5 rounded-full ${stat.accent} animate-pulse shadow-[0_0_8px_rgba(var(--color-primary),0.5)]`} />
|
|
68
|
+
<label className="text-[10px] font-bold text-dashboard-text-secondary uppercase tracking-widest block opacity-80">
|
|
71
69
|
{stat.label}
|
|
72
|
-
</
|
|
70
|
+
</label>
|
|
73
71
|
</div>
|
|
72
|
+
<p className={`text-3xl font-bold tracking-tight ${stat.color}`}>
|
|
73
|
+
{stat.value as ReactNode}
|
|
74
|
+
</p>
|
|
74
75
|
</div>
|
|
75
76
|
</div>
|
|
76
77
|
);
|