@jhits/plugin-blog 0.0.9 → 0.0.11
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/package.json +58 -59
- package/src/api/categories.ts +0 -43
- package/src/api/check-title.ts +0 -60
- package/src/api/config-handler.ts +0 -76
- package/src/api/handler.ts +0 -418
- package/src/api/index.ts +0 -33
- package/src/api/route.ts +0 -116
- package/src/api/router.ts +0 -128
- package/src/api-server.ts +0 -11
- package/src/config.ts +0 -161
- package/src/hooks/index.d.ts +0 -8
- package/src/hooks/index.d.ts.map +0 -1
- package/src/hooks/index.js +0 -7
- package/src/hooks/index.ts +0 -9
- package/src/hooks/useBlog.d.ts +0 -31
- package/src/hooks/useBlog.d.ts.map +0 -1
- package/src/hooks/useBlog.js +0 -57
- package/src/hooks/useBlog.ts +0 -85
- package/src/hooks/useBlogs.d.ts +0 -39
- package/src/hooks/useBlogs.d.ts.map +0 -1
- package/src/hooks/useBlogs.js +0 -82
- package/src/hooks/useBlogs.ts +0 -123
- package/src/hooks/useCategories.d.ts +0 -9
- package/src/hooks/useCategories.d.ts.map +0 -1
- package/src/hooks/useCategories.js +0 -70
- package/src/hooks/useCategories.ts +0 -76
- package/src/index.d.ts +0 -55
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -228
- package/src/index.server.ts +0 -14
- package/src/index.tsx +0 -335
- package/src/init.d.ts +0 -40
- package/src/init.d.ts.map +0 -1
- package/src/init.js +0 -41
- package/src/init.tsx +0 -63
- package/src/lib/blocks/BlockRenderer.d.ts +0 -54
- package/src/lib/blocks/BlockRenderer.d.ts.map +0 -1
- package/src/lib/blocks/BlockRenderer.js +0 -54
- package/src/lib/blocks/BlockRenderer.tsx +0 -141
- package/src/lib/blocks/index.ts +0 -6
- package/src/lib/config-storage.d.ts +0 -30
- package/src/lib/config-storage.d.ts.map +0 -1
- package/src/lib/config-storage.js +0 -31
- package/src/lib/config-storage.ts +0 -65
- package/src/lib/index.ts +0 -9
- 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/ColumnsBlock.js +0 -182
- package/src/lib/layouts/blocks/ColumnsBlock.tsx +0 -298
- package/src/lib/layouts/blocks/ColumnsBlock.tsx.tmp +0 -81
- 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/blocks/SectionBlock.js +0 -44
- package/src/lib/layouts/blocks/SectionBlock.tsx +0 -104
- package/src/lib/layouts/blocks/index.ts +0 -8
- package/src/lib/layouts/index.d.ts +0 -23
- package/src/lib/layouts/index.d.ts.map +0 -1
- package/src/lib/layouts/index.js +0 -45
- package/src/lib/layouts/index.ts +0 -52
- package/src/lib/layouts/registerLayoutBlocks.d.ts +0 -9
- package/src/lib/layouts/registerLayoutBlocks.d.ts.map +0 -1
- package/src/lib/layouts/registerLayoutBlocks.js +0 -60
- package/src/lib/layouts/registerLayoutBlocks.ts +0 -64
- package/src/lib/mappers/apiMapper.d.ts +0 -66
- package/src/lib/mappers/apiMapper.d.ts.map +0 -1
- package/src/lib/mappers/apiMapper.js +0 -191
- package/src/lib/mappers/apiMapper.ts +0 -254
- package/src/lib/migration/index.ts +0 -6
- package/src/lib/migration/mapper.ts +0 -140
- 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/RichTextEditor.js +0 -564
- package/src/lib/rich-text/RichTextEditor.tsx +0 -826
- 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/RichTextPreview.js +0 -144
- package/src/lib/rich-text/RichTextPreview.tsx +0 -210
- package/src/lib/rich-text/index.d.ts +0 -9
- package/src/lib/rich-text/index.d.ts.map +0 -1
- package/src/lib/rich-text/index.js +0 -6
- package/src/lib/rich-text/index.ts +0 -10
- package/src/lib/utils/blockHelpers.d.ts +0 -23
- package/src/lib/utils/blockHelpers.d.ts.map +0 -1
- package/src/lib/utils/blockHelpers.js +0 -65
- package/src/lib/utils/blockHelpers.ts +0 -72
- package/src/lib/utils/configValidation.d.ts +0 -23
- package/src/lib/utils/configValidation.d.ts.map +0 -1
- package/src/lib/utils/configValidation.js +0 -113
- package/src/lib/utils/configValidation.ts +0 -137
- package/src/lib/utils/index.ts +0 -8
- package/src/lib/utils/slugify.ts +0 -79
- package/src/registry/BlockRegistry.d.ts +0 -62
- package/src/registry/BlockRegistry.d.ts.map +0 -1
- package/src/registry/BlockRegistry.js +0 -112
- package/src/registry/BlockRegistry.ts +0 -139
- package/src/registry/index.d.ts +0 -6
- package/src/registry/index.d.ts.map +0 -1
- package/src/registry/index.js +0 -4
- package/src/registry/index.ts +0 -11
- package/src/state/EditorContext.d.ts +0 -45
- package/src/state/EditorContext.d.ts.map +0 -1
- package/src/state/EditorContext.js +0 -215
- package/src/state/EditorContext.tsx +0 -283
- package/src/state/index.d.ts +0 -7
- package/src/state/index.d.ts.map +0 -1
- package/src/state/index.js +0 -6
- package/src/state/index.ts +0 -8
- package/src/state/reducer.d.ts +0 -11
- package/src/state/reducer.d.ts.map +0 -1
- package/src/state/reducer.js +0 -443
- package/src/state/reducer.ts +0 -694
- package/src/state/types.d.ts +0 -162
- package/src/state/types.d.ts.map +0 -1
- package/src/state/types.js +0 -27
- package/src/state/types.ts +0 -160
- package/src/types/block.d.ts +0 -221
- package/src/types/block.d.ts.map +0 -1
- package/src/types/block.js +0 -6
- package/src/types/block.ts +0 -269
- package/src/types/index.d.ts +0 -8
- package/src/types/index.d.ts.map +0 -1
- package/src/types/index.js +0 -5
- package/src/types/index.ts +0 -17
- package/src/types/post.d.ts +0 -136
- package/src/types/post.d.ts.map +0 -1
- package/src/types/post.js +0 -5
- package/src/types/post.ts +0 -169
- package/src/utils/client.d.ts +0 -48
- package/src/utils/client.d.ts.map +0 -1
- package/src/utils/client.js +0 -77
- package/src/utils/client.ts +0 -122
- package/src/utils/index.ts +0 -7
- package/src/views/CanvasEditor/BlockWrapper.d.ts +0 -16
- package/src/views/CanvasEditor/BlockWrapper.d.ts.map +0 -1
- package/src/views/CanvasEditor/BlockWrapper.js +0 -276
- package/src/views/CanvasEditor/BlockWrapper.tsx +0 -522
- package/src/views/CanvasEditor/CanvasEditorView.d.ts +0 -14
- package/src/views/CanvasEditor/CanvasEditorView.d.ts.map +0 -1
- package/src/views/CanvasEditor/CanvasEditorView.js +0 -209
- package/src/views/CanvasEditor/CanvasEditorView.tsx +0 -337
- package/src/views/CanvasEditor/EditorBody.d.ts +0 -22
- package/src/views/CanvasEditor/EditorBody.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorBody.js +0 -505
- package/src/views/CanvasEditor/EditorBody.tsx +0 -665
- package/src/views/CanvasEditor/EditorHeader.d.ts +0 -18
- package/src/views/CanvasEditor/EditorHeader.d.ts.map +0 -1
- package/src/views/CanvasEditor/EditorHeader.js +0 -101
- package/src/views/CanvasEditor/EditorHeader.tsx +0 -268
- package/src/views/CanvasEditor/LayoutContainer.d.ts +0 -17
- package/src/views/CanvasEditor/LayoutContainer.d.ts.map +0 -1
- package/src/views/CanvasEditor/LayoutContainer.js +0 -222
- package/src/views/CanvasEditor/LayoutContainer.tsx +0 -322
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts +0 -13
- package/src/views/CanvasEditor/SaveConfirmationModal.d.ts.map +0 -1
- package/src/views/CanvasEditor/SaveConfirmationModal.js +0 -78
- package/src/views/CanvasEditor/SaveConfirmationModal.tsx +0 -233
- 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/CustomBlockItem.js +0 -44
- package/src/views/CanvasEditor/components/CustomBlockItem.tsx +0 -92
- 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/EditorCanvas.js +0 -32
- package/src/views/CanvasEditor/components/EditorCanvas.tsx +0 -160
- 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/EditorLibrary.js +0 -25
- package/src/views/CanvasEditor/components/EditorLibrary.tsx +0 -122
- 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/EditorSidebar.js +0 -20
- package/src/views/CanvasEditor/components/EditorSidebar.tsx +0 -181
- 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/ErrorBanner.js +0 -8
- package/src/views/CanvasEditor/components/ErrorBanner.tsx +0 -31
- 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/FeaturedMediaSection.js +0 -182
- package/src/views/CanvasEditor/components/FeaturedMediaSection.tsx +0 -341
- 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/LibraryItem.js +0 -43
- package/src/views/CanvasEditor/components/LibraryItem.tsx +0 -80
- 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/PrivacySettingsSection.js +0 -63
- package/src/views/CanvasEditor/components/PrivacySettingsSection.tsx +0 -212
- 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/components/index.js +0 -12
- package/src/views/CanvasEditor/components/index.ts +0 -28
- 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/index.js +0 -9
- package/src/views/CanvasEditor/hooks/index.ts +0 -10
- 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/useHeroBlock.js +0 -79
- package/src/views/CanvasEditor/hooks/useHeroBlock.ts +0 -103
- 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/useKeyboardShortcuts.js +0 -114
- package/src/views/CanvasEditor/hooks/useKeyboardShortcuts.ts +0 -142
- 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/usePostLoader.js +0 -32
- package/src/views/CanvasEditor/hooks/usePostLoader.ts +0 -39
- 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/useRegisteredBlocks.js +0 -47
- package/src/views/CanvasEditor/hooks/useRegisteredBlocks.ts +0 -55
- 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/hooks/useUnsavedChanges.js +0 -285
- package/src/views/CanvasEditor/hooks/useUnsavedChanges.ts +0 -339
- package/src/views/CanvasEditor/index.d.ts +0 -16
- package/src/views/CanvasEditor/index.d.ts.map +0 -1
- package/src/views/CanvasEditor/index.js +0 -9
- package/src/views/CanvasEditor/index.ts +0 -16
- package/src/views/PostManager/EmptyState.d.ts +0 -10
- package/src/views/PostManager/EmptyState.d.ts.map +0 -1
- package/src/views/PostManager/EmptyState.js +0 -12
- package/src/views/PostManager/EmptyState.tsx +0 -42
- package/src/views/PostManager/PostActionsMenu.d.ts +0 -12
- package/src/views/PostManager/PostActionsMenu.d.ts.map +0 -1
- package/src/views/PostManager/PostActionsMenu.js +0 -58
- package/src/views/PostManager/PostActionsMenu.tsx +0 -112
- package/src/views/PostManager/PostCards.d.ts +0 -15
- package/src/views/PostManager/PostCards.d.ts.map +0 -1
- package/src/views/PostManager/PostCards.js +0 -79
- package/src/views/PostManager/PostCards.tsx +0 -197
- package/src/views/PostManager/PostFilters.d.ts +0 -16
- package/src/views/PostManager/PostFilters.d.ts.map +0 -1
- package/src/views/PostManager/PostFilters.js +0 -10
- package/src/views/PostManager/PostFilters.tsx +0 -95
- package/src/views/PostManager/PostManagerView.d.ts +0 -11
- package/src/views/PostManager/PostManagerView.d.ts.map +0 -1
- package/src/views/PostManager/PostManagerView.js +0 -174
- package/src/views/PostManager/PostManagerView.tsx +0 -289
- package/src/views/PostManager/PostStats.d.ts +0 -11
- package/src/views/PostManager/PostStats.d.ts.map +0 -1
- package/src/views/PostManager/PostStats.js +0 -46
- package/src/views/PostManager/PostStats.tsx +0 -81
- package/src/views/PostManager/PostTable.d.ts +0 -15
- package/src/views/PostManager/PostTable.d.ts.map +0 -1
- package/src/views/PostManager/PostTable.js +0 -79
- package/src/views/PostManager/PostTable.tsx +0 -230
- package/src/views/PostManager/index.d.ts +0 -12
- package/src/views/PostManager/index.d.ts.map +0 -1
- package/src/views/PostManager/index.js +0 -11
- package/src/views/PostManager/index.ts +0 -15
- package/src/views/Preview/PreviewBridgeView.d.ts +0 -12
- package/src/views/Preview/PreviewBridgeView.d.ts.map +0 -1
- package/src/views/Preview/PreviewBridgeView.js +0 -11
- package/src/views/Preview/PreviewBridgeView.tsx +0 -64
- package/src/views/Preview/index.d.ts +0 -6
- package/src/views/Preview/index.d.ts.map +0 -1
- package/src/views/Preview/index.js +0 -4
- package/src/views/Preview/index.ts +0 -7
- package/src/views/Settings/SettingsView.d.ts +0 -10
- package/src/views/Settings/SettingsView.d.ts.map +0 -1
- package/src/views/Settings/SettingsView.js +0 -111
- package/src/views/Settings/SettingsView.tsx +0 -298
- package/src/views/Settings/index.d.ts +0 -6
- package/src/views/Settings/index.d.ts.map +0 -1
- package/src/views/Settings/index.js +0 -4
- package/src/views/Settings/index.ts +0 -7
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts +0 -12
- package/src/views/SlugSEO/SlugSEOManagerView.d.ts.map +0 -1
- package/src/views/SlugSEO/SlugSEOManagerView.js +0 -11
- package/src/views/SlugSEO/SlugSEOManagerView.tsx +0 -94
- package/src/views/SlugSEO/index.d.ts +0 -6
- package/src/views/SlugSEO/index.d.ts.map +0 -1
- package/src/views/SlugSEO/index.js +0 -4
- package/src/views/SlugSEO/index.ts +0 -7
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post Manager View
|
|
3
|
-
* Production-ready listing page for managing blog posts
|
|
4
|
-
* Follows dashboard earth-tone design system
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
'use client';
|
|
8
|
-
|
|
9
|
-
import React, { useState, useEffect } from 'react';
|
|
10
|
-
import { Plus, List, Grid3x3 } from 'lucide-react';
|
|
11
|
-
import { PostListItem, PostStatus } from '../../types/post';
|
|
12
|
-
import { PostStats } from './PostStats';
|
|
13
|
-
import { PostFilters } from './PostFilters';
|
|
14
|
-
import { PostTable } from './PostTable';
|
|
15
|
-
import { PostCards } from './PostCards';
|
|
16
|
-
import { EmptyState } from './EmptyState';
|
|
17
|
-
import { apiToBlogPost, type APIBlogDocument } from '../../lib/mappers/apiMapper';
|
|
18
|
-
|
|
19
|
-
export interface PostManagerViewProps {
|
|
20
|
-
siteId: string;
|
|
21
|
-
locale: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
type ViewMode = 'list' | 'cards';
|
|
25
|
-
|
|
26
|
-
const STORAGE_KEY_PREFIX = 'blog-view-mode';
|
|
27
|
-
|
|
28
|
-
export function PostManagerView({ siteId, locale }: PostManagerViewProps) {
|
|
29
|
-
const [posts, setPosts] = useState<PostListItem[]>([]);
|
|
30
|
-
const [totalPosts, setTotalPosts] = useState<number>(0);
|
|
31
|
-
const [loading, setLoading] = useState(true);
|
|
32
|
-
const [search, setSearch] = useState('');
|
|
33
|
-
const [statusFilter, setStatusFilter] = useState<PostStatus | 'all'>('all');
|
|
34
|
-
const [categoryFilter, setCategoryFilter] = useState<string>('all');
|
|
35
|
-
|
|
36
|
-
// Load view mode preference from localStorage
|
|
37
|
-
const getStoredViewMode = (): ViewMode => {
|
|
38
|
-
if (typeof window === 'undefined') return 'list';
|
|
39
|
-
const stored = localStorage.getItem(`${STORAGE_KEY_PREFIX}-${siteId}`);
|
|
40
|
-
return (stored === 'list' || stored === 'cards') ? stored : 'list';
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const [viewMode, setViewMode] = useState<ViewMode>(getStoredViewMode);
|
|
44
|
-
|
|
45
|
-
// Save view mode preference to localStorage when it changes
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (typeof window !== 'undefined') {
|
|
48
|
-
localStorage.setItem(`${STORAGE_KEY_PREFIX}-${siteId}`, viewMode);
|
|
49
|
-
}
|
|
50
|
-
}, [viewMode, siteId]);
|
|
51
|
-
|
|
52
|
-
// Fetch posts from API
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
const fetchPosts = async () => {
|
|
55
|
-
try {
|
|
56
|
-
setLoading(true);
|
|
57
|
-
const response = await fetch('/api/plugin-blog?admin=true');
|
|
58
|
-
const data = await response.json();
|
|
59
|
-
|
|
60
|
-
if (data.blogs && Array.isArray(data.blogs)) {
|
|
61
|
-
// Convert API format to PostListItem format
|
|
62
|
-
const postListItems: PostListItem[] = data.blogs.map((doc: APIBlogDocument) => {
|
|
63
|
-
const blogPost = apiToBlogPost(doc);
|
|
64
|
-
// Use semantic ID (id) - plugin-images handles resolution
|
|
65
|
-
// The id is the semantic ID (e.g., "blog-featured-{slug}") which plugin-images resolves
|
|
66
|
-
const featuredImageId = blogPost.metadata.featuredImage?.id;
|
|
67
|
-
// Extract category from metadata or hero block
|
|
68
|
-
let category: string | undefined = undefined;
|
|
69
|
-
if (blogPost.metadata.categories && blogPost.metadata.categories.length > 0) {
|
|
70
|
-
category = blogPost.metadata.categories[0];
|
|
71
|
-
} else {
|
|
72
|
-
// Check hero block for category
|
|
73
|
-
const heroBlock = blogPost.blocks.find(block => block.type === 'hero');
|
|
74
|
-
if (heroBlock && heroBlock.data && typeof heroBlock.data === 'object') {
|
|
75
|
-
const heroCategory = (heroBlock.data as any).category;
|
|
76
|
-
if (heroCategory && typeof heroCategory === 'string' && heroCategory.trim()) {
|
|
77
|
-
category = heroCategory.trim();
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return {
|
|
82
|
-
id: blogPost.id,
|
|
83
|
-
title: blogPost.title,
|
|
84
|
-
slug: blogPost.slug,
|
|
85
|
-
status: blogPost.publication.status,
|
|
86
|
-
date: blogPost.publication.date,
|
|
87
|
-
excerpt: blogPost.metadata.excerpt,
|
|
88
|
-
featuredImage: featuredImageId,
|
|
89
|
-
authorId: blogPost.publication.authorId,
|
|
90
|
-
updatedAt: blogPost.updatedAt,
|
|
91
|
-
category: category,
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
setPosts(postListItems);
|
|
95
|
-
setTotalPosts(data.total);
|
|
96
|
-
}
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error('Failed to fetch posts:', error);
|
|
99
|
-
} finally {
|
|
100
|
-
setLoading(false);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
fetchPosts();
|
|
105
|
-
}, []);
|
|
106
|
-
|
|
107
|
-
// Extract unique categories from posts
|
|
108
|
-
const categories = React.useMemo(() => {
|
|
109
|
-
const categorySet = new Set<string>();
|
|
110
|
-
posts.forEach(post => {
|
|
111
|
-
if (post.category && post.category.trim()) {
|
|
112
|
-
categorySet.add(post.category.trim());
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
return Array.from(categorySet).sort();
|
|
116
|
-
}, [posts]);
|
|
117
|
-
|
|
118
|
-
// Filter posts
|
|
119
|
-
const filteredPosts = React.useMemo(() => {
|
|
120
|
-
return posts.filter((post) => {
|
|
121
|
-
const matchesSearch =
|
|
122
|
-
search === '' ||
|
|
123
|
-
post.title.toLowerCase().includes(search.toLowerCase()) ||
|
|
124
|
-
post.excerpt?.toLowerCase().includes(search.toLowerCase()) ||
|
|
125
|
-
post.slug.toLowerCase().includes(search.toLowerCase());
|
|
126
|
-
|
|
127
|
-
const matchesStatus = statusFilter === 'all' || post.status === statusFilter;
|
|
128
|
-
|
|
129
|
-
const matchesCategory = categoryFilter === 'all' || post.category === categoryFilter;
|
|
130
|
-
|
|
131
|
-
return matchesSearch && matchesStatus && matchesCategory;
|
|
132
|
-
});
|
|
133
|
-
}, [posts, search, statusFilter, categoryFilter]);
|
|
134
|
-
|
|
135
|
-
// Action handlers
|
|
136
|
-
const handleCreatePost = () => {
|
|
137
|
-
// Navigate to editor route - the plugin router will handle this
|
|
138
|
-
// The route 'new' maps to the editor view
|
|
139
|
-
window.location.href = '/dashboard/blog/new';
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const handleEdit = (postId: string) => {
|
|
143
|
-
// Find the post to get its slug
|
|
144
|
-
const post = posts.find(p => p.id === postId);
|
|
145
|
-
if (post) {
|
|
146
|
-
// Navigate to editor with slug (API uses slug, not ID)
|
|
147
|
-
window.location.href = `/dashboard/blog/editor/${post.slug}`;
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const handlePreview = (postId: string) => {
|
|
152
|
-
// Open preview in new tab
|
|
153
|
-
window.open(`/dashboard/blog/preview/${postId}`, '_blank');
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const handleDuplicate = (postId: string) => {
|
|
157
|
-
// TODO: Implement duplicate functionality
|
|
158
|
-
const post = posts.find((p) => p.id === postId);
|
|
159
|
-
if (post) {
|
|
160
|
-
const duplicated: PostListItem = {
|
|
161
|
-
...post,
|
|
162
|
-
id: `duplicate-${Date.now()}`,
|
|
163
|
-
title: `${post.title} (Copy)`,
|
|
164
|
-
slug: `${post.slug}-copy-${Date.now()}`,
|
|
165
|
-
status: 'draft',
|
|
166
|
-
updatedAt: new Date().toISOString(),
|
|
167
|
-
};
|
|
168
|
-
setPosts((prev) => [...prev, duplicated]);
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const handleDelete = async (postId: string) => {
|
|
173
|
-
if (confirm('Are you sure you want to delete this post?')) {
|
|
174
|
-
try {
|
|
175
|
-
const post = posts.find(p => p.id === postId);
|
|
176
|
-
if (post) {
|
|
177
|
-
const response = await fetch(`/api/plugin-blog/${post.slug}`, {
|
|
178
|
-
method: 'DELETE',
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
if (response.ok) {
|
|
182
|
-
// Remove from local state
|
|
183
|
-
setPosts((prev) => prev.filter((p) => p.id !== postId));
|
|
184
|
-
setTotalPosts(prev => prev - 1);
|
|
185
|
-
} else {
|
|
186
|
-
const error = await response.json();
|
|
187
|
-
alert(error.error || 'Failed to delete post');
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
} catch (error) {
|
|
191
|
-
console.error('Failed to delete post:', error);
|
|
192
|
-
alert('Failed to delete post');
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const hasActiveFilters = search !== '' || statusFilter !== 'all' || categoryFilter !== 'all';
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<div className="h-full w-full rounded-[2.5rem] bg-white dark:bg-neutral-900 p-8 overflow-y-auto">
|
|
201
|
-
{/* Header Section */}
|
|
202
|
-
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8">
|
|
203
|
-
<div>
|
|
204
|
-
<h1 className="text-3xl font-black text-neutral-950 dark:text-white uppercase tracking-tighter mb-2">
|
|
205
|
-
Blog Posts
|
|
206
|
-
</h1>
|
|
207
|
-
<p className="text-sm text-neutral-500 dark:text-neutral-400">
|
|
208
|
-
Manage your blog posts, drafts, and published content
|
|
209
|
-
</p>
|
|
210
|
-
</div>
|
|
211
|
-
|
|
212
|
-
<button
|
|
213
|
-
onClick={handleCreatePost}
|
|
214
|
-
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"
|
|
215
|
-
>
|
|
216
|
-
<Plus size={16} />
|
|
217
|
-
New Post
|
|
218
|
-
</button>
|
|
219
|
-
</div>
|
|
220
|
-
|
|
221
|
-
{/* Stats Summary */}
|
|
222
|
-
<PostStats total={totalPosts} posts={posts} />
|
|
223
|
-
|
|
224
|
-
{/* Filters & Search Bar with View Toggle */}
|
|
225
|
-
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-6">
|
|
226
|
-
<PostFilters
|
|
227
|
-
search={search}
|
|
228
|
-
onSearchChange={setSearch}
|
|
229
|
-
statusFilter={statusFilter}
|
|
230
|
-
onStatusFilterChange={setStatusFilter}
|
|
231
|
-
categoryFilter={categoryFilter}
|
|
232
|
-
onCategoryFilterChange={setCategoryFilter}
|
|
233
|
-
categories={categories}
|
|
234
|
-
/>
|
|
235
|
-
|
|
236
|
-
{/* View Toggle */}
|
|
237
|
-
<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">
|
|
238
|
-
<button
|
|
239
|
-
onClick={() => setViewMode('list')}
|
|
240
|
-
className={`p-2 rounded-full transition-all ${viewMode === 'list'
|
|
241
|
-
? 'bg-white dark:bg-neutral-900 text-primary shadow-sm'
|
|
242
|
-
: 'text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white'
|
|
243
|
-
}`}
|
|
244
|
-
title="List View"
|
|
245
|
-
>
|
|
246
|
-
<List size={18} />
|
|
247
|
-
</button>
|
|
248
|
-
<button
|
|
249
|
-
onClick={() => setViewMode('cards')}
|
|
250
|
-
className={`p-2 rounded-full transition-all ${viewMode === 'cards'
|
|
251
|
-
? 'bg-white dark:bg-neutral-900 text-primary shadow-sm'
|
|
252
|
-
: 'text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white'
|
|
253
|
-
}`}
|
|
254
|
-
title="Card View"
|
|
255
|
-
>
|
|
256
|
-
<Grid3x3 size={18} />
|
|
257
|
-
</button>
|
|
258
|
-
</div>
|
|
259
|
-
</div>
|
|
260
|
-
|
|
261
|
-
{/* Content */}
|
|
262
|
-
{loading ? (
|
|
263
|
-
<div className="flex items-center justify-center py-20">
|
|
264
|
-
<div className="w-8 h-8 border-4 border-primary/20 border-t-primary rounded-full animate-spin" />
|
|
265
|
-
</div>
|
|
266
|
-
) : filteredPosts.length === 0 ? (
|
|
267
|
-
<EmptyState hasFilters={hasActiveFilters} onCreatePost={handleCreatePost} />
|
|
268
|
-
) : viewMode === 'list' ? (
|
|
269
|
-
<PostTable
|
|
270
|
-
posts={filteredPosts}
|
|
271
|
-
locale={locale}
|
|
272
|
-
onEdit={handleEdit}
|
|
273
|
-
onPreview={handlePreview}
|
|
274
|
-
onDuplicate={handleDuplicate}
|
|
275
|
-
onDelete={handleDelete}
|
|
276
|
-
/>
|
|
277
|
-
) : (
|
|
278
|
-
<PostCards
|
|
279
|
-
posts={filteredPosts}
|
|
280
|
-
locale={locale}
|
|
281
|
-
onEdit={handleEdit}
|
|
282
|
-
onPreview={handlePreview}
|
|
283
|
-
onDuplicate={handleDuplicate}
|
|
284
|
-
onDelete={handleDelete}
|
|
285
|
-
/>
|
|
286
|
-
)}
|
|
287
|
-
</div>
|
|
288
|
-
);
|
|
289
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post Stats Component
|
|
3
|
-
* Displays summary statistics for blog posts
|
|
4
|
-
*/
|
|
5
|
-
import { PostListItem } from '../../types/post';
|
|
6
|
-
export interface PostStatsProps {
|
|
7
|
-
posts: PostListItem[];
|
|
8
|
-
total: Number;
|
|
9
|
-
}
|
|
10
|
-
export declare function PostStats({ total, posts }: PostStatsProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
-
//# sourceMappingURL=PostStats.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PostStats.d.ts","sourceRoot":"","sources":["PostStats.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,cAAc,2CA+DzD"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post Stats Component
|
|
3
|
-
* Displays summary statistics for blog posts
|
|
4
|
-
*/
|
|
5
|
-
'use client';
|
|
6
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { FileText, CheckCircle2, Clock, Archive } from 'lucide-react';
|
|
8
|
-
export function PostStats({ total, posts }) {
|
|
9
|
-
const published = posts.filter(p => p.status === 'published').length;
|
|
10
|
-
const drafts = posts.filter(p => p.status === 'draft').length;
|
|
11
|
-
const scheduled = posts.filter(p => p.status === 'scheduled').length;
|
|
12
|
-
const stats = [
|
|
13
|
-
{
|
|
14
|
-
label: 'Total Posts',
|
|
15
|
-
value: total,
|
|
16
|
-
icon: FileText,
|
|
17
|
-
color: 'text-neutral-600 dark:text-neutral-400',
|
|
18
|
-
bgColor: 'bg-neutral-100 dark:bg-neutral-800',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
label: 'Published',
|
|
22
|
-
value: published,
|
|
23
|
-
icon: CheckCircle2,
|
|
24
|
-
color: 'text-green-600 dark:text-green-400',
|
|
25
|
-
bgColor: 'bg-green-500/10 dark:bg-green-500/20',
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
label: 'Drafts',
|
|
29
|
-
value: drafts,
|
|
30
|
-
icon: Clock,
|
|
31
|
-
color: 'text-amber-600 dark:text-amber-400',
|
|
32
|
-
bgColor: 'bg-amber-500/10 dark:bg-amber-500/20',
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
label: 'Scheduled',
|
|
36
|
-
value: scheduled,
|
|
37
|
-
icon: Archive,
|
|
38
|
-
color: 'text-blue-600 dark:text-blue-400',
|
|
39
|
-
bgColor: 'bg-blue-500/10 dark:bg-blue-500/20',
|
|
40
|
-
},
|
|
41
|
-
];
|
|
42
|
-
return (_jsx("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-6", children: stats.map((stat) => {
|
|
43
|
-
const Icon = stat.icon;
|
|
44
|
-
return (_jsx("div", { className: `p-4 rounded-2xl border border-neutral-300 dark:border-neutral-700 ${stat.bgColor}`, children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: `p-2 rounded-xl ${stat.bgColor}`, children: _jsx(Icon, { className: `size-5 ${stat.color}` }) }), _jsxs("div", { children: [_jsx("p", { className: "text-2xl font-black text-neutral-950 dark:text-white", children: stat.value }), _jsx("p", { className: "text-xs text-neutral-500 dark:text-neutral-400 uppercase tracking-wider", children: stat.label })] })] }) }, stat.label));
|
|
45
|
-
}) }));
|
|
46
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post Stats Component
|
|
3
|
-
* Displays summary statistics for blog posts
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use client';
|
|
7
|
-
|
|
8
|
-
import { FileText, CheckCircle2, Clock, Archive } from 'lucide-react';
|
|
9
|
-
import { PostListItem } from '../../types/post';
|
|
10
|
-
import { ReactNode } from 'react';
|
|
11
|
-
|
|
12
|
-
export interface PostStatsProps {
|
|
13
|
-
posts: PostListItem[];
|
|
14
|
-
total: Number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function PostStats({ total, posts }: PostStatsProps) {
|
|
18
|
-
const published = posts.filter(p => p.status === 'published').length;
|
|
19
|
-
const drafts = posts.filter(p => p.status === 'draft').length;
|
|
20
|
-
const scheduled = posts.filter(p => p.status === 'scheduled').length;
|
|
21
|
-
|
|
22
|
-
const stats = [
|
|
23
|
-
{
|
|
24
|
-
label: 'Total Posts',
|
|
25
|
-
value: total,
|
|
26
|
-
icon: FileText,
|
|
27
|
-
color: 'text-neutral-600 dark:text-neutral-400',
|
|
28
|
-
bgColor: 'bg-neutral-100 dark:bg-neutral-800',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
label: 'Published',
|
|
32
|
-
value: published,
|
|
33
|
-
icon: CheckCircle2,
|
|
34
|
-
color: 'text-green-600 dark:text-green-400',
|
|
35
|
-
bgColor: 'bg-green-500/10 dark:bg-green-500/20',
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
label: 'Drafts',
|
|
39
|
-
value: drafts,
|
|
40
|
-
icon: Clock,
|
|
41
|
-
color: 'text-amber-600 dark:text-amber-400',
|
|
42
|
-
bgColor: 'bg-amber-500/10 dark:bg-amber-500/20',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
label: 'Scheduled',
|
|
46
|
-
value: scheduled,
|
|
47
|
-
icon: Archive,
|
|
48
|
-
color: 'text-blue-600 dark:text-blue-400',
|
|
49
|
-
bgColor: 'bg-blue-500/10 dark:bg-blue-500/20',
|
|
50
|
-
},
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
|
55
|
-
{stats.map((stat) => {
|
|
56
|
-
const Icon = stat.icon;
|
|
57
|
-
return (
|
|
58
|
-
<div
|
|
59
|
-
key={stat.label}
|
|
60
|
-
className={`p-4 rounded-2xl border border-neutral-300 dark:border-neutral-700 ${stat.bgColor}`}
|
|
61
|
-
>
|
|
62
|
-
<div className="flex items-center gap-3">
|
|
63
|
-
<div className={`p-2 rounded-xl ${stat.bgColor}`}>
|
|
64
|
-
<Icon className={`size-5 ${stat.color}`} />
|
|
65
|
-
</div>
|
|
66
|
-
<div>
|
|
67
|
-
<p className="text-2xl font-black text-neutral-950 dark:text-white">
|
|
68
|
-
{stat.value as ReactNode}
|
|
69
|
-
</p>
|
|
70
|
-
<p className="text-xs text-neutral-500 dark:text-neutral-400 uppercase tracking-wider">
|
|
71
|
-
{stat.label}
|
|
72
|
-
</p>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
})}
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post Table Component
|
|
3
|
-
* Professional table layout for displaying posts
|
|
4
|
-
*/
|
|
5
|
-
import { PostListItem } from '../../types/post';
|
|
6
|
-
export interface PostTableProps {
|
|
7
|
-
posts: PostListItem[];
|
|
8
|
-
locale: string;
|
|
9
|
-
onEdit: (postId: string) => void;
|
|
10
|
-
onPreview: (postId: string) => void;
|
|
11
|
-
onDuplicate: (postId: string) => void;
|
|
12
|
-
onDelete: (postId: string) => void;
|
|
13
|
-
}
|
|
14
|
-
export declare function PostTable({ posts, locale, onEdit, onPreview, onDuplicate, onDelete, }: PostTableProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
-
//# sourceMappingURL=PostTable.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PostTable.d.ts","sourceRoot":"","sources":["PostTable.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,YAAY,EAAc,MAAM,kBAAkB,CAAC;AAI5D,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;AA0BD,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,MAAM,EACN,MAAM,EACN,SAAS,EACT,WAAW,EACX,QAAQ,GACX,EAAE,cAAc,2CA8KhB"}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Post Table Component
|
|
3
|
-
* Professional table layout for displaying posts
|
|
4
|
-
*/
|
|
5
|
-
'use client';
|
|
6
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { useState, useEffect } from 'react';
|
|
8
|
-
import { Calendar, User, UserCheck } from 'lucide-react';
|
|
9
|
-
import { Image } from '@jhits/plugin-images';
|
|
10
|
-
import { PostActionsMenu } from './PostActionsMenu';
|
|
11
|
-
import { useSession } from 'next-auth/react';
|
|
12
|
-
function getStatusBadgeColor(status) {
|
|
13
|
-
switch (status) {
|
|
14
|
-
case 'published':
|
|
15
|
-
return 'bg-green-500/10 text-green-700 dark:text-green-400 border-green-500/20';
|
|
16
|
-
case 'draft':
|
|
17
|
-
return 'bg-amber-500/10 text-amber-700 dark:text-amber-400 border-amber-500/20';
|
|
18
|
-
case 'scheduled':
|
|
19
|
-
return 'bg-blue-500/10 text-blue-700 dark:text-blue-400 border-blue-500/20';
|
|
20
|
-
case 'archived':
|
|
21
|
-
return 'bg-neutral-500/10 text-neutral-700 dark:text-neutral-400 border-neutral-500/20';
|
|
22
|
-
default:
|
|
23
|
-
return 'bg-neutral-500/10 text-neutral-700 dark:text-neutral-400 border-neutral-500/20';
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function formatDate(dateString, locale) {
|
|
27
|
-
if (!dateString)
|
|
28
|
-
return 'No date';
|
|
29
|
-
return new Date(dateString).toLocaleDateString(locale, {
|
|
30
|
-
day: 'numeric',
|
|
31
|
-
month: 'short',
|
|
32
|
-
year: 'numeric',
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
export function PostTable({ posts, locale, onEdit, onPreview, onDuplicate, onDelete, }) {
|
|
36
|
-
var _a;
|
|
37
|
-
const { data: session, status: sessionStatus } = useSession();
|
|
38
|
-
const currentUserId = (_a = session === null || session === void 0 ? void 0 : session.user) === null || _a === void 0 ? void 0 : _a.id;
|
|
39
|
-
const [userMap, setUserMap] = useState({});
|
|
40
|
-
// Helper function to check if user is the owner
|
|
41
|
-
const isPostOwner = (post) => {
|
|
42
|
-
if (sessionStatus === 'loading')
|
|
43
|
-
return false; // Don't show actions while loading
|
|
44
|
-
if (!currentUserId || !post.authorId)
|
|
45
|
-
return false;
|
|
46
|
-
// Convert both to strings for comparison to handle ObjectId vs string
|
|
47
|
-
return String(currentUserId) === String(post.authorId);
|
|
48
|
-
};
|
|
49
|
-
// Fetch users to map IDs to names
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
const fetchUsers = async () => {
|
|
52
|
-
try {
|
|
53
|
-
const response = await fetch('/api/users');
|
|
54
|
-
const users = await response.json();
|
|
55
|
-
if (Array.isArray(users)) {
|
|
56
|
-
const map = {};
|
|
57
|
-
users.forEach((user) => {
|
|
58
|
-
var _a;
|
|
59
|
-
const id = (_a = user._id) === null || _a === void 0 ? void 0 : _a.toString();
|
|
60
|
-
if (id) {
|
|
61
|
-
map[id] = user.name || user.email || 'Unknown';
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
setUserMap(map);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
console.error('Failed to fetch users:', error);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
fetchUsers();
|
|
72
|
-
}, []);
|
|
73
|
-
const getAuthorName = (authorId) => {
|
|
74
|
-
if (!authorId)
|
|
75
|
-
return 'Unknown';
|
|
76
|
-
return userMap[authorId] || authorId;
|
|
77
|
-
};
|
|
78
|
-
return (_jsx("div", { className: "bg-neutral-100 dark:bg-neutral-800/50 rounded-[2.5rem] border border-neutral-300 dark:border-neutral-700 overflow-hidden", children: _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "bg-neutral-200 dark:bg-neutral-900/50 border-b border-neutral-300 dark:border-neutral-700", children: _jsxs("tr", { children: [_jsx("th", { className: "px-6 py-4 text-left text-[10px] font-black uppercase tracking-widest text-neutral-600 dark:text-neutral-400", children: "Post" }), _jsx("th", { className: "px-6 py-4 text-left text-[10px] font-black uppercase tracking-widest text-neutral-600 dark:text-neutral-400", children: "Author" }), _jsx("th", { className: "px-6 py-4 text-left text-[10px] font-black uppercase tracking-widest text-neutral-600 dark:text-neutral-400", children: "Category" }), _jsx("th", { className: "px-6 py-4 text-left text-[10px] font-black uppercase tracking-widest text-neutral-600 dark:text-neutral-400", children: "Status" }), _jsx("th", { className: "px-6 py-4 text-left text-[10px] font-black uppercase tracking-widest text-neutral-600 dark:text-neutral-400", children: "Last Modified" }), _jsx("th", { className: "px-6 py-4 text-right text-[10px] font-black uppercase tracking-widest text-neutral-600 dark:text-neutral-400", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-neutral-300 dark:divide-neutral-700", children: posts.map((post) => (_jsxs("tr", { className: "hover:bg-white dark:hover:bg-neutral-900/50 transition-colors cursor-pointer", children: [_jsx("td", { className: "px-6 py-4", children: _jsxs("div", { className: "flex items-center gap-4", children: [post.featuredImage ? (_jsx("div", { className: "w-16 h-16 rounded-xl bg-neutral-200 dark:bg-neutral-700 overflow-hidden flex-shrink-0 relative", children: _jsx(Image, { id: post.featuredImage, alt: post.title, fill: true, editable: false, className: "w-full h-full object-cover" }) })) : (_jsx("div", { className: "w-16 h-16 rounded-xl bg-neutral-200 dark:bg-neutral-700 flex items-center justify-center flex-shrink-0", children: _jsx("span", { className: "text-xs text-neutral-400", children: "No Image" }) })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("button", { onClick: () => onEdit(post.id), className: "text-left w-full hover:cursor-pointer p-0 m-0 border-0 bg-transparent", children: _jsx("h3", { className: "font-bold hover:underline text-neutral-950 dark:text-white mb-1 line-clamp-1 text-left", children: post.title.trim() }) }), _jsxs("p", { className: "text-xs text-neutral-500 dark:text-neutral-400 font-mono text-left", children: ["/", post.slug] })] })] }) }), _jsx("td", { className: "px-6 py-4", children: _jsxs("div", { className: "flex items-center gap-2", children: [isPostOwner(post) ? (_jsx(UserCheck, { size: 14, className: "text-primary" })) : (_jsx(User, { size: 14, className: "text-neutral-400" })), _jsxs("span", { className: `text-sm ${isPostOwner(post) ? 'text-primary font-semibold' : 'text-neutral-600 dark:text-neutral-400'}`, children: [getAuthorName(post.authorId), isPostOwner(post) && (_jsx("span", { className: "ml-2 text-xs text-primary/70", children: "(Jij)" }))] })] }) }), _jsx("td", { className: "px-6 py-4", children: _jsx("span", { className: "text-sm text-neutral-600 dark:text-neutral-400", children: post.category || 'Uncategorized' }) }), _jsx("td", { className: "px-6 py-4", children: _jsx("span", { className: `inline-flex items-center px-3 py-1 rounded-full text-[10px] font-black uppercase tracking-wider border ${getStatusBadgeColor(post.status)}`, children: post.status }) }), _jsx("td", { className: "px-6 py-4", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { size: 14, className: "text-neutral-400" }), _jsx("span", { className: "text-sm text-neutral-600 dark:text-neutral-400", children: formatDate(post.updatedAt, locale) })] }) }), _jsx("td", { className: "px-6 py-4", children: isPostOwner(post) ? (_jsx("div", { className: "flex items-center justify-end", children: _jsx(PostActionsMenu, { onEdit: () => onEdit(post.id), onPreview: () => onPreview(post.id), onDuplicate: () => onDuplicate(post.id), onDelete: () => onDelete(post.id) }) })) : (_jsx("div", { className: "flex items-center justify-end text-neutral-400 text-xs", children: "Alleen auteur" })) })] }, post.id))) })] }) }) }));
|
|
79
|
-
}
|