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