@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,283 +1,204 @@
1
1
  /**
2
2
  * Editor Context
3
- * React Context for managing editor state
4
- * Multi-Tenant: Accepts custom blocks from client applications
3
+ * Enhanced with language management and simplified state handling
5
4
  */
6
5
 
7
6
  'use client';
8
7
 
9
8
  import React, { createContext, useContext, useReducer, useCallback, useMemo, useEffect, useRef, useState } from 'react';
10
9
  import { editorReducer } from './reducer';
11
- import { EditorContextValue, EditorState, EditorAction, initialEditorState } from './types';
10
+ import { EditorContextValue, EditorState, initialEditorState } from './types';
12
11
  import { Block } from '../types/block';
13
- import { BlogPost } from '../types/post';
12
+ import { BlogPost, PostStatus } from '../types/post';
13
+ import { apiToBlogPost } from '../lib/mappers/apiMapper';
14
14
  import { blockRegistry } from '../registry/BlockRegistry';
15
15
  import { ClientBlockDefinition } from '../types/block';
16
16
  import { registerLayoutBlocks } from '../lib/layouts/registerLayoutBlocks';
17
17
 
18
- // Create the context
18
+ const API_BASE_URL = process.env.NEXT_PUBLIC_DASHBOARD_URL || 'http://localhost:3001';
19
19
  const EditorContext = createContext<EditorContextValue | null>(null);
20
20
 
21
- /**
22
- * Editor Provider Props
23
- */
24
21
  export interface EditorProviderProps {
25
22
  children: React.ReactNode;
26
- /** Initial state (optional) */
27
23
  initialState?: Partial<EditorState>;
28
- /** Callback when save is triggered */
29
24
  onSave?: (state: EditorState, heroBlock?: Block | null) => Promise<void>;
30
- /**
31
- * Custom blocks from client application
32
- * These blocks will be registered in the BlockRegistry on mount
33
- */
34
25
  customBlocks?: ClientBlockDefinition[];
35
- /** Enable dark mode for content area and wrappers (default: true) */
36
26
  darkMode?: boolean;
37
- /** Background colors for the editor */
38
- backgroundColors?: {
39
- /** Background color for light mode (REQUIRED) */
40
- light: string;
41
- /** Background color for dark mode (optional) */
42
- dark?: string;
43
- };
27
+ backgroundColors?: { light: string; dark?: string };
28
+ translations?: Record<string, any>;
44
29
  }
45
30
 
