@jhits/plugin-blog 0.0.19 → 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 -490
- 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 -594
- 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
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
|
-
import React
|
|
9
|
-
import {
|
|
10
|
-
import { Image } from '@jhits/plugin-images';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { FileText, Tag, Activity } from 'lucide-react';
|
|
10
|
+
import { Image as PluginImage } from '@jhits/plugin-images';
|
|
11
11
|
import { PostListItem, PostStatus } from '../../types/post';
|
|
12
12
|
import { PostActionsMenu } from './PostActionsMenu';
|
|
13
13
|
import { LanguageFlags } from './LanguageFlags';
|
|
@@ -25,17 +25,17 @@ export interface PostCardsProps {
|
|
|
25
25
|
function getStatusBadgeColor(status: PostStatus | 'not-translated') {
|
|
26
26
|
switch (status) {
|
|
27
27
|
case 'published':
|
|
28
|
-
return 'bg-
|
|
28
|
+
return 'bg-emerald-500 text-white border-emerald-400 shadow-lg shadow-emerald-500/20';
|
|
29
29
|
case 'draft':
|
|
30
|
-
return 'bg-amber-500
|
|
30
|
+
return 'bg-amber-500 text-white border-amber-400 shadow-lg shadow-amber-500/20';
|
|
31
31
|
case 'scheduled':
|
|
32
|
-
return 'bg-blue-500
|
|
32
|
+
return 'bg-blue-500 text-white border-blue-400 shadow-lg shadow-blue-500/20';
|
|
33
33
|
case 'archived':
|
|
34
|
-
return 'bg-neutral-500
|
|
34
|
+
return 'bg-neutral-500 text-white border-neutral-400';
|
|
35
35
|
case 'not-translated':
|
|
36
|
-
return 'bg-red-500/10 text-red-
|
|
36
|
+
return 'bg-red-500/10 text-red-500 border-red-500/20 italic';
|
|
37
37
|
default:
|
|
38
|
-
return 'bg-neutral-500/10 text-neutral-
|
|
38
|
+
return 'bg-neutral-500/10 text-neutral-500 border-neutral-500/20';
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -58,150 +58,144 @@ export function PostCards({
|
|
|
58
58
|
}: PostCardsProps) {
|
|
59
59
|
const { data: session, status: sessionStatus } = useSession();
|
|
60
60
|
const currentUserId = (session?.user as any)?.id;
|
|
61
|
-
const [userMap, setUserMap] = useState<Record<string, string>>({});
|
|
62
61
|
|
|
63
62
|
// Helper function to check if user is the owner
|
|
64
63
|
const isPostOwner = (post: PostListItem): boolean => {
|
|
65
|
-
if (sessionStatus === 'loading') return false;
|
|
64
|
+
if (sessionStatus === 'loading') return false;
|
|
66
65
|
if (!currentUserId || !post.authorId) return false;
|
|
67
|
-
// Convert both to strings for comparison to handle ObjectId vs string
|
|
68
66
|
return String(currentUserId) === String(post.authorId);
|
|
69
67
|
};
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
users.forEach((user: { _id: string; name?: string; email?: string }) => {
|
|
80
|
-
const id = user._id?.toString();
|
|
81
|
-
if (id) {
|
|
82
|
-
map[id] = user.name || user.email || 'Unknown';
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
setUserMap(map);
|
|
86
|
-
}
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.error('Failed to fetch users:', error);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
fetchUsers();
|
|
92
|
-
}, []);
|
|
93
|
-
|
|
94
|
-
const getAuthorName = (authorId?: string) => {
|
|
95
|
-
if (!authorId) return 'Unknown';
|
|
96
|
-
return userMap[authorId] || authorId;
|
|
69
|
+
const getAuthorData = (post: PostListItem) => {
|
|
70
|
+
if (post.author) {
|
|
71
|
+
return {
|
|
72
|
+
name: post.author.name || 'Unknown Author',
|
|
73
|
+
image: post.author.image
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return { name: 'Unknown Author' };
|
|
97
77
|
};
|
|
98
78
|
|
|
99
79
|
return (
|
|
100
|
-
<div className="grid grid-cols-1 md:grid-cols-2
|
|
101
|
-
{posts.map((post) =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<div
|
|
108
|
-
{post.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<div className="bg-white/90 dark:bg-neutral-900/90 backdrop-blur-sm rounded-full p-1 shadow-lg border border-neutral-200 dark:border-neutral-700">
|
|
125
|
-
<PostActionsMenu
|
|
126
|
-
onEdit={() => onEdit(post.id)}
|
|
127
|
-
onPreview={() => onPreview(post.id)}
|
|
128
|
-
onDuplicate={() => onDuplicate(post.id)}
|
|
129
|
-
onDelete={() => onDelete(post.id)}
|
|
80
|
+
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
|
81
|
+
{posts.map((post) => {
|
|
82
|
+
const author = getAuthorData(post);
|
|
83
|
+
const owner = isPostOwner(post);
|
|
84
|
+
const initials = author.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
key={post.id}
|
|
89
|
+
className="group relative flex flex-col h-full animate-in fade-in slide-in-from-bottom-2 duration-500"
|
|
90
|
+
>
|
|
91
|
+
{/* Card Background with glass effect */}
|
|
92
|
+
<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" />
|
|
93
|
+
|
|
94
|
+
<div className="relative p-3.5 flex flex-col h-full space-y-5">
|
|
95
|
+
{/* 1. Featured Image Area */}
|
|
96
|
+
<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">
|
|
97
|
+
{post.featuredImage ? (
|
|
98
|
+
<PluginImage
|
|
99
|
+
id={post.featuredImage}
|
|
100
|
+
alt={post.title}
|
|
101
|
+
fill
|
|
102
|
+
editable={false}
|
|
103
|
+
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
|
|
130
104
|
/>
|
|
105
|
+
) : (
|
|
106
|
+
<div className="w-full h-full flex flex-col items-center justify-center bg-dashboard-bg/50 opacity-20">
|
|
107
|
+
<FileText size={40} />
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
{/* Status Badge Overlay */}
|
|
112
|
+
<div className="absolute top-3.5 right-3.5">
|
|
113
|
+
<span
|
|
114
|
+
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)}`}
|
|
115
|
+
>
|
|
116
|
+
{post.status === 'not-translated' ? 'Edition Missing' : post.status}
|
|
117
|
+
</span>
|
|
131
118
|
</div>
|
|
119
|
+
|
|
120
|
+
{/* Quick Actions Overlay */}
|
|
121
|
+
{owner && (
|
|
122
|
+
<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">
|
|
123
|
+
<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">
|
|
124
|
+
<PostActionsMenu
|
|
125
|
+
onEdit={() => onEdit(post.id)}
|
|
126
|
+
onPreview={() => onPreview(post.id)}
|
|
127
|
+
onDuplicate={() => onDuplicate(post.id)}
|
|
128
|
+
onDelete={() => onDelete(post.id)}
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
132
133
|
</div>
|
|
133
|
-
)}
|
|
134
|
-
{/* Status Badge Overlay */}
|
|
135
|
-
<div className="absolute top-4 right-4">
|
|
136
|
-
<span
|
|
137
|
-
className={`inline-flex items-center px-3 py-1 rounded-full text-[10px] font-black uppercase tracking-wider border backdrop-blur-sm ${getStatusBadgeColor(post.status)}`}
|
|
138
|
-
>
|
|
139
|
-
{post.status}
|
|
140
|
-
</span>
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
134
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
</h3>
|
|
155
|
-
</button>
|
|
156
|
-
<p className="text-xs text-neutral-500 dark:text-neutral-400 font-mono line-clamp-1">
|
|
157
|
-
/{post.slug}
|
|
158
|
-
</p>
|
|
159
|
-
</div>
|
|
135
|
+
{/* 2. Content Area */}
|
|
136
|
+
<div className="px-3.5 pb-2.5 flex-1 flex flex-col">
|
|
137
|
+
<div className="flex items-center gap-3 mb-3.5">
|
|
138
|
+
<LanguageFlags post={post} />
|
|
139
|
+
<div className="h-4 w-px bg-dashboard-border/30" />
|
|
140
|
+
<span className="text-[10px] font-bold text-primary uppercase tracking-widest flex items-center gap-1.5 opacity-80">
|
|
141
|
+
<Tag size={12} />
|
|
142
|
+
{post.category || 'Article'}
|
|
143
|
+
</span>
|
|
144
|
+
</div>
|
|
160
145
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
146
|
+
<button
|
|
147
|
+
onClick={() => onEdit(post.id)}
|
|
148
|
+
className="text-left w-full group/title mb-2.5"
|
|
149
|
+
>
|
|
150
|
+
<h3 className="text-xl font-bold text-dashboard-text tracking-tight leading-tight line-clamp-2 group-hover/title:text-primary transition-colors">
|
|
151
|
+
{post.title}
|
|
152
|
+
</h3>
|
|
153
|
+
</button>
|
|
154
|
+
|
|
155
|
+
<p className="text-sm text-dashboard-text-secondary font-medium line-clamp-2 opacity-60 mb-6">
|
|
156
|
+
{post.excerpt || 'No summary available for this publication.'}
|
|
166
157
|
</p>
|
|
167
|
-
)}
|
|
168
|
-
</div>
|
|
169
|
-
|
|
170
|
-
{/* Languages */}
|
|
171
|
-
<div className="mb-6">
|
|
172
|
-
<LanguageFlags post={post} />
|
|
173
|
-
</div>
|
|
174
|
-
|
|
175
|
-
{/* Meta Information */}
|
|
176
|
-
<div className="space-y-3 pt-4 border-t border-neutral-200 dark:border-neutral-700 mt-auto">
|
|
177
|
-
{/* Author */}
|
|
178
|
-
<div className="flex items-center gap-2">
|
|
179
|
-
{isPostOwner(post) ? (
|
|
180
|
-
<UserCheck size={14} className="text-primary" />
|
|
181
|
-
) : (
|
|
182
|
-
<User size={14} className="text-neutral-400" />
|
|
183
|
-
)}
|
|
184
|
-
<span className={`text-xs ${isPostOwner(post) ? 'text-primary font-semibold' : 'text-neutral-600 dark:text-neutral-400'}`}>
|
|
185
|
-
{getAuthorName(post.authorId)}
|
|
186
|
-
{isPostOwner(post) && (
|
|
187
|
-
<span className="ml-1">(You)</span>
|
|
188
|
-
)}
|
|
189
|
-
</span>
|
|
190
|
-
</div>
|
|
191
158
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
159
|
+
{/* Footer Meta */}
|
|
160
|
+
<div className="pt-4 border-t border-dashboard-border/30 mt-auto space-y-4">
|
|
161
|
+
<div className="flex items-center justify-between gap-4">
|
|
162
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
163
|
+
<div className={`size-9 rounded-xl flex items-center justify-center text-xs font-bold border transition-all overflow-hidden shrink-0 ${
|
|
164
|
+
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'
|
|
165
|
+
}`}>
|
|
166
|
+
{author.image ? (
|
|
167
|
+
<img src={author.image} alt={author.name} className="size-full object-cover" crossOrigin="anonymous" />
|
|
168
|
+
) : initials}
|
|
169
|
+
</div>
|
|
170
|
+
<div className="flex flex-col min-w-0">
|
|
171
|
+
<span className="text-[10px] font-bold text-dashboard-text uppercase tracking-tight truncate">
|
|
172
|
+
{author.name} {owner && <span className="text-primary ml-0.5">(YOU)</span>}
|
|
173
|
+
</span>
|
|
174
|
+
<span className="text-[9px] font-semibold text-dashboard-text-secondary uppercase tracking-widest opacity-60">
|
|
175
|
+
{formatDate(post.updatedAt, locale)}
|
|
176
|
+
</span>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
{post.status === 'published' && (
|
|
181
|
+
<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">
|
|
182
|
+
<Activity size={10} className="animate-pulse" />
|
|
183
|
+
<span className="text-[8px] font-bold uppercase tracking-widest">Live</span>
|
|
184
|
+
</div>
|
|
185
|
+
)}
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div className="bg-dashboard-bg/40 px-3 py-1.5 rounded-xl border border-dashboard-border/30">
|
|
189
|
+
<p className="text-[9px] font-mono text-dashboard-text-secondary/50 uppercase tracking-tight truncate">
|
|
190
|
+
Path: /{post.slug}
|
|
191
|
+
</p>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
198
194
|
</div>
|
|
199
195
|
</div>
|
|
200
|
-
|
|
201
196
|
</div>
|
|
202
|
-
|
|
203
|
-
)
|
|
197
|
+
);
|
|
198
|
+
})}
|
|
204
199
|
</div>
|
|
205
200
|
);
|
|
206
201
|
}
|
|
207
|
-
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import { Search, Filter, Tag, Globe } from 'lucide-react';
|
|
10
10
|
import { PostStatus } from '../../types/post';
|
|
11
|
+
import { FilterDropdown } from './FilterDropdown';
|
|
11
12
|
|
|
12
13
|
export interface PostFiltersProps {
|
|
13
14
|
search: string;
|
|
@@ -36,97 +37,101 @@ export function PostFilters({
|
|
|
36
37
|
}: PostFiltersProps) {
|
|
37
38
|
const langNames: Record<string, string> = {
|
|
38
39
|
nl: 'Nederlands (NL)',
|
|
39
|
-
en: 'English (
|
|
40
|
-
sv: 'Svenska (
|
|
40
|
+
en: 'English (GB)',
|
|
41
|
+
sv: 'Svenska (SE)',
|
|
41
42
|
de: 'Deutsch (DE)',
|
|
42
43
|
fr: 'Français (FR)',
|
|
43
44
|
es: 'Español (ES)',
|
|
44
45
|
it: 'Italiano (IT)',
|
|
45
46
|
pt: 'Português (PT)',
|
|
46
47
|
};
|
|
48
|
+
|
|
49
|
+
const getFlagUrl = (lang: string) => {
|
|
50
|
+
const mapping: Record<string, string> = {
|
|
51
|
+
en: 'gb',
|
|
52
|
+
nl: 'nl',
|
|
53
|
+
sv: 'se',
|
|
54
|
+
de: 'de',
|
|
55
|
+
fr: 'fr',
|
|
56
|
+
es: 'es',
|
|
57
|
+
it: 'it',
|
|
58
|
+
pt: 'pt'
|
|
59
|
+
};
|
|
60
|
+
const countryCode = mapping[lang] || lang;
|
|
61
|
+
return `https://flagcdn.com/w40/${countryCode.toLowerCase()}.png`;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const statusOptions = [
|
|
65
|
+
{ label: 'All Statuses', value: 'all' },
|
|
66
|
+
{ label: 'Published', value: 'published' },
|
|
67
|
+
{ label: 'Draft', value: 'draft' },
|
|
68
|
+
{ label: 'Scheduled', value: 'scheduled' },
|
|
69
|
+
{ label: 'Archived', value: 'archived' },
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const categoryOptions = [
|
|
73
|
+
{ label: 'All Categories', value: 'all' },
|
|
74
|
+
...categories.map(cat => ({ label: cat, value: cat }))
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const languageOptions = availableLanguages.map(lang => ({
|
|
78
|
+
label: langNames[lang] || lang.toUpperCase(),
|
|
79
|
+
value: lang,
|
|
80
|
+
icon: <img src={getFlagUrl(lang)} alt="" className="w-4 h-2.5 rounded-sm object-cover shadow-sm border border-white/10" />
|
|
81
|
+
}));
|
|
82
|
+
|
|
47
83
|
return (
|
|
48
|
-
<div className="flex flex-col
|
|
84
|
+
<div className="flex-1 flex flex-col md:flex-row items-center gap-4">
|
|
49
85
|
{/* Search Input */}
|
|
50
|
-
<div className="relative flex-1">
|
|
51
|
-
<
|
|
52
|
-
Search posts by title or content
|
|
53
|
-
</label>
|
|
54
|
-
<Search className="absolute left-4 top-1/2 -translate-y-1/2 text-neutral-400 size-4" />
|
|
86
|
+
<div className="relative flex-1 group w-full">
|
|
87
|
+
<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} />
|
|
55
88
|
<input
|
|
56
89
|
id="blog-post-search"
|
|
57
90
|
name="blog-post-search"
|
|
58
91
|
type="text"
|
|
59
92
|
value={search}
|
|
60
93
|
onChange={(e) => onSearchChange(e.target.value)}
|
|
61
|
-
placeholder="Search
|
|
62
|
-
className="w-full pl-
|
|
94
|
+
placeholder="Search articles by title..."
|
|
95
|
+
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"
|
|
63
96
|
/>
|
|
64
97
|
</div>
|
|
65
98
|
|
|
66
|
-
{/*
|
|
67
|
-
<div className="
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
<select
|
|
73
|
-
id="blog-post-status-filter"
|
|
74
|
-
name="blog-post-status-filter"
|
|
99
|
+
{/* Selects Container */}
|
|
100
|
+
<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">
|
|
101
|
+
{/* Status Filter */}
|
|
102
|
+
<FilterDropdown
|
|
103
|
+
label="Status"
|
|
104
|
+
icon={<Filter size={14} className="text-primary/60" />}
|
|
75
105
|
value={statusFilter}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
<option value="scheduled">Scheduled</option>
|
|
83
|
-
<option value="archived">Archived</option>
|
|
84
|
-
</select>
|
|
85
|
-
</div>
|
|
106
|
+
options={statusOptions}
|
|
107
|
+
onChange={(val) => onStatusFilterChange(val as any)}
|
|
108
|
+
minWidth="140px"
|
|
109
|
+
/>
|
|
110
|
+
|
|
111
|
+
<div className="h-6 w-px bg-dashboard-border/30 mx-1 hidden md:block" />
|
|
86
112
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
</label>
|
|
92
|
-
<Tag className="absolute left-4 top-1/2 -translate-y-1/2 text-neutral-400 size-4 pointer-events-none" />
|
|
93
|
-
<select
|
|
94
|
-
id="blog-post-category-filter"
|
|
95
|
-
name="blog-post-category-filter"
|
|
113
|
+
{/* Category Filter */}
|
|
114
|
+
<FilterDropdown
|
|
115
|
+
label="Category"
|
|
116
|
+
icon={<Tag size={14} className="text-primary/60" />}
|
|
96
117
|
value={categoryFilter}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
{categories.map((category) => (
|
|
102
|
-
<option key={category} value={category}>
|
|
103
|
-
{category}
|
|
104
|
-
</option>
|
|
105
|
-
))}
|
|
106
|
-
</select>
|
|
107
|
-
</div>
|
|
118
|
+
options={categoryOptions}
|
|
119
|
+
onChange={onCategoryFilterChange}
|
|
120
|
+
minWidth="150px"
|
|
121
|
+
/>
|
|
108
122
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
<select
|
|
116
|
-
id="blog-post-language-filter"
|
|
117
|
-
name="blog-post-language-filter"
|
|
123
|
+
<div className="h-6 w-px bg-dashboard-border/30 mx-1 hidden md:block" />
|
|
124
|
+
|
|
125
|
+
{/* Language Selector */}
|
|
126
|
+
<FilterDropdown
|
|
127
|
+
label="Language"
|
|
128
|
+
icon={<Globe size={14} className="text-primary/60" />}
|
|
118
129
|
value={language}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
<option key={lang} value={lang}>
|
|
124
|
-
{langNames[lang] || lang.toUpperCase()}
|
|
125
|
-
</option>
|
|
126
|
-
))}
|
|
127
|
-
</select>
|
|
130
|
+
options={languageOptions}
|
|
131
|
+
onChange={onLanguageChange}
|
|
132
|
+
minWidth="160px"
|
|
133
|
+
/>
|
|
128
134
|
</div>
|
|
129
135
|
</div>
|
|
130
136
|
);
|
|
131
137
|
}
|
|
132
|
-
|