@jhits/plugin-blog 0.0.18 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 -500
  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 -604
  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
@@ -6,7 +6,6 @@
6
6
  'use client';
7
7
 
8
8
  import React from 'react';
9
- import { Plus, Trash2 } from 'lucide-react';
10
9
  import { BlockEditProps, BlockPreviewProps } from '../../../types/block';
11
10
  import { LayoutContainer } from '../../../views/CanvasEditor/LayoutContainer';
12
11
  import { COLUMN_LAYOUTS, ColumnLayout } from '../index';
@@ -31,117 +30,49 @@ export const ColumnsEdit: React.FC<BlockEditProps & {
31
30
  onChildBlockDelete,
32
31
  onChildBlockMove,
33
32
  }) => {
33
+ // Filter out hero block from being rendered inside containers
34
+ const blocks = childBlocks.filter(b => b.type !== 'hero');
35
+
34
36
  // Support both old layout-based system and new dynamic column count
35
37
  const columnCount = block.data.columnCount as number | undefined;
36
38
  const layout: ColumnLayout | undefined = block.data.layout as ColumnLayout | undefined;
37
39
 
38
- // Determine number of columns: use columnCount if set, otherwise derive from layout
39
40
  let numColumns: number;
40
- let gridClass: string;
41
41
  let columnWidths: number[];
42
42
 
43
- // Grid class mapping for Tailwind (must be explicit for dynamic classes)
44
- const gridClassMap: Record<number, string> = {
45
- 1: 'grid-cols-1',
46
- 2: 'grid-cols-2',
47
- 3: 'grid-cols-3',
48
- 4: 'grid-cols-4',
49
- 5: 'grid-cols-5',
50
- 6: 'grid-cols-6',
51
- };
52
-
53
43
  if (columnCount !== undefined && columnCount > 0) {
54
- // Dynamic column system
55
44
  numColumns = columnCount;
56
- // Create equal-width columns
57
45
  const widthPercent = Math.floor(100 / numColumns);
58
46
  columnWidths = Array(numColumns).fill(widthPercent);
59
- // Use explicit grid class from map, fallback to inline style if needed
60
- gridClass = gridClassMap[numColumns] || `grid-cols-${numColumns}`;
61
47
  } else if (layout && COLUMN_LAYOUTS[layout]) {
62
- // Legacy layout-based system
63
48
  const layoutConfig = COLUMN_LAYOUTS[layout];
64
49
  numColumns = layoutConfig.widths.length;
65
- gridClass = layoutConfig.grid;
66
50
  columnWidths = layoutConfig.widths;
67
51
  } else {
68
- // Default to 2 columns
69
52
  numColumns = 2;
70
- gridClass = 'grid-cols-2';
71
53
  columnWidths = [50, 50];
72
54
  }
73
55
 
74
- // Split child blocks into columns based on columnIndex in meta, or round-robin
75
56
  const columns: Block[][] = Array.from({ length: numColumns }, () => []);
76
- childBlocks.forEach((childBlock) => {
57
+ blocks.forEach((childBlock) => {
77
58
  const columnIndex = childBlock.meta?.columnIndex;
78
59
  if (typeof columnIndex === 'number' && columnIndex >= 0 && columnIndex < numColumns) {
79
60
  columns[columnIndex].push(childBlock);
80
61
  } else {
81
- // Fallback to round-robin if no columnIndex specified
82
- const index = childBlocks.indexOf(childBlock);
62
+ const index = blocks.indexOf(childBlock);
83
63
  columns[index % numColumns].push(childBlock);
84
64
  }
85
65
  });
86
66
 
87
- // Get column widths from data or use equal widths
88
67
  const storedWidths = block.data.columnWidths as number[] | undefined;
89
68
  const currentWidths = storedWidths && storedWidths.length === numColumns
90
69
  ? storedWidths
91
- : columnWidths; // Use calculated equal widths if not set
92
-
93
- // Add column handler
94
- const addColumn = () => {
95
- if (numColumns >= 4) return; // Max 4 columns
96
- const newColumnCount = numColumns + 1;
97
- // Calculate new equal widths
98
- const newWidthPercent = Math.floor(100 / newColumnCount);
99
- const newWidths = Array(newColumnCount).fill(newWidthPercent);
100
- onUpdate({
101
- ...block.data,
102
- columnCount: newColumnCount,
103
- columnWidths: newWidths,
104
- // Clear layout if using dynamic columns
105
- layout: undefined,
106
- });
107
- };
108
-
109
- // Delete column handler
110
- const deleteColumn = (colIndex: number) => {
111
- if (numColumns <= 1) return; // Don't allow deleting the last column
112
-
113
- // Move blocks from deleted column to the last column (or previous column if deleting last)
114
- const blocksToMove = columns[colIndex] || [];
115
- const targetColumnIndex = colIndex === numColumns - 1 ? numColumns - 2 : numColumns - 1;
116
- const targetColumn = columns[targetColumnIndex] || [];
117
-
118
- // Move each block to the target column using moveBlock (which will update meta.columnIndex)
119
- blocksToMove.forEach((blockToMove, blockIndex) => {
120
- const newIndex = targetColumn.length + blockIndex;
121
- onChildBlockMove(blockToMove.id, newIndex, `${block.id}-col-${targetColumnIndex}`);
122
- });
123
-
124
- // Update column count and widths after moving blocks
125
- const newColumnCount = numColumns - 1;
126
- // Remove the deleted column's width and redistribute
127
- const newWidths = currentWidths.filter((_, i) => i !== colIndex);
128
- // Normalize to ensure they sum to 100
129
- const total = newWidths.reduce((sum, w) => sum + w, 0);
130
- const normalizedWidths = newWidths.map(w => Math.round((w / total) * 100));
131
-
132
- onUpdate({
133
- ...block.data,
134
- columnCount: newColumnCount,
135
- columnWidths: normalizedWidths,
136
- layout: undefined,
137
- });
138
- };
70
+ : columnWidths;
139
71
 
140
72
  return (
141
- <div className="rounded-xl bg-white relative">
142
- {/* Column Grid */}
73
+ <div className="rounded-xl bg-transparent relative">
143
74
  <div
144
- className="grid gap-8 p-6"
75
+ className="grid gap-2 p-1"
145
76
  style={{
146
77
  gridTemplateColumns: currentWidths.map(w => `${w}%`).join(' '),
147
78
  }}
@@ -149,61 +80,33 @@ export const ColumnsEdit: React.FC<BlockEditProps & {
149
80
  {Array.from({ length: numColumns }).map((_, colIndex) => (
150
81
  <div
151
82
  key={colIndex}
152
- className={`group/col min-h-[200px] rounded-xl border border-dashed transition-all relative ${isSelected
83
+ className={`group/col min-h-[100px] rounded-xl border border-dashed transition-all relative ${isSelected
153
84
  ? 'border-primary/20'
154
- : 'border-gray-200/50'
85
+ : 'border-neutral-200 dark:border-neutral-800'
155
86
  }`}
156
87
  >
157
- <div className="p-4">
88
+ <div className="p-1">
158
89
  <div className="mb-3 flex items-center justify-between">
159
- <span className="text-[10px] font-black uppercase tracking-widest text-gray-400">
90
+ <span className="text-[10px] font-black uppercase tracking-widest text-neutral-400 dark:text-neutral-600">
160
91
  Column {colIndex + 1}
161
92
  </span>
162
- <div className="flex items-center gap-2">
163
- <span className="text-[9px] text-gray-500">
164
- {currentWidths[colIndex]}%
165
- </span>
166
- {/* Delete Column Button - Similar to table, appears on column hover */}
167
- {numColumns > 1 && (
168
- <button
169
- onClick={(e) => {
170
- e.stopPropagation();
171
- deleteColumn(colIndex);
172
- }}
173
- className="opacity-0 group-hover/col:opacity-100 p-1 text-neutral-400 hover:text-red-500 transition-all rounded"
174
- title="Delete Column"
175
- aria-label="Delete Column"
176
- >
177
- <Trash2 size={10} />
178
- </button>
179
- )}
180
- </div>
93
+ </div>
94
+ <div data-layout-container={`${block.id}-col-${colIndex}`}>
95
+ <LayoutContainer
96
+ blocks={columns[colIndex]}
97
+ containerId={`${block.id}-col-${colIndex}`}
98
+ onBlockAdd={onChildBlockAdd}
99
+ onBlockUpdate={onChildBlockUpdate}
100
+ onBlockDelete={onChildBlockDelete}
101
+ onBlockMove={onChildBlockMove}
102
+ emptyLabel="Drop blocks here"
103
+ />
181
104
  </div>
182
105
 
183
- <LayoutContainer
184
- blocks={columns[colIndex] || []}
185
- containerId={`${block.id}-col-${colIndex}`}
186
- onBlockAdd={onChildBlockAdd}
187
- onBlockUpdate={onChildBlockUpdate}
188
- onBlockDelete={onChildBlockDelete}
189
- onBlockMove={onChildBlockMove}
190
- emptyLabel={`Drop blocks in column ${colIndex + 1}`}
191
- />
192
106
  </div>
193
107
  </div>
194
108
  ))}
195
109
  </div>
196
-
197
- {/* Add Column Button - Similar to table, pinned top right */}
198
- {numColumns < 4 && (
199
- <button
200
- onClick={addColumn}
201
- className="absolute top-0 right-0 h-8 w-8 flex items-center justify-center bg-white border-l border-b border-gray-200 text-primary hover:bg-primary hover:text-white transition-all z-10 rounded-br-xl"
202
- title="Add Column"
203
- >
204
- <Plus size={16} />
205
- </button>
206
- )}
207
110
  </div>
208
111
  );
209
112
  };
@@ -214,85 +117,69 @@ export const ColumnsEdit: React.FC<BlockEditProps & {
214
117
  export const ColumnsPreview: React.FC<BlockPreviewProps & {
215
118
  childBlocks?: Block[];
216
119
  renderChild?: (block: Block) => React.ReactNode;
217
- }> = ({ block, childBlocks = [], renderChild, context }) => {
218
- // Support both old layout-based system and new dynamic column count
120
+ }> = ({ block, context, childBlocks = [], renderChild }) => {
121
+ // Filter out hero block from children
122
+ const rawChildren = childBlocks.length > 0
123
+ ? childBlocks
124
+ : (block.children && Array.isArray(block.children) && typeof block.children[0] === 'object'
125
+ ? block.children as Block[]
126
+ : []);
127
+
128
+ const children = rawChildren.filter(b => b.type !== 'hero');
129
+
219
130
  const columnCount = block.data.columnCount as number | undefined;
220
131
  const layout: ColumnLayout | undefined = block.data.layout as ColumnLayout | undefined;
221
132
 
222
- // Determine number of columns: use columnCount if set, otherwise derive from layout
223
133
  let numColumns: number;
224
- let gridClass: string;
225
-
226
- // Grid class mapping for Tailwind (must be explicit for dynamic classes)
227
- const gridClassMap: Record<number, string> = {
228
- 1: 'grid-cols-1',
229
- 2: 'grid-cols-2',
230
- 3: 'grid-cols-3',
231
- 4: 'grid-cols-4',
232
- 5: 'grid-cols-5',
233
- 6: 'grid-cols-6',
234
- };
235
-
236
- // Get column widths
237
- const storedWidths = block.data.columnWidths as number[] | undefined;
134
+ let columnWidths: number[];
238
135
 
239
136
  if (columnCount !== undefined && columnCount > 0) {
240
- // Dynamic column system
241
137
  numColumns = columnCount;
242
- gridClass = gridClassMap[numColumns] || `grid-cols-${numColumns}`;
138
+ const widthPercent = Math.floor(100 / numColumns);
139
+ columnWidths = Array(numColumns).fill(widthPercent);
243
140
  } else if (layout && COLUMN_LAYOUTS[layout]) {
244
- // Legacy layout-based system
245
141
  const layoutConfig = COLUMN_LAYOUTS[layout];
246
142
  numColumns = layoutConfig.widths.length;
247
- gridClass = layoutConfig.grid;
143
+ columnWidths = layoutConfig.widths;
248
144
  } else {
249
- // Default to 2 columns
250
145
  numColumns = 2;
251
- gridClass = 'grid-cols-2';
146
+ columnWidths = [50, 50];
252
147
  }
253
-
254
- // Use stored widths if available, otherwise use equal widths
255
- const columnWidths = storedWidths && storedWidths.length === numColumns
256
- ? storedWidths
257
- : Array(numColumns).fill(Math.floor(100 / numColumns));
258
148
 
259
- // If childBlocks are provided, use them; otherwise get from block.children
260
- const children = childBlocks.length > 0
261
- ? childBlocks
262
- : (block.children && Array.isArray(block.children) && typeof block.children[0] === 'object'
263
- ? block.children as Block[]
264
- : []);
265
-
266
- // Split child blocks into columns based on columnIndex in meta, or round-robin
267
149
  const columns: Block[][] = Array.from({ length: numColumns }, () => []);
268
150
  children.forEach((childBlock) => {
269
151
  const columnIndex = childBlock.meta?.columnIndex;
270
152
  if (typeof columnIndex === 'number' && columnIndex >= 0 && columnIndex < numColumns) {
271
153
  columns[columnIndex].push(childBlock);
272
154
  } else {
273
- // Fallback to round-robin if no columnIndex specified
274
155
  const index = children.indexOf(childBlock);
275
156
  columns[index % numColumns].push(childBlock);
276
157
  }
277
158
  });
278
159
 
160
+ const storedWidths = block.data.columnWidths as number[] | undefined;
161
+ const currentWidths = storedWidths && storedWidths.length === numColumns
162
+ ? storedWidths
163
+ : columnWidths;
164
+
279
165
  return (
280
- <div
281
- className="grid gap-8 my-8"
282
- style={{
283
- gridTemplateColumns: columnWidths.map(w => `${w}%`).join(' '),
284
- }}
285
- >
286
- {Array.from({ length: numColumns }).map((_, colIndex) => (
287
- <div key={colIndex} className="min-h-[100px]">
288
- {columns[colIndex]?.map((childBlock) => (
289
- <React.Fragment key={childBlock.id}>
290
- {renderChild ? renderChild(childBlock) : null}
291
- </React.Fragment>
292
- ))}
293
- </div>
294
- ))}
166
+ <div className="w-full">
167
+ <div
168
+ className="grid gap-8"
169
+ style={{
170
+ gridTemplateColumns: currentWidths.map(w => `${w}%`).join(' '),
171
+ }}
172
+ >
173
+ {columns.map((colBlocks, colIdx) => (
174
+ <div key={colIdx} className="w-full">
175
+ {colBlocks.map((childBlock) => (
176
+ <React.Fragment key={childBlock.id}>
177
+ {renderChild?.(childBlock)}
178
+ </React.Fragment>
179
+ ))}
180
+ </div>
181
+ ))}
182
+ </div>
295
183
  </div>
296
184
  );
297
185
  };
298
-
@@ -8,7 +8,7 @@
8
8
  import React from 'react';
9
9
  import { BlockEditProps, BlockPreviewProps } from '../../../types/block';
10
10
  import { LayoutContainer } from '../../../views/CanvasEditor/LayoutContainer';
11
- import { LAYOUT_CONSTANTS, LAYOUT_BACKGROUNDS } from '../index';
11
+ import { LAYOUT_BACKGROUNDS } from '../index';
12
12
  import { Block } from '../../../types/block';
13
13
 
14
14
  /**
@@ -32,31 +32,26 @@ export const SectionEdit: React.FC<BlockEditProps & {
32
32
  }) => {
33
33
  const background = (block.data.background as keyof typeof LAYOUT_BACKGROUNDS) || 'DEFAULT';
34
34
 
35
+ // Filter out hero block from being rendered inside containers
36
+ const blocks = childBlocks.filter(b => b.type !== 'hero');
37
+
35
38
  return (
36
39
  <div
37
- className={`rounded-xl transition-all ${isSelected
38
- ? 'bg-primary/5'
39
- : ''
40
- } ${LAYOUT_BACKGROUNDS[background]}`}
41
- onDragStart={(e) => {
42
- // Prevent section from being dragged when dragging nested blocks
43
- // Check if the drag started on a nested block wrapper
44
- const nestedBlockWrapper = (e.target as HTMLElement).closest('[data-block-wrapper]');
45
- if (nestedBlockWrapper) {
46
- const nestedBlockId = nestedBlockWrapper.getAttribute('data-block-id');
47
- // If dragging a nested block, prevent the section's drag handler from firing
48
- if (nestedBlockId && nestedBlockId !== block.id) {
49
- e.stopPropagation();
50
- e.preventDefault();
51
- console.log('[SectionBlock] Preventing section drag, nested block is being dragged:', nestedBlockId);
52
- }
53
- }
54
- }}
40
+ className={`rounded-3xl transition-all relative group/section ${
41
+ isSelected ? 'ring-2 ring-primary/20 bg-primary/5' : 'border border-dashed border-neutral-200 dark:border-neutral-800'
42
+ } ${LAYOUT_BACKGROUNDS[background]}`}
55
43
  >
44
+ {/* Section Identifier Tag */}
45
+ <div className="absolute -top-3 left-8 px-2 py-0.5 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-md z-20 pointer-events-none transition-opacity opacity-40 group-hover/section:opacity-100">
46
+ <span className="text-[8px] font-black uppercase tracking-widest text-neutral-400 dark:text-neutral-500">
47
+ Section Container
48
+ </span>
49
+ </div>
50
+
56
51
  {/* Nested Content */}
57
- <div className={`px-8 py-4`}>
52
+ <div className={`px-8 py-6`} data-layout-container={block.id}>
58
53
  <LayoutContainer
59
- blocks={childBlocks}
54
+ blocks={blocks}
60
55
  containerId={block.id}
61
56
  onBlockAdd={onChildBlockAdd}
62
57
  onBlockUpdate={onChildBlockUpdate}
@@ -75,15 +70,17 @@ export const SectionEdit: React.FC<BlockEditProps & {
75
70
  export const SectionPreview: React.FC<BlockPreviewProps & {
76
71
  childBlocks?: Block[];
77
72
  renderChild?: (block: Block) => React.ReactNode;
78
- }> = ({ block, childBlocks = [], renderChild, context }) => {
73
+ }> = ({ block, context, childBlocks = [], renderChild }) => {
79
74
  const background = (block.data.background as keyof typeof LAYOUT_BACKGROUNDS) || 'DEFAULT';
80
75
 
81
- // If childBlocks are provided, use them; otherwise get from block.children
82
- const children = childBlocks.length > 0
76
+ // Filter out hero block from children
77
+ const rawChildren = childBlocks.length > 0
83
78
  ? childBlocks
84
79
  : (block.children && Array.isArray(block.children) && typeof block.children[0] === 'object'
85
80
  ? block.children as Block[]
86
81
  : []);
82
+
83
+ const children = rawChildren.filter(b => b.type !== 'hero');
87
84
 
88
85
  return (
89
86
  <section className={`w-full ${LAYOUT_BACKGROUNDS[background]}`}>
@@ -95,10 +92,9 @@ export const SectionPreview: React.FC<BlockPreviewProps & {
95
92
  </React.Fragment>
96
93
  ))
97
94
  ) : (
98
- <div className="text-gray-400 text-sm italic">Empty section</div>
95
+ children.length > 0 ? null : <div className="text-gray-400 text-sm italic">Empty section</div>
99
96
  )}
100
97
  </div>
101
98
  </section>
102
99
  );
103
100
  };
104
-
@@ -12,12 +12,12 @@ export const LAYOUT_CONSTANTS = {
12
12
  BORDER_RADIUS: '2rem', // 32px - Consistent rounded corners
13
13
  } as const;
14
14
 
15
- // Background Colors (Light mode only - matches client website theme)
15
+ // Background Colors (Theme-aware - integrates with client website theme)
16
16
  export const LAYOUT_BACKGROUNDS = {
17
- DEFAULT: 'bg-white',
18
- NEUTRAL: 'bg-neutral-50',
17
+ DEFAULT: 'bg-transparent',
18
+ NEUTRAL: 'bg-neutral-50 dark:bg-neutral-900/50',
19
19
  SAGE: 'bg-primary/5',
20
- CREAM: 'bg-amber-50/50',
20
+ CREAM: 'bg-amber-50/50 dark:bg-orange-950/10',
21
21
  } as const;
22
22
 
23
23
  // Column Layout Presets
@@ -41,6 +41,11 @@ export interface APIBlogDocument {
41
41
  canonicalUrl?: string;
42
42
  };
43
43
  authorId?: string;
44
+ author?: {
45
+ name: string;
46
+ image?: string;
47
+ displayRole?: string;
48
+ };
44
49
  createdAt?: string | Date;
45
50
  updatedAt?: string | Date;
46
51
  // Multilingual fields
@@ -59,6 +64,11 @@ export interface APIBlogDocument {
59
64
  };
60
65
  availableLanguages?: string[];
61
66
  lang?: string;
67
+ status?: PostStatus;
68
+ publication?: {
69
+ status: PostStatus;
70
+ date?: string | Date;
71
+ };
62
72
  metadata?: {
63
73
  lang?: string;
64
74
  };
@@ -72,7 +82,7 @@ export function apiToBlogPost(doc: APIBlogDocument): BlogPost {
72
82
 
73
83
  // Use contentBlocks if available, otherwise fallback to content (legacy)
74
84
  // Hero block is included in contentBlocks
75
- const blocks = doc.contentBlocks || [];
85
+ const blocks = doc.contentBlocks || doc.content || [];
76
86
 
77
87
  // Convert publication data
78
88
  const publicationDate = doc.publicationData?.date
@@ -106,9 +116,18 @@ export function apiToBlogPost(doc: APIBlogDocument): BlogPost {
106
116
  privacy: undefined, // Privacy settings not in API yet
107
117
  };
108
118
 
109
- // Convert publication data - API uses 'concept' but we use 'draft'
110
- const apiStatus = doc.publicationData?.status || 'concept';
111
- const normalizedStatus = apiStatus === 'concept' ? 'draft' : apiStatus;
119
+ // Convert publication data - strictly isolate per-language status
120
+ // 1. Prefer specific language status from 'publication.status'
121
+ // 2. Fallback to top-level 'status' if provided by specific language endpoint
122
+ // 3. Defaults to 'draft'
123
+ let apiStatus: string = doc.publication?.status || doc.status || 'concept';
124
+
125
+ // If we're missing translation, force draft
126
+ if (apiStatus === 'not-translated' || apiStatus === 'concept') {
127
+ apiStatus = 'draft';
128
+ }
129
+
130
+ const normalizedStatus = apiStatus as PostStatus;
112
131
 
113
132
  const publication = {
114
133
  status: normalizedStatus as PostStatus,
@@ -119,6 +138,12 @@ export function apiToBlogPost(doc: APIBlogDocument): BlogPost {
119
138
  : undefined,
120
139
  };
121
140
 
141
+ const author = doc.author ? {
142
+ name: doc.author.name,
143
+ image: doc.author.image,
144
+ displayRole: doc.author.displayRole,
145
+ } : undefined;
146
+
122
147
  return {
123
148
  id,
124
149
  title: doc.title,
@@ -126,8 +151,10 @@ export function apiToBlogPost(doc: APIBlogDocument): BlogPost {
126
151
  blocks,
127
152
  seo,
128
153
  publication,
154
+ author,
129
155
  metadata,
130
156
  languages: doc.languages,
157
+ availableLanguages: doc.availableLanguages || (doc.languages ? Object.keys(doc.languages) : []),
131
158
  createdAt: doc.createdAt
132
159
  ? (typeof doc.createdAt === 'string' ? doc.createdAt : doc.createdAt.toISOString())
133
160
  : new Date().toISOString(),
@@ -192,11 +219,7 @@ export function editorStateToAPI(state: {
192
219
  // Map status: draft -> concept, published -> published, everything else stays as-is
193
220
  const apiStatus = state.status === 'draft' ? 'concept' : state.status;
194
221
 
195
- console.log('[editorStateToAPI] Mapping status:', {
196
- editorStatus: state.status,
197
- apiStatus: apiStatus,
198
- willBePublished: apiStatus === 'published'
199
- });
222
+
200
223
 
201
224
  // Try to get category from metadata first, then check hero block
202
225
  let category: string | undefined = undefined;
@@ -213,43 +236,51 @@ export function editorStateToAPI(state: {
213
236
  }
214
237
  }
215
238
 
216
- console.log('[editorStateToAPI] Category resolution:', {
217
- fromMetadata: state.metadata.categories?.[0],
218
- fromHeroBlock: (heroBlock || state.blocks.find(b => b.type === 'hero'))?.data ? ((heroBlock || state.blocks.find(b => b.type === 'hero'))!.data as any).category : undefined,
219
- finalCategory: category,
220
- hasHeroBlock: !!heroBlock,
221
- heroBlockImage: heroBlock?.data ? (heroBlock.data as any)?.image : undefined,
222
- });
239
+
223
240
 
224
241
  // Include hero block in contentBlocks if it exists
225
- // Filter out any existing hero blocks from state.blocks first, then add the current hero block
226
242
  const contentBlocksWithoutHero = state.blocks.filter(block => block.type !== 'hero');
227
243
  const allBlocks = heroBlock
228
244
  ? [heroBlock, ...contentBlocksWithoutHero]
229
245
  : contentBlocksWithoutHero;
246
+
247
+ // Try to get featured image from metadata first, then check hero block
248
+ let featuredImage = state.metadata.featuredImage;
249
+ if (!featuredImage?.id?.trim()) {
250
+ const heroBlockToCheck = heroBlock || state.blocks.find(block => block.type === 'hero');
251
+ if (heroBlockToCheck && heroBlockToCheck.data && typeof heroBlockToCheck.data === 'object') {
252
+ const heroImageId = (heroBlockToCheck.data as any).imageId;
253
+ if (heroImageId && typeof heroImageId === 'string' && heroImageId.trim()) {
254
+ featuredImage = {
255
+ id: heroImageId.trim(),
256
+ alt: state.title,
257
+ isCustom: false
258
+ };
259
+ }
260
+ }
261
+ }
230
262
 
231
- console.log('[editorStateToAPI] Hero block details:', {
232
- hasHeroBlock: !!heroBlock,
233
- heroBlockType: heroBlock?.type,
234
- heroBlockId: heroBlock?.id,
235
- heroBlockImage: heroBlock?.data ? (heroBlock.data as any)?.image : undefined,
236
- heroBlockImageSrc: heroBlock?.data ? (heroBlock.data as any)?.image?.src : undefined,
237
- contentBlocksCount: allBlocks.length,
238
- contentBlocksTypes: allBlocks.map(b => b.type),
239
- heroBlockInContentBlocks: allBlocks.find(b => b.type === 'hero')?.data ? (allBlocks.find(b => b.type === 'hero')!.data as any)?.image : undefined,
240
- });
263
+ // Try to get summary from hero block first, then metadata
264
+ let summary = state.metadata.excerpt;
265
+ const heroBlockToCheck = heroBlock || state.blocks.find(block => block.type === 'hero');
266
+ if (heroBlockToCheck && heroBlockToCheck.data && typeof heroBlockToCheck.data === 'object') {
267
+ const heroDesc = (heroBlockToCheck.data as any).description;
268
+ if (heroDesc && typeof heroDesc === 'string' && heroDesc.trim()) {
269
+ summary = heroDesc.trim();
270
+ }
271
+ }
241
272
 
242
273
  return {
243
274
  title: state.title,
244
275
  slug: state.slug,
245
276
  contentBlocks: allBlocks,
246
- summary: state.metadata.excerpt,
277
+ summary: summary,
247
278
  // Only save semantic ID (id) and alt - plugin-images handles transform data
248
279
  // Only create image object if id exists and is not empty
249
- image: state.metadata.featuredImage?.id?.trim() ? {
250
- id: state.metadata.featuredImage.id.trim(),
251
- alt: state.metadata.featuredImage.alt || '',
252
- isCustom: state.metadata.featuredImage.isCustom,
280
+ image: featuredImage?.id?.trim() ? {
281
+ id: featuredImage.id.trim(),
282
+ alt: featuredImage.alt || '',
283
+ isCustom: featuredImage.isCustom,
253
284
  // Don't save transform fields - plugin-images API handles those
254
285
  } : undefined,
255
286
  categoryTags: {