46
- /**
47
- * Editor Provider
48
- * Provides editor state and actions to child components
49
- * Automatically registers client-provided blocks on mount
50
- */
51
31
  export function EditorProvider({
52
32
  children,
53
- initialState,
54
- onSave,
55
- customBlocks = [],
56
- darkMode = true,
57
- backgroundColors
33
+ initialState,
34
+ onSave,
35
+ customBlocks = [],
36
+ darkMode = false,
37
+ backgroundColors,
38
+ translations
58
39
  }: EditorProviderProps) {
59
- // Register core layout blocks on mount
60
- useEffect(() => {
61
- registerLayoutBlocks();
62
- }, []);
63
-
64
- // Register client blocks on mount
65
- useEffect(() => {
66
- if (customBlocks && customBlocks.length > 0) {
67
- try {
68
- blockRegistry.registerClientBlocks(customBlocks);
69
- } catch (error) {
70
- console.error('[EditorContext] Failed to register custom blocks:', error);
71
- }
72
- }
73
- }, [customBlocks]);
74
-
75
- const [state, dispatch] = useReducer(
76
- editorReducer,
77
- { ...initialEditorState, ...initialState }
78
- );
40
+ useEffect(() => { registerLayoutBlocks(); }, []);
41
+ useEffect(() => { if (customBlocks?.length) blockRegistry.registerClientBlocks(customBlocks); }, [customBlocks]);
79
42
 
80
- // Use a ref to always have access to the latest state in callbacks
43
+ const [state, dispatch] = useReducer(editorReducer, { ...initialEditorState, ...initialState });
44
+ const [user, setUser] = useState<any>(null);
81
45
  const stateRef = useRef(state);
82
46
  stateRef.current = state;
83
47
 
84
- // History state for undo/redo
48
+ // Language Management
49
+ const [availableLanguages, setAvailableLanguages] = useState<string[]>([]);
50
+
51
+ useEffect(() => {
52
+ const fetchUser = async () => {
53
+ try {
54
+ const res = await fetch(`${API_BASE_URL}/api/me`);
55
+ if (res.ok) {
56
+ const data = await res.json();
57
+ if (data.user) setUser(data.user);
58
+ }
59
+ } catch (e) {}
60
+ };
61
+ fetchUser();
62
+ }, []);
63
+
64
+ // History logic (Simplified)
85
65
  const [history, setHistory] = useState<EditorState[]>([]);
86
66
  const [historyIndex, setHistoryIndex] = useState(-1);
87
67
  const isRestoringRef = useRef(false);
88
- const MAX_HISTORY = 50; // Limit history to prevent memory issues
89
68
 
90
- // Save current state to history after state changes (but not during undo/redo)
91
- // Debounce history updates to avoid excessive re-renders
92
- const historyTimeoutRef = useRef<NodeJS.Timeout | null>(null);
93
-
94
69
  useEffect(() => {
95
- if (isRestoringRef.current) {
96
- isRestoringRef.current = false;
97
- return;
98
- }
99
-
100
- // Clear existing timeout
101
- if (historyTimeoutRef.current) {
102
- clearTimeout(historyTimeoutRef.current);
103
- }
104
-
105
- // Debounce history updates to reduce re-renders
106
- historyTimeoutRef.current = setTimeout(() => {
107
- // Save current state to history
70
+ if (isRestoringRef.current) { isRestoringRef.current = false; return; }
71
+ const timer = setTimeout(() => {
108
72
  setHistory(prev => {
109
- const newHistory = [...prev];
110
- // Remove any future history if we're not at the end
111
- if (historyIndex < newHistory.length - 1) {
112
- newHistory.splice(historyIndex + 1);
113
- }
114
- // Add current state
115
- newHistory.push({ ...state });
116
- // Limit history size
117
- if (newHistory.length > MAX_HISTORY) {
118
- newHistory.shift();
119
- return newHistory;
120
- }
121
- return newHistory;
122
- });
123
- setHistoryIndex(prev => {
124
- const newIndex = prev + 1;
125
- return newIndex >= MAX_HISTORY ? MAX_HISTORY - 1 : newIndex;
73
+ const next = prev.slice(0, historyIndex + 1);
74
+ next.push({ ...state });
75
+ return next.slice(-50);
126
76
  });
127
- }, 300); // Debounce by 300ms
128
-
129
- return () => {
130
- if (historyTimeoutRef.current) {
131
- clearTimeout(historyTimeoutRef.current);
77
+ setHistoryIndex(prev => Math.min(prev + 1, 49));
78
+ }, 300);
79
+ return () => clearTimeout(timer);
80
+ }, [state.blocks, state.title, state.slug]);
81
+
82
+ const helpers = useMemo(() => ({
83
+ addBlock: (type: string, index?: number, containerId?: string) => {
84
+ const def = blockRegistry.get(type);
85
+ const block: Block = {
86
+ id: `block-${Date.now()}`,
87
+ type,
88
+ data: { ...(def?.defaultData || {}) }
89
+ };
90
+ dispatch({ type: 'ADD_BLOCK', payload: { block, index, containerId } });
91
+ },
92
+ updateBlock: (id: string, data: any) =>
93
+ dispatch({ type: 'UPDATE_BLOCK', payload: { id, data } }),
94
+ deleteBlock: (id: string) =>
95
+ dispatch({ type: 'DELETE_BLOCK', payload: { id } }),
96
+ duplicateBlock: (id: string) =>
97
+ dispatch({ type: 'DUPLICATE_BLOCK', payload: { id } }),
98
+ moveBlock: (id: string, newIndex: number, containerId?: string) =>
99
+ dispatch({ type: 'MOVE_BLOCK', payload: { id, newIndex, containerId } }),
100
+ loadPost: (post: BlogPost) => {
101
+
102
+ dispatch({ type: 'LOAD_POST', payload: post });
103
+
104
+ // 1. Collect all unique language keys from the post data
105
+ const createdLangs = new Set<string>();
106
+
107
+ // From availableLanguages array (source of truth from API)
108
+ if (post.availableLanguages) {
109
+ post.availableLanguages.forEach(l => createdLangs.add(l));
132
110
  }
133
- };
134
- }, [state.blocks, state.title, state.slug, state.seo, state.metadata, state.status, historyIndex]);
135
-
136
- // Helper: Add a new block (supports nested containers)
137
- const addBlock = useCallback((type: string, index?: number, containerId?: string) => {
138
- const blockDefinition = blockRegistry.get(type);
139
- if (!blockDefinition) {
140
- console.warn(`Block type "${type}" not found in registry. Available types:`,
141
- blockRegistry.getAll().map(b => b.type).join(', '));
142
- return;
143
- }
144
-
145
- const newBlock: Block = {
146
- id: `block-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
147
- type,
148
- data: { ...blockDefinition.defaultData },
149
- };
150
-
151
- dispatch({ type: 'ADD_BLOCK', payload: { block: newBlock, index, containerId } });
152
- }, []);
153
-
154
- // Helper: Update a block
155
- const updateBlock = useCallback((id: string, data: Partial<Block['data']>) => {
156
- dispatch({ type: 'UPDATE_BLOCK', payload: { id, data } });
157
- }, []);
158
-
159
- // Helper: Delete a block
160
- const deleteBlock = useCallback((id: string) => {
161
- dispatch({ type: 'DELETE_BLOCK', payload: { id } });
162
- }, []);
163
-
164
- // Helper: Duplicate a block
165
- const duplicateBlock = useCallback((id: string) => {
166
- dispatch({ type: 'DUPLICATE_BLOCK', payload: { id } });
167
- }, []);
168
-
169
- // Helper: Move a block (supports nested containers)
170
- const moveBlock = useCallback((id: string, newIndex: number, containerId?: string) => {
171
- dispatch({ type: 'MOVE_BLOCK', payload: { id, newIndex, containerId } });
172
- }, []);
173
-
174
- // Helper: Load a post
175
- const loadPost = useCallback((post: BlogPost) => {
176
- dispatch({ type: 'LOAD_POST', payload: post });
177
- }, []);
178
-
179
- // Helper: Reset editor
180
- const resetEditor = useCallback(() => {
181
- dispatch({ type: 'RESET_EDITOR' });
182
- }, []);
183
-
184
- // Helper: Save
185
- // Uses stateRef to always get the latest state, avoiding stale closure issues
186
- const save = useCallback(async (heroBlock?: Block | null) => {
187
- if (onSave) {
188
- // Use stateRef.current to get the absolute latest state
189
- // This ensures we don't have stale closure issues with React state updates
190
- await onSave(stateRef.current, heroBlock);
191
- dispatch({ type: 'MARK_CLEAN' });
192
- }
193
- }, [onSave]);
194
-
195
- // Helper: Undo
196
- const undo = useCallback(() => {
197
- if (historyIndex > 0 && history.length > 0) {
198
- const previousState = history[historyIndex - 1];
199
- if (previousState) {
111
+
112
+ // From languages summary keys (backup)
113
+ if (post.languages) {
114
+ Object.keys(post.languages).forEach(l => createdLangs.add(l));
115
+ }
116
+
117
+ // 2. Update state with the consolidated list
118
+ setAvailableLanguages(Array.from(createdLangs));
119
+ },
120
+ resetEditor: () => dispatch({ type: 'RESET_EDITOR' }),
121
+ save: async (heroBlock?: Block | null, statusOverride?: PostStatus) => {
122
+ if (onSave) {
123
+ const currentState = { ...stateRef.current };
124
+ if (statusOverride) currentState.status = statusOverride;
125
+
126
+ await onSave(currentState, heroBlock);
127
+ dispatch({ type: 'MARK_CLEAN' });
128
+ }
129
+ },
130
+ undo: () => {
131
+ if (historyIndex > 0) {
200
132
  isRestoringRef.current = true;
201
- setHistoryIndex(prev => prev - 1);
202
- dispatch({ type: 'LOAD_POST', payload: {
203
- id: previousState.postId || '',
204
- title: previousState.title,
205
- slug: previousState.slug,
206
- blocks: previousState.blocks,
207
- seo: previousState.seo,
208
- publication: {
209
- status: previousState.status,
210
- authorId: undefined,
211
- },
212
- metadata: previousState.metadata,
213
- createdAt: new Date().toISOString(),
214
- updatedAt: new Date().toISOString(),
215
- } });
133
+ const prev = history[historyIndex - 1];
134
+ setHistoryIndex(i => i - 1);
135
+ dispatch({ type: 'LOAD_POST', payload: prev as any });
216
136
  }
217
- }
218
- }, [history, historyIndex, dispatch]);
219
-
220
- // Helper: Redo
221
- const redo = useCallback(() => {
222
- if (historyIndex < history.length - 1) {
223
- const nextState = history[historyIndex + 1];
224
- if (nextState) {
137
+ },
138
+ redo: () => {
139
+ if (historyIndex < history.length - 1) {
225
140
  isRestoringRef.current = true;
226
- setHistoryIndex(prev => prev + 1);
227
- dispatch({ type: 'LOAD_POST', payload: {
228
- id: nextState.postId || '',
229
- title: nextState.title,
230
- slug: nextState.slug,
231
- blocks: nextState.blocks,
232
- seo: nextState.seo,
233
- publication: {
234
- status: nextState.status,
235
- authorId: undefined,
236
- },
237
- metadata: nextState.metadata,
238
- createdAt: new Date().toISOString(),
239
- updatedAt: new Date().toISOString(),
240
- } });
141
+ const next = history[historyIndex + 1];
142
+ setHistoryIndex(i => i + 1);
143
+ dispatch({ type: 'LOAD_POST', payload: next as any });
241
144
  }
242
- }
243
- }, [history, historyIndex, dispatch]);
244
-
245
- // Memoize the context value
246
- const value = useMemo<EditorContextValue>(
247
- () => ({
248
- state,
249
- dispatch,
250
- darkMode,
251
- backgroundColors,
252
- helpers: {
253
- addBlock,
254
- updateBlock,
255
- deleteBlock,
256
- duplicateBlock,
257
- moveBlock,
258
- loadPost,
259
- resetEditor,
260
- save,
261
- undo,
262
- redo,
263
145
  },
264
- canUndo: historyIndex > 0 && history.length > 0,
146
+ switchLanguage: async (newLang: string, postId?: string) => {
147
+ // 1. Always update the current language in state first to show immediate UI feedback
148
+ dispatch({ type: 'SET_CURRENT_LANGUAGE', payload: newLang });
149
+
150
+ if (!postId) {
151
+ dispatch({ type: 'SET_STATUS', payload: 'draft' });
152
+ return;
153
+ }
154
+
155
+ // 2. Fetch the language version from the API
156
+ const fetchUrl = `${API_BASE_URL}/api/plugin-blog/${postId}?language=${newLang}&admin=true`;
157
+
158
+ const res = await fetch(fetchUrl);
159
+ if (res.ok) {
160
+ const data = await res.json();
161
+ const blogPost = apiToBlogPost(data);
162
+
163
+ // Update the available languages list from the source of truth
164
+ if (data.availableLanguages) {
165
+ setAvailableLanguages(data.availableLanguages);
166
+ }
167
+
168
+ // If this is a NEW translation (missing in the document),
169
+ // customize it so it's not a perfect clone of the primary language
170
+ if (data.isMissingTranslation) {
171
+ blogPost.publication.status = 'draft';
172
+ blogPost.title = `${blogPost.title} - ${newLang.toUpperCase()}`;
173
+ // Important: Mark as dirty so it can be saved as a new version
174
+ dispatch({ type: 'LOAD_POST', payload: blogPost });
175
+ dispatch({ type: 'MARK_DIRTY' });
176
+ } else {
177
+ dispatch({ type: 'LOAD_POST', payload: blogPost });
178
+ dispatch({ type: 'MARK_CLEAN' });
179
+ }
180
+ }
181
+ }
182
+ }), [onSave, history, historyIndex, state.postId]);
183
+
184
+ const value = useMemo(() => ({
185
+ state,
186
+ dispatch,
187
+ darkMode,
188
+ backgroundColors,
189
+ translations,
190
+ user,
191
+ helpers,
192
+ availableLanguages,
193
+ canUndo: historyIndex > 0,
265
194
  canRedo: historyIndex < history.length - 1,
266
- }),
267
- [state, dispatch, darkMode, backgroundColors, addBlock, updateBlock, deleteBlock, duplicateBlock, moveBlock, loadPost, resetEditor, save, undo, redo, historyIndex, history.length]
268
- );
195
+ }), [state, darkMode, backgroundColors, user, helpers, historyIndex, history.length, availableLanguages]);
269
196
 
270
197
  return <EditorContext.Provider value={value}>{children}</EditorContext.Provider>;
271
198
  }
272
199
 
273
- /**
274
- * Hook to access editor context
275
- * @throws Error if used outside EditorProvider
276
- */
277
- export function useEditor(): EditorContextValue {
200
+ export function useEditor() {
278
201
  const context = useContext(EditorContext);
279
- if (!context) {
280
- throw new Error('useEditor must be used within an EditorProvider');
281
- }
202
+ if (!context) throw new Error('useEditor must be used within EditorProvider');
282
203
  return context;
283
204
  }