@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
|
@@ -27,12 +27,13 @@ export function useHeroBlock(state, registeredBlocks) {
|
|
|
27
27
|
return heroBlockFromContent;
|
|
28
28
|
}
|
|
29
29
|
// If no hero block in contentBlocks, initialize from defaults
|
|
30
|
-
//
|
|
30
|
+
// We map metadata.excerpt to both 'summary' and 'description' to support different block implementations
|
|
31
31
|
const initialData = {
|
|
32
32
|
...heroData,
|
|
33
33
|
title: state.title || heroData.title || '',
|
|
34
34
|
summary: state.metadata.excerpt || heroData.summary || '',
|
|
35
|
-
|
|
35
|
+
description: state.metadata.excerpt || heroData.description || '',
|
|
36
|
+
category: state.metadata.categories?.[0] || heroData.category || '',
|
|
36
37
|
};
|
|
37
38
|
return {
|
|
38
39
|
id: generateBlockId(),
|
|
@@ -47,36 +48,32 @@ export function useHeroBlock(state, registeredBlocks) {
|
|
|
47
48
|
else {
|
|
48
49
|
setHeroBlock(null);
|
|
49
50
|
}
|
|
50
|
-
}, [heroBlockDefinition, state.blocks, state.title, state.metadata.excerpt]);
|
|
51
|
+
}, [heroBlockDefinition, state.blocks, state.title, state.metadata.excerpt, state.metadata.categories]);
|
|
51
52
|
// Sync hero block with editor state when post is loaded (for existing posts)
|
|
52
|
-
// BUT: Never sync image from featured image to hero - they are independent
|
|
53
|
-
// Only sync title, summary, and category
|
|
54
53
|
useEffect(() => {
|
|
55
54
|
if (heroBlock && heroBlockDefinition && state.postId) {
|
|
56
|
-
// Only update if the hero block data doesn't match the editor state
|
|
57
|
-
// This prevents overwriting user edits with stale data
|
|
58
55
|
const currentTitle = heroBlock.data?.title || '';
|
|
59
56
|
const currentSummary = heroBlock.data?.summary || '';
|
|
57
|
+
const currentDescription = heroBlock.data?.description || '';
|
|
60
58
|
const currentCategory = heroBlock.data?.category || '';
|
|
61
59
|
const stateTitle = state.title || '';
|
|
62
|
-
const
|
|
60
|
+
const stateExcerpt = state.metadata.excerpt || '';
|
|
63
61
|
const stateCategory = state.metadata.categories?.[0] || '';
|
|
64
|
-
// Check if hero block is out of sync with editor state
|
|
65
|
-
// NOTE: We do NOT sync image anymore - hero and featured image are independent
|
|
66
62
|
const titleMismatch = currentTitle !== stateTitle;
|
|
67
|
-
|
|
63
|
+
// Check both summary and description for mismatches
|
|
64
|
+
const summaryMismatch = currentSummary !== stateExcerpt;
|
|
65
|
+
const descriptionMismatch = currentDescription !== stateExcerpt;
|
|
68
66
|
const categoryMismatch = currentCategory !== stateCategory;
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
if ((titleMismatch || summaryMismatch || categoryMismatch) && (stateTitle || stateSummary || stateCategory)) {
|
|
67
|
+
// Sync if state has values and there's a mismatch
|
|
68
|
+
if ((titleMismatch || summaryMismatch || descriptionMismatch || categoryMismatch) && (stateTitle || stateExcerpt || stateCategory)) {
|
|
72
69
|
setHeroBlock({
|
|
73
70
|
...heroBlock,
|
|
74
71
|
data: {
|
|
75
72
|
...heroBlock.data,
|
|
76
|
-
title: stateTitle ||
|
|
77
|
-
summary:
|
|
78
|
-
|
|
79
|
-
category: stateCategory ||
|
|
73
|
+
title: stateTitle || currentTitle,
|
|
74
|
+
summary: stateExcerpt || currentSummary,
|
|
75
|
+
description: stateExcerpt || currentDescription,
|
|
76
|
+
category: stateCategory || currentCategory,
|
|
80
77
|
},
|
|
81
78
|
});
|
|
82
79
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { BlogPost } from '../../../types/post';
|
|
2
|
+
/**
|
|
3
|
+
* Technical hook for loading post data into the Orchestrator
|
|
4
|
+
*/
|
|
2
5
|
export declare function usePostLoader(postId: string | undefined, currentPostId: string | null, loadPost: (post: BlogPost) => void, resetHeroBlock: () => void, language?: string): {
|
|
3
6
|
isLoadingPost: boolean;
|
|
4
7
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePostLoader.d.ts","sourceRoot":"","sources":["../../../../src/views/CanvasEditor/hooks/usePostLoader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usePostLoader.d.ts","sourceRoot":"","sources":["../../../../src/views/CanvasEditor/hooks/usePostLoader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;GAEG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,EAClC,cAAc,EAAE,MAAM,IAAI,EAC1B,QAAQ,CAAC,EAAE,MAAM;;EA2CpB"}
|
|
@@ -1,32 +1,34 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { useEffect, useState, useRef } from 'react';
|
|
2
3
|
import { apiToBlogPost } from '../../../lib/mappers/apiMapper';
|
|
4
|
+
/**
|
|
5
|
+
* Technical hook for loading post data into the Orchestrator
|
|
6
|
+
*/
|
|
3
7
|
export function usePostLoader(postId, currentPostId, loadPost, resetHeroBlock, language) {
|
|
4
8
|
const [isLoadingPost, setIsLoadingPost] = useState(false);
|
|
5
|
-
// Use a ref to track language so changes don't re-trigger initial load
|
|
6
9
|
const languageRef = useRef(language);
|
|
7
10
|
languageRef.current = language;
|
|
8
11
|
useEffect(() => {
|
|
12
|
+
// Only trigger if we have a target ID but no active post in state
|
|
13
|
+
// This prevents infinite loops while allowing initial synchronization
|
|
9
14
|
if (postId && !currentPostId) {
|
|
10
15
|
const loadPostData = async () => {
|
|
11
16
|
try {
|
|
12
17
|
setIsLoadingPost(true);
|
|
13
|
-
// Reset hero block before loading new post so it gets re-initialized from the new post's blocks
|
|
14
18
|
resetHeroBlock();
|
|
15
|
-
const lang = languageRef.current;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
: `/api/plugin-blog/${postId}`;
|
|
19
|
+
const lang = languageRef.current || 'nl';
|
|
20
|
+
// We use the relative URL which will hit the Dashboard API
|
|
21
|
+
const url = `/api/plugin-blog/${postId}?language=${lang}&admin=true`;
|
|
19
22
|
const response = await fetch(url);
|
|
20
23
|
if (!response.ok) {
|
|
21
|
-
throw new Error(
|
|
24
|
+
throw new Error(`NODE_HANDSHAKE_FAILED: ${response.status}`);
|
|
22
25
|
}
|
|
23
26
|
const apiDoc = await response.json();
|
|
24
27
|
const blogPost = apiToBlogPost(apiDoc);
|
|
25
28
|
loadPost(blogPost);
|
|
26
29
|
}
|
|
27
30
|
catch (error) {
|
|
28
|
-
console.error('
|
|
29
|
-
alert('Failed to load post. Please try again.');
|
|
31
|
+
console.error('[usePostLoader] CRITICAL_SYNC_ERROR:', error);
|
|
30
32
|
}
|
|
31
33
|
finally {
|
|
32
34
|
setIsLoadingPost(false);
|
|
@@ -34,9 +36,6 @@ export function usePostLoader(postId, currentPostId, loadPost, resetHeroBlock, l
|
|
|
34
36
|
};
|
|
35
37
|
loadPostData();
|
|
36
38
|
}
|
|
37
|
-
|
|
38
|
-
// Language switching is handled separately by handleLanguageChange
|
|
39
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
40
|
-
}, [postId, currentPostId]);
|
|
39
|
+
}, [postId, currentPostId, loadPost, resetHeroBlock]);
|
|
41
40
|
return { isLoadingPost };
|
|
42
41
|
}
|
|
@@ -63,7 +63,6 @@ export function useUnsavedChanges({ state, isDirty, onSave, heroBlock, autoSaveE
|
|
|
63
63
|
try {
|
|
64
64
|
localStorage.setItem(AUTO_SAVE_STORAGE_KEY, enabled.toString());
|
|
65
65
|
setAutoSaveEnabledState(enabled);
|
|
66
|
-
console.log('[useUnsavedChanges] Auto-save preference updated:', enabled);
|
|
67
66
|
}
|
|
68
67
|
catch (error) {
|
|
69
68
|
console.error('[useUnsavedChanges] Failed to save auto-save preference:', error);
|
|
@@ -89,7 +88,6 @@ export function useUnsavedChanges({ state, isDirty, onSave, heroBlock, autoSaveE
|
|
|
89
88
|
// When a post is loaded (postId exists) and isDirty is false, update the saved state reference
|
|
90
89
|
if (postId && !isDirty && lastSavedStateRef.current === '') {
|
|
91
90
|
lastSavedStateRef.current = getStateSnapshot();
|
|
92
|
-
console.log('[useUnsavedChanges] Initialized saved state reference after post load');
|
|
93
91
|
}
|
|
94
92
|
// Also update if isDirty becomes false after being true (e.g., after save or MARK_CLEAN)
|
|
95
93
|
if (!isDirty && lastSavedStateRef.current !== getStateSnapshot()) {
|
|
@@ -106,11 +104,9 @@ export function useUnsavedChanges({ state, isDirty, onSave, heroBlock, autoSaveE
|
|
|
106
104
|
setSaveStatus('saving');
|
|
107
105
|
setCountdown(null);
|
|
108
106
|
countdownStartTimeRef.current = null;
|
|
109
|
-
console.log('[useUnsavedChanges] Auto-saving...');
|
|
110
107
|
await onSave(heroBlock);
|
|
111
108
|
lastSavedStateRef.current = getStateSnapshot();
|
|
112
109
|
setSaveStatus('saved');
|
|
113
|
-
console.log('[useUnsavedChanges] Auto-save completed');
|
|
114
110
|
// Clear save status after 2 seconds
|
|
115
111
|
if (saveStatusTimeoutRef.current) {
|
|
116
112
|
clearTimeout(saveStatusTimeoutRef.current);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Empty State Component
|
|
3
|
-
*
|
|
3
|
+
* Generic empty state for when no posts are found
|
|
4
4
|
*/
|
|
5
5
|
'use client';
|
|
6
6
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
-
import {
|
|
7
|
+
import { PenTool, Plus } from 'lucide-react';
|
|
8
8
|
export function EmptyState({ hasFilters, onCreatePost }) {
|
|
9
|
-
return (_jsxs("div", { className: "flex flex-col items-center justify-center py-20 px-8 bg-neutral-100 dark:bg-neutral-800/50 rounded-[2.5rem] border-2 border-dashed border-neutral-300 dark:border-neutral-700", children: [_jsx("div", { className: "w-24 h-24 rounded-full bg-
|
|
9
|
+
return (_jsxs("div", { className: "flex flex-col items-center justify-center py-20 px-8 bg-neutral-100 dark:bg-neutral-800/50 rounded-[2.5rem] border-2 border-dashed border-neutral-300 dark:border-neutral-700", children: [_jsx("div", { className: "w-24 h-24 rounded-full bg-primary/10 dark:bg-primary/20 flex items-center justify-center mb-6", children: _jsx(PenTool, { className: "text-primary size-12" }) }), _jsx("h3", { className: "text-xl font-black text-neutral-950 dark:text-white uppercase tracking-tight mb-2", children: hasFilters ? 'No Results Found' : 'Your Journal is Empty' }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400 text-center mb-6 max-w-md leading-relaxed", children: hasFilters
|
|
10
10
|
? 'Try adjusting your search or filter criteria to find what you\'re looking for.'
|
|
11
|
-
: '
|
|
11
|
+
: 'It looks like you haven\'t published any stories yet. Start writing to share your insights with the world.' }), !hasFilters && (_jsxs("button", { onClick: onCreatePost, className: "inline-flex items-center gap-2 px-8 py-4 bg-primary text-white rounded-full text-[10px] font-black uppercase tracking-widest hover:bg-primary/90 transition-all shadow-lg shadow-primary/20", children: [_jsx(Plus, { size: 16 }), "Start Writing"] }))] }));
|
|
12
12
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filter Dropdown Component
|
|
3
|
+
* Custom styled dropdown matching the dashboard design
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
interface Option {
|
|
7
|
+
label: string;
|
|
8
|
+
value: string;
|
|
9
|
+
icon?: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
interface FilterDropdownProps {
|
|
12
|
+
options: Option[];
|
|
13
|
+
value: string;
|
|
14
|
+
onChange: (value: string) => void;
|
|
15
|
+
icon: React.ReactNode;
|
|
16
|
+
label: string;
|
|
17
|
+
minWidth?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function FilterDropdown({ options, value, onChange, icon, label, minWidth }: FilterDropdownProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=FilterDropdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterDropdown.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/FilterDropdown.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAsC,MAAM,OAAO,CAAC;AAG3D,UAAU,MAAM;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC1B;AAED,UAAU,mBAAmB;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,cAAc,CAAC,EAC3B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,QAAkB,EACrB,EAAE,mBAAmB,2CA8DrB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filter Dropdown Component
|
|
3
|
+
* Custom styled dropdown matching the dashboard design
|
|
4
|
+
*/
|
|
5
|
+
'use client';
|
|
6
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useState, useRef, useEffect } from 'react';
|
|
8
|
+
import { ChevronDown } from 'lucide-react';
|
|
9
|
+
export function FilterDropdown({ options, value, onChange, icon, label, minWidth = '160px' }) {
|
|
10
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
11
|
+
const dropdownRef = useRef(null);
|
|
12
|
+
const selectedOption = options.find(opt => opt.value === value) || options[0];
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const handleClickOutside = (event) => {
|
|
15
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
16
|
+
setIsOpen(false);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
20
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
21
|
+
}, []);
|
|
22
|
+
return (_jsxs("div", { className: "relative", ref: dropdownRef, children: [_jsxs("button", { onClick: () => setIsOpen(!isOpen), className: `flex items-center gap-3 px-4 py-2.5 rounded-xl transition-all hover:bg-white/5 group ${isOpen ? 'text-primary' : 'text-dashboard-text-secondary'}`, style: { minWidth }, children: [_jsx("div", { className: `${isOpen ? 'text-primary' : 'text-dashboard-text-secondary group-hover:text-primary'} transition-colors`, children: selectedOption.icon || icon }), _jsxs("div", { className: "flex flex-col items-start flex-1 overflow-hidden text-left", children: [_jsx("span", { className: "text-[8px] font-black uppercase tracking-[0.2em] opacity-40", children: label }), _jsx("span", { className: "text-[10px] font-black uppercase tracking-widest truncate w-full", children: selectedOption.label })] }), _jsx(ChevronDown, { className: `size-3 transition-transform duration-300 ${isOpen ? 'rotate-180 text-primary' : 'opacity-30'}` })] }), isOpen && (_jsx("div", { className: "absolute top-full left-0 mt-3 p-2 bg-dashboard-bg/95 backdrop-blur-2xl border border-dashboard-border rounded-2xl shadow-2xl z-[100] min-w-[200px] animate-in fade-in zoom-in-95 duration-200", children: options.map((option) => (_jsxs("button", { onClick: () => {
|
|
23
|
+
onChange(option.value);
|
|
24
|
+
setIsOpen(false);
|
|
25
|
+
}, className: `w-full flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all ${option.value === value
|
|
26
|
+
? 'bg-primary/10 text-primary font-bold'
|
|
27
|
+
: 'text-dashboard-text-secondary hover:bg-white/5 hover:text-dashboard-text'}`, children: [_jsx("div", { className: option.value === value ? 'text-primary' : 'opacity-50', children: option.icon || icon }), _jsx("span", { className: "text-[10px] font-black uppercase tracking-widest", children: option.label })] }, option.value))) }))] }));
|
|
28
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LanguageFlags.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/LanguageFlags.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,UAAU,kBAAkB;IACxB,IAAI,EAAE,YAAY,CAAC;CACtB;AAwBD,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"LanguageFlags.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/LanguageFlags.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,UAAU,kBAAkB;IACxB,IAAI,EAAE,YAAY,CAAC;CACtB;AAwBD,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,EAAE,kBAAkB,kDAqGzD"}
|
|
@@ -46,9 +46,12 @@ export function LanguageFlags({ post }) {
|
|
|
46
46
|
return (_jsx("div", { className: "flex gap-2", children: post.availableLanguages.map((lang) => {
|
|
47
47
|
const langData = post.languages?.[lang];
|
|
48
48
|
const status = langData?.status || 'draft';
|
|
49
|
+
// Try to get title from metadata, fallback to root title
|
|
49
50
|
const langTitle = langData?.metadata?.title || post.title;
|
|
50
51
|
const isHovered = hoveredLang === lang;
|
|
51
|
-
return (_jsxs("div", { ref: (el) => { flagRefs.current[lang] = el; }, className: "relative", onMouseEnter: () =>
|
|
52
|
+
return (_jsxs("div", { ref: (el) => { flagRefs.current[lang] = el; }, className: "relative", onMouseEnter: () => {
|
|
53
|
+
setHoveredLang(lang);
|
|
54
|
+
}, onMouseLeave: () => setHoveredLang(null), children: [_jsxs(motion.div, { whileHover: { scale: 1.15 }, className: `relative flex items-center justify-center w-7 h-4.5 rounded shadow-sm overflow-hidden border cursor-help transition-colors ${status === 'published'
|
|
52
55
|
? 'border-green-500/40 bg-white'
|
|
53
56
|
: 'border-amber-500/40 bg-white opacity-90'}`, children: [_jsx("img", { src: getFlagUrl(lang), alt: lang, className: "w-full h-full object-cover" }), status !== 'published' && (_jsx("div", { className: "absolute inset-0 bg-amber-500/5" }))] }), typeof document !== 'undefined' && createPortal(_jsx(AnimatePresence, { children: isHovered && tooltipCoords && (_jsx(motion.div, { initial: { opacity: 0, y: -90, x: '-50%', scale: 0.95 }, animate: { opacity: 1, y: -100, x: '-50%', scale: 1 }, exit: { opacity: 0, y: -90, x: '-50%', scale: 0.95 }, className: "fixed z-[9999] pointer-events-none", style: {
|
|
54
57
|
top: tooltipCoords.top - 12,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostCards.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/PostCards.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,YAAY,EAAc,MAAM,kBAAkB,CAAC;AAK5D,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AA4BD,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,MAAM,EACN,MAAM,EACN,SAAS,EACT,WAAW,EACX,QAAQ,GACX,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"PostCards.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/PostCards.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,YAAY,EAAc,MAAM,kBAAkB,CAAC;AAK5D,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AA4BD,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,MAAM,EACN,MAAM,EACN,SAAS,EACT,WAAW,EACX,QAAQ,GACX,EAAE,cAAc,2CA+IhB"}
|
|
@@ -4,26 +4,25 @@
|
|
|
4
4
|
*/
|
|
5
5
|
'use client';
|
|
6
6
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { Image } from '@jhits/plugin-images';
|
|
7
|
+
import { FileText, Tag, Activity } from 'lucide-react';
|
|
8
|
+
import { Image as PluginImage } from '@jhits/plugin-images';
|
|
10
9
|
import { PostActionsMenu } from './PostActionsMenu';
|
|
11
10
|
import { LanguageFlags } from './LanguageFlags';
|
|
12
11
|
import { useSession } from 'next-auth/react';
|
|
13
12
|
function getStatusBadgeColor(status) {
|
|
14
13
|
switch (status) {
|
|
15
14
|
case 'published':
|
|
16
|
-
return 'bg-
|
|
15
|
+
return 'bg-emerald-500 text-white border-emerald-400 shadow-lg shadow-emerald-500/20';
|
|
17
16
|
case 'draft':
|
|
18
|
-
return 'bg-amber-500
|
|
17
|
+
return 'bg-amber-500 text-white border-amber-400 shadow-lg shadow-amber-500/20';
|
|
19
18
|
case 'scheduled':
|
|
20
|
-
return 'bg-blue-500
|
|
19
|
+
return 'bg-blue-500 text-white border-blue-400 shadow-lg shadow-blue-500/20';
|
|
21
20
|
case 'archived':
|
|
22
|
-
return 'bg-neutral-500
|
|
21
|
+
return 'bg-neutral-500 text-white border-neutral-400';
|
|
23
22
|
case 'not-translated':
|
|
24
|
-
return 'bg-red-500/10 text-red-
|
|
23
|
+
return 'bg-red-500/10 text-red-500 border-red-500/20 italic';
|
|
25
24
|
default:
|
|
26
|
-
return 'bg-neutral-500/10 text-neutral-
|
|
25
|
+
return 'bg-neutral-500/10 text-neutral-500 border-neutral-500/20';
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
function formatDate(dateString, locale) {
|
|
@@ -38,43 +37,27 @@ function formatDate(dateString, locale) {
|
|
|
38
37
|
export function PostCards({ posts, locale, onEdit, onPreview, onDuplicate, onDelete, }) {
|
|
39
38
|
const { data: session, status: sessionStatus } = useSession();
|
|
40
39
|
const currentUserId = session?.user?.id;
|
|
41
|
-
const [userMap, setUserMap] = useState({});
|
|
42
40
|
// Helper function to check if user is the owner
|
|
43
41
|
const isPostOwner = (post) => {
|
|
44
42
|
if (sessionStatus === 'loading')
|
|
45
|
-
return false;
|
|
43
|
+
return false;
|
|
46
44
|
if (!currentUserId || !post.authorId)
|
|
47
45
|
return false;
|
|
48
|
-
// Convert both to strings for comparison to handle ObjectId vs string
|
|
49
46
|
return String(currentUserId) === String(post.authorId);
|
|
50
47
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
users.forEach((user) => {
|
|
60
|
-
const id = user._id?.toString();
|
|
61
|
-
if (id) {
|
|
62
|
-
map[id] = user.name || user.email || 'Unknown';
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
setUserMap(map);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
console.error('Failed to fetch users:', error);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
fetchUsers();
|
|
73
|
-
}, []);
|
|
74
|
-
const getAuthorName = (authorId) => {
|
|
75
|
-
if (!authorId)
|
|
76
|
-
return 'Unknown';
|
|
77
|
-
return userMap[authorId] || authorId;
|
|
48
|
+
const getAuthorData = (post) => {
|
|
49
|
+
if (post.author) {
|
|
50
|
+
return {
|
|
51
|
+
name: post.author.name || 'Unknown Author',
|
|
52
|
+
image: post.author.image
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return { name: 'Unknown Author' };
|
|
78
56
|
};
|
|
79
|
-
return (_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2
|
|
57
|
+
return (_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6", children: posts.map((post) => {
|
|
58
|
+
const author = getAuthorData(post);
|
|
59
|
+
const owner = isPostOwner(post);
|
|
60
|
+
const initials = author.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
|
|
61
|
+
return (_jsxs("div", { className: "group relative flex flex-col h-full animate-in fade-in slide-in-from-bottom-2 duration-500", children: [_jsx("div", { className: "absolute inset-0 bg-dashboard-card/40 backdrop-blur-md rounded-[2.5rem] border border-dashboard-border/40 transition-all duration-500 group-hover:border-primary/30 group-hover:bg-primary/5 group-hover:shadow-xl group-hover:shadow-primary/5" }), _jsxs("div", { className: "relative p-3.5 flex flex-col h-full space-y-5", children: [_jsxs("div", { className: "aspect-[16/9.5] relative bg-dashboard-bg/50 rounded-[2rem] overflow-hidden border border-dashboard-border/40 group-hover:border-primary/20 transition-all shadow-sm", children: [post.featuredImage ? (_jsx(PluginImage, { id: post.featuredImage, alt: post.title, fill: true, editable: false, className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" })) : (_jsx("div", { className: "w-full h-full flex flex-col items-center justify-center bg-dashboard-bg/50 opacity-20", children: _jsx(FileText, { size: 40 }) })), _jsx("div", { className: "absolute top-3.5 right-3.5", children: _jsx("span", { className: `inline-flex items-center px-3 py-1 rounded-full text-[9px] font-bold uppercase tracking-wider border backdrop-blur-md shadow-sm ${getStatusBadgeColor(post.status)}`, children: post.status === 'not-translated' ? 'Edition Missing' : post.status }) }), owner && (_jsx("div", { className: "absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-all duration-500 flex items-center justify-center opacity-0 group-hover:opacity-100 z-20", children: _jsx("div", { className: "bg-white/10 backdrop-blur-md p-1.5 rounded-2xl border border-white/20 shadow-2xl flex items-center gap-1 scale-90 group-hover:scale-100 transition-transform duration-500", children: _jsx(PostActionsMenu, { onEdit: () => onEdit(post.id), onPreview: () => onPreview(post.id), onDuplicate: () => onDuplicate(post.id), onDelete: () => onDelete(post.id) }) }) }))] }), _jsxs("div", { className: "px-3.5 pb-2.5 flex-1 flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-3 mb-3.5", children: [_jsx(LanguageFlags, { post: post }), _jsx("div", { className: "h-4 w-px bg-dashboard-border/30" }), _jsxs("span", { className: "text-[10px] font-bold text-primary uppercase tracking-widest flex items-center gap-1.5 opacity-80", children: [_jsx(Tag, { size: 12 }), post.category || 'Article'] })] }), _jsx("button", { onClick: () => onEdit(post.id), className: "text-left w-full group/title mb-2.5", children: _jsx("h3", { className: "text-xl font-bold text-dashboard-text tracking-tight leading-tight line-clamp-2 group-hover/title:text-primary transition-colors", children: post.title }) }), _jsx("p", { className: "text-sm text-dashboard-text-secondary font-medium line-clamp-2 opacity-60 mb-6", children: post.excerpt || 'No summary available for this publication.' }), _jsxs("div", { className: "pt-4 border-t border-dashboard-border/30 mt-auto space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [_jsx("div", { className: `size-9 rounded-xl flex items-center justify-center text-xs font-bold border transition-all overflow-hidden shrink-0 ${owner ? 'bg-primary text-white border-primary/20 shadow-lg shadow-primary/20' : 'bg-dashboard-bg/50 text-dashboard-text-secondary border-dashboard-border/60'}`, children: author.image ? (_jsx("img", { src: author.image, alt: author.name, className: "size-full object-cover", crossOrigin: "anonymous" })) : initials }), _jsxs("div", { className: "flex flex-col min-w-0", children: [_jsxs("span", { className: "text-[10px] font-bold text-dashboard-text uppercase tracking-tight truncate", children: [author.name, " ", owner && _jsx("span", { className: "text-primary ml-0.5", children: "(YOU)" })] }), _jsx("span", { className: "text-[9px] font-semibold text-dashboard-text-secondary uppercase tracking-widest opacity-60", children: formatDate(post.updatedAt, locale) })] })] }), post.status === 'published' && (_jsxs("div", { className: "flex items-center gap-1.5 px-2.5 py-0.5 bg-emerald-500/10 text-emerald-500 rounded-full border border-emerald-500/20", children: [_jsx(Activity, { size: 10, className: "animate-pulse" }), _jsx("span", { className: "text-[8px] font-bold uppercase tracking-widest", children: "Live" })] }))] }), _jsx("div", { className: "bg-dashboard-bg/40 px-3 py-1.5 rounded-xl border border-dashboard-border/30", children: _jsxs("p", { className: "text-[9px] font-mono text-dashboard-text-secondary/50 uppercase tracking-tight truncate", children: ["Path: /", post.slug] }) })] })] })] })] }, post.id));
|
|
62
|
+
}) }));
|
|
80
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostFilters.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/PostFilters.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"PostFilters.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/PostFilters.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,UAAU,GAAG,KAAK,CAAC;IACjC,oBAAoB,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,KAAK,IAAI,CAAC;IAC1D,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAChC;AAED,wBAAgB,WAAW,CAAC,EACxB,MAAM,EACN,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,sBAAsB,EACtB,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,kBAAkB,GACrB,EAAE,gBAAgB,2CAoGlB"}
|
|
@@ -5,16 +5,47 @@
|
|
|
5
5
|
'use client';
|
|
6
6
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { Search, Filter, Tag, Globe } from 'lucide-react';
|
|
8
|
+
import { FilterDropdown } from './FilterDropdown';
|
|
8
9
|
export function PostFilters({ search, onSearchChange, statusFilter, onStatusFilterChange, categoryFilter, onCategoryFilterChange, categories, language, onLanguageChange, availableLanguages, }) {
|
|
9
10
|
const langNames = {
|
|
10
11
|
nl: 'Nederlands (NL)',
|
|
11
|
-
en: 'English (
|
|
12
|
-
sv: 'Svenska (
|
|
12
|
+
en: 'English (GB)',
|
|
13
|
+
sv: 'Svenska (SE)',
|
|
13
14
|
de: 'Deutsch (DE)',
|
|
14
15
|
fr: 'Français (FR)',
|
|
15
16
|
es: 'Español (ES)',
|
|
16
17
|
it: 'Italiano (IT)',
|
|
17
18
|
pt: 'Português (PT)',
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
+
const getFlagUrl = (lang) => {
|
|
21
|
+
const mapping = {
|
|
22
|
+
en: 'gb',
|
|
23
|
+
nl: 'nl',
|
|
24
|
+
sv: 'se',
|
|
25
|
+
de: 'de',
|
|
26
|
+
fr: 'fr',
|
|
27
|
+
es: 'es',
|
|
28
|
+
it: 'it',
|
|
29
|
+
pt: 'pt'
|
|
30
|
+
};
|
|
31
|
+
const countryCode = mapping[lang] || lang;
|
|
32
|
+
return `https://flagcdn.com/w40/${countryCode.toLowerCase()}.png`;
|
|
33
|
+
};
|
|
34
|
+
const statusOptions = [
|
|
35
|
+
{ label: 'All Statuses', value: 'all' },
|
|
36
|
+
{ label: 'Published', value: 'published' },
|
|
37
|
+
{ label: 'Draft', value: 'draft' },
|
|
38
|
+
{ label: 'Scheduled', value: 'scheduled' },
|
|
39
|
+
{ label: 'Archived', value: 'archived' },
|
|
40
|
+
];
|
|
41
|
+
const categoryOptions = [
|
|
42
|
+
{ label: 'All Categories', value: 'all' },
|
|
43
|
+
...categories.map(cat => ({ label: cat, value: cat }))
|
|
44
|
+
];
|
|
45
|
+
const languageOptions = availableLanguages.map(lang => ({
|
|
46
|
+
label: langNames[lang] || lang.toUpperCase(),
|
|
47
|
+
value: lang,
|
|
48
|
+
icon: _jsx("img", { src: getFlagUrl(lang), alt: "", className: "w-4 h-2.5 rounded-sm object-cover shadow-sm border border-white/10" })
|
|
49
|
+
}));
|
|
50
|
+
return (_jsxs("div", { className: "flex-1 flex flex-col md:flex-row items-center gap-4", children: [_jsxs("div", { className: "relative flex-1 group w-full", children: [_jsx(Search, { className: "absolute left-4 top-1/2 -translate-y-1/2 text-dashboard-text-secondary/40 group-focus-within:text-primary transition-colors duration-300", size: 18 }), _jsx("input", { id: "blog-post-search", name: "blog-post-search", type: "text", value: search, onChange: (e) => onSearchChange(e.target.value), placeholder: "Search articles by title...", className: "w-full pl-12 pr-6 py-3.5 bg-dashboard-card/40 border border-dashboard-border/40 rounded-2xl text-sm font-semibold text-dashboard-text placeholder:text-dashboard-text-secondary/30 outline-none focus:border-primary/30 transition-all" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5 p-1.5 bg-dashboard-card/50 rounded-xl border border-dashboard-border/40 w-full md:w-auto", children: [_jsx(FilterDropdown, { label: "Status", icon: _jsx(Filter, { size: 14, className: "text-primary/60" }), value: statusFilter, options: statusOptions, onChange: (val) => onStatusFilterChange(val), minWidth: "140px" }), _jsx("div", { className: "h-6 w-px bg-dashboard-border/30 mx-1 hidden md:block" }), _jsx(FilterDropdown, { label: "Category", icon: _jsx(Tag, { size: 14, className: "text-primary/60" }), value: categoryFilter, options: categoryOptions, onChange: onCategoryFilterChange, minWidth: "150px" }), _jsx("div", { className: "h-6 w-px bg-dashboard-border/30 mx-1 hidden md:block" }), _jsx(FilterDropdown, { label: "Language", icon: _jsx(Globe, { size: 14, className: "text-primary/60" }), value: language, options: languageOptions, onChange: onLanguageChange, minWidth: "160px" })] })] }));
|
|
20
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostManagerView.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/PostManagerView.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"PostManagerView.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/PostManagerView.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAcH,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAMD,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,oBAAoB,2CA8OvE"}
|