@jhits/plugin-blog 0.0.17 → 0.0.18

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 (66) hide show
  1. package/dist/api/categories.d.ts.map +1 -1
  2. package/dist/api/categories.js +43 -12
  3. package/dist/api/handler.d.ts +1 -0
  4. package/dist/api/handler.d.ts.map +1 -1
  5. package/dist/api/handler.js +259 -32
  6. package/dist/hooks/useBlogs.d.ts +2 -0
  7. package/dist/hooks/useBlogs.d.ts.map +1 -1
  8. package/dist/hooks/useBlogs.js +10 -2
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +5 -3
  12. package/dist/lib/i18n.d.ts +14 -0
  13. package/dist/lib/i18n.d.ts.map +1 -0
  14. package/dist/lib/i18n.js +58 -0
  15. package/dist/lib/mappers/apiMapper.d.ts +18 -0
  16. package/dist/lib/mappers/apiMapper.d.ts.map +1 -1
  17. package/dist/lib/mappers/apiMapper.js +1 -0
  18. package/dist/state/reducer.d.ts.map +1 -1
  19. package/dist/state/reducer.js +11 -6
  20. package/dist/state/types.d.ts +5 -0
  21. package/dist/state/types.d.ts.map +1 -1
  22. package/dist/state/types.js +1 -0
  23. package/dist/types/post.d.ts +25 -0
  24. package/dist/types/post.d.ts.map +1 -1
  25. package/dist/utils/client.d.ts +2 -0
  26. package/dist/utils/client.d.ts.map +1 -1
  27. package/dist/utils/client.js +3 -1
  28. package/dist/views/CanvasEditor/CanvasEditorView.d.ts.map +1 -1
  29. package/dist/views/CanvasEditor/CanvasEditorView.js +130 -4
  30. package/dist/views/CanvasEditor/EditorHeader.d.ts +5 -1
  31. package/dist/views/CanvasEditor/EditorHeader.d.ts.map +1 -1
  32. package/dist/views/CanvasEditor/EditorHeader.js +23 -5
  33. package/dist/views/CanvasEditor/hooks/usePostLoader.d.ts +1 -1
  34. package/dist/views/CanvasEditor/hooks/usePostLoader.d.ts.map +1 -1
  35. package/dist/views/CanvasEditor/hooks/usePostLoader.js +14 -4
  36. package/dist/views/PostManager/LanguageFlags.d.ts +11 -0
  37. package/dist/views/PostManager/LanguageFlags.d.ts.map +1 -0
  38. package/dist/views/PostManager/LanguageFlags.js +60 -0
  39. package/dist/views/PostManager/PostCards.d.ts.map +1 -1
  40. package/dist/views/PostManager/PostCards.js +4 -1
  41. package/dist/views/PostManager/PostFilters.d.ts +4 -1
  42. package/dist/views/PostManager/PostFilters.d.ts.map +1 -1
  43. package/dist/views/PostManager/PostFilters.js +13 -3
  44. package/dist/views/PostManager/PostManagerView.d.ts.map +1 -1
  45. package/dist/views/PostManager/PostManagerView.js +24 -3
  46. package/dist/views/PostManager/PostTable.d.ts.map +1 -1
  47. package/dist/views/PostManager/PostTable.js +4 -1
  48. package/package.json +3 -3
  49. package/src/api/categories.ts +58 -11
  50. package/src/api/handler.ts +286 -31
  51. package/src/hooks/useBlogs.ts +12 -1
  52. package/src/index.tsx +7 -3
  53. package/src/lib/i18n.ts +78 -0
  54. package/src/lib/mappers/apiMapper.ts +20 -0
  55. package/src/state/reducer.ts +12 -6
  56. package/src/state/types.ts +5 -0
  57. package/src/types/post.ts +28 -0
  58. package/src/utils/client.ts +4 -0
  59. package/src/views/CanvasEditor/CanvasEditorView.tsx +164 -20
  60. package/src/views/CanvasEditor/EditorHeader.tsx +93 -18
  61. package/src/views/CanvasEditor/hooks/usePostLoader.ts +15 -4
  62. package/src/views/PostManager/LanguageFlags.tsx +136 -0
  63. package/src/views/PostManager/PostCards.tsx +22 -12
  64. package/src/views/PostManager/PostFilters.tsx +38 -1
  65. package/src/views/PostManager/PostManagerView.tsx +25 -2
  66. package/src/views/PostManager/PostTable.tsx +12 -1
@@ -39,6 +39,24 @@ export interface APIBlogDocument {
39
39
  authorId?: string;
40
40
  createdAt?: string | Date;
41
41
  updatedAt?: string | Date;
42
+ languages?: {
43
+ [key: string]: {
44
+ blocks: Block[];
45
+ metadata: {
46
+ title?: string;
47
+ excerpt?: string;
48
+ featuredImage?: any;
49
+ categories?: string[];
50
+ tags?: string[];
51
+ seo?: any;
52
+ };
53
+ };
54
+ };
55
+ availableLanguages?: string[];
56
+ lang?: string;
57
+ metadata?: {
58
+ lang?: string;
59
+ };
42
60
  }
43
61
  /**
44
62
  * Convert API document to BlogPost format
@@ -1 +1 @@
1
- {"version":3,"file":"apiMapper.d.ts","sourceRoot":"","sources":["../../../src/lib/mappers/apiMapper.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE;QACJ,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,OAAO,CAAC;KAGtB,CAAC;IACF,YAAY,CAAC,EAAE;QACX,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,eAAe,CAAC,EAAE;QACd,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;QAChC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACxB,CAAC;IACF,GAAG,CAAC,EAAE;QACF,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,QAAQ,CAmE5D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAgCzF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,GAAG,EAAE,WAAW,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,eAAe,CAAC,CAkFxE"}
1
+ {"version":3,"file":"apiMapper.d.ts","sourceRoot":"","sources":["../../../src/lib/mappers/apiMapper.ts"],"names":[],"mappings":"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"}
@@ -58,6 +58,7 @@ export function apiToBlogPost(doc) {
58
58
  seo,
59
59
  publication,
60
60
  metadata,
61
+ languages: doc.languages,
61
62
  createdAt: doc.createdAt
62
63
  ? (typeof doc.createdAt === 'string' ? doc.createdAt : doc.createdAt.toISOString())
63
64
  : new Date().toISOString(),
@@ -1 +1 @@
1
- {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/state/reducer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAsB,MAAM,SAAS,CAAC;AAqUxE;;;GAGG;AACH,wBAAgB,aAAa,CACzB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,GACrB,WAAW,CAmWb"}
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/state/reducer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAsB,MAAM,SAAS,CAAC;AAqUxE;;;GAGG;AACH,wBAAgB,aAAa,CACzB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,GACrB,WAAW,CAyWb"}
@@ -563,12 +563,12 @@ export function editorReducer(state, action) {
563
563
  const post = action.payload;
564
564
  return {
565
565
  ...state,
566
- blocks: post.blocks,
567
- title: post.title,
568
- slug: post.slug,
569
- seo: post.seo,
570
- metadata: post.metadata,
571
- status: post.publication.status,
566
+ blocks: post.blocks || [],
567
+ title: post.title || '',
568
+ slug: post.slug || '',
569
+ seo: post.seo || {},
570
+ metadata: post.metadata || {},
571
+ status: post.publication?.status || 'draft',
572
572
  postId: post.id,
573
573
  isDirty: false,
574
574
  selectedBlockId: null,
@@ -588,6 +588,11 @@ export function editorReducer(state, action) {
588
588
  ...state,
589
589
  isDirty: true,
590
590
  };
591
+ case 'SET_CURRENT_LANGUAGE':
592
+ return {
593
+ ...state,
594
+ currentLanguage: action.payload,
595
+ };
591
596
  case 'UNDO':
592
597
  case 'REDO':
593
598
  case 'SAVE_HISTORY':
@@ -31,6 +31,8 @@ export interface EditorState {
31
31
  draggedBlockId: string | null;
32
32
  /** Post ID (if editing existing post) */
33
33
  postId: string | null;
34
+ /** Currently selected language for editing */
35
+ currentLanguage: string | null;
34
36
  }
35
37
  /**
36
38
  * Editor Actions
@@ -102,6 +104,9 @@ export type EditorAction = {
102
104
  type: 'MARK_CLEAN';
103
105
  } | {
104
106
  type: 'MARK_DIRTY';
107
+ } | {
108
+ type: 'SET_CURRENT_LANGUAGE';
109
+ payload: string;
105
110
  } | {
106
111
  type: 'UNDO';
107
112
  } | {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/state/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/state/types.ts"],"names":[],"mappings":"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"}
@@ -24,4 +24,5 @@ export const initialEditorState = {
24
24
  selectedBlockId: null,
25
25
  draggedBlockId: null,
26
26
  postId: null,
27
+ currentLanguage: null,
27
28
  };
@@ -52,6 +52,8 @@ export interface PrivacySettings {
52
52
  * Post Metadata
53
53
  */
54
54
  export interface PostMetadata {
55
+ /** Language-specific title */
56
+ title?: string;
55
57
  /** Featured image */
56
58
  featuredImage?: {
57
59
  id?: string;
@@ -94,6 +96,8 @@ export interface BlogPost {
94
96
  publication: PublicationData;
95
97
  /** Additional metadata */
96
98
  metadata: PostMetadata;
99
+ /** Content per language (for multi-language posts) */
100
+ languages?: BlogPostLanguages;
97
101
  /** Creation timestamp */
98
102
  createdAt: string;
99
103
  /** Last update timestamp */
@@ -116,6 +120,9 @@ export interface PostListItem {
116
120
  authorId?: string;
117
121
  updatedAt: string;
118
122
  category?: string;
123
+ lang?: string;
124
+ availableLanguages?: string[];
125
+ languages?: BlogPostLanguages;
119
126
  }
120
127
  /**
121
128
  * Post Filter Options
@@ -126,6 +133,7 @@ export interface PostFilterOptions {
126
133
  tag?: string;
127
134
  authorId?: string;
128
135
  search?: string;
136
+ language?: string;
129
137
  dateFrom?: string;
130
138
  dateTo?: string;
131
139
  limit?: number;
@@ -133,4 +141,21 @@ export interface PostFilterOptions {
133
141
  sortBy?: 'date' | 'title' | 'updatedAt';
134
142
  sortOrder?: 'asc' | 'desc';
135
143
  }
144
+ /**
145
+ * Blog post language content (blocks + metadata per language)
146
+ */
147
+ export interface BlogPostLanguageContent {
148
+ blocks: Block[];
149
+ metadata: PostMetadata;
150
+ /** Per-language last update timestamp */
151
+ updatedAt?: string;
152
+ /** Per-language publication status */
153
+ status?: PostStatus;
154
+ }
155
+ /**
156
+ * Languages object storing content per language
157
+ */
158
+ export interface BlogPostLanguages {
159
+ [key: string]: BlogPostLanguageContent;
160
+ }
136
161
  //# sourceMappingURL=post.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"post.d.ts","sourceRoot":"","sources":["../../src/types/post.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"post.d.ts","sourceRoot":"","sources":["../../src/types/post.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,8 @@ export interface FetchBlogsOptions {
10
10
  skip?: number;
11
11
  /** Filter by status (published, draft, concept) */
12
12
  status?: string;
13
+ /** Filter by language */
14
+ language?: string;
13
15
  /** Whether to fetch all posts for admin (includes drafts) */
14
16
  admin?: boolean;
15
17
  /** API base URL (default: '/api/blogs') */
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/utils/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAC9B,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC7B,0BAA0B;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiD3F;AAED,MAAM,WAAW,gBAAgB;IAC7B,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkB5E"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/utils/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAC9B,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC7B,0BAA0B;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmD3F;AAED,MAAM,WAAW,gBAAgB;IAC7B,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkB5E"}
@@ -12,7 +12,7 @@ import { apiToBlogPost } from '../lib/mappers/apiMapper';
12
12
  * ```
13
13
  */
14
14
  export async function fetchBlogs(options = {}) {
15
- const { limit = 10, skip = 0, status, admin = false, apiBaseUrl = '/api/plugin-blog', } = options;
15
+ const { limit = 10, skip = 0, status, language, admin = false, apiBaseUrl = '/api/plugin-blog', } = options;
16
16
  const params = new URLSearchParams();
17
17
  if (limit)
18
18
  params.set('limit', limit.toString());
@@ -20,6 +20,8 @@ export async function fetchBlogs(options = {}) {
20
20
  params.set('skip', skip.toString());
21
21
  if (status)
22
22
  params.set('status', status);
23
+ if (language)
24
+ params.set('language', language);
23
25
  if (admin)
24
26
  params.set('admin', 'true');
25
27
  const url = `${apiBaseUrl}?${params.toString()}`;
@@ -1 +1 @@
1
- {"version":3,"file":"CanvasEditorView.d.ts","sourceRoot":"","sources":["../../../src/views/CanvasEditor/CanvasEditorView.tsx"],"names":[],"mappings":"AAaA,MAAM,WAAW,qBAAqB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,gBAAgB,CAAC,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACL;AAED,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,qBAAqB,2CAsTpI"}
1
+ {"version":3,"file":"CanvasEditorView.d.ts","sourceRoot":"","sources":["../../../src/views/CanvasEditor/CanvasEditorView.tsx"],"names":[],"mappings":"AAaA,MAAM,WAAW,qBAAqB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,gBAAgB,CAAC,EAAE;QACf,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACL;AAED,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,qBAAqB,2CAscpI"}
@@ -17,19 +17,145 @@ export function CanvasEditorView({ postId, darkMode, backgroundColors: propsBack
17
17
  const [isPreviewMode, setIsPreviewMode] = useState(false);
18
18
  const [isSaving, setIsSaving] = useState(false);
19
19
  const [saveError, setSaveError] = useState(null);
20
+ // Language state for multilingual support
21
+ const [primaryLanguage, setPrimaryLanguage] = useState(locale || 'en');
22
+ const [isLoadingLanguage, setIsLoadingLanguage] = useState(true);
23
+ const [availableLanguages, setAvailableLanguages] = useState([locale || 'en']);
24
+ const [currentLanguage, setCurrentLanguage] = useState(locale || 'en');
20
25
  // Get registered blocks
21
26
  const registeredBlocks = useRegisteredBlocks();
22
27
  // Hero block management
23
28
  const { heroBlock, setHeroBlock, heroBlockDefinition } = useHeroBlock(state, registeredBlocks);
24
- // Post loading
29
+ // Post loading - wait for language settings to be loaded first
25
30
  const { isLoadingPost } = usePostLoader(postId, state.postId, (post) => {
26
31
  helpers.loadPost(post);
32
+ // Don't reset current language when loading - preserve user's selection
33
+ // This allows switching to a new language even if no content exists yet
34
+ // Update available languages from post's languages object
35
+ if (post.languages && Object.keys(post.languages).length > 0) {
36
+ const langs = Object.keys(post.languages);
37
+ // Add current language to available if not already there
38
+ if (!langs.includes(currentLanguage)) {
39
+ langs.push(currentLanguage);
40
+ }
41
+ setAvailableLanguages(langs);
42
+ }
27
43
  // After loading, ensure we're marked as clean
28
44
  // Use setTimeout to ensure this runs after the reducer has processed LOAD_POST
29
45
  setTimeout(() => {
30
46
  dispatch({ type: 'MARK_CLEAN' });
31
47
  }, 0);
32
- }, () => setHeroBlock(null));
48
+ }, () => setHeroBlock(null), !isLoadingLanguage ? currentLanguage : undefined);
49
+ // Sync currentLanguage to editor state on mount so onSave always knows the active language
50
+ useEffect(() => {
51
+ dispatch({ type: 'SET_CURRENT_LANGUAGE', payload: currentLanguage });
52
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount
53
+ // Fetch primary language from settings (simulated - in real app would come from config)
54
+ useEffect(() => {
55
+ const fetchLanguageSettings = async () => {
56
+ try {
57
+ // Use the locale prop as the initial language
58
+ // Only set this once on initial load, don't override if user already selected a language
59
+ if (!currentLanguage || currentLanguage === 'en') {
60
+ setPrimaryLanguage(locale || 'nl');
61
+ if (!availableLanguages.includes(locale || 'nl')) {
62
+ setAvailableLanguages([locale || 'nl']);
63
+ }
64
+ setCurrentLanguage(locale || 'nl');
65
+ dispatch({ type: 'SET_CURRENT_LANGUAGE', payload: locale || 'nl' });
66
+ }
67
+ }
68
+ catch (error) {
69
+ console.error('Failed to fetch language settings:', error);
70
+ }
71
+ finally {
72
+ setIsLoadingLanguage(false);
73
+ }
74
+ };
75
+ fetchLanguageSettings();
76
+ }, []); // Empty dependency - only run once on mount
77
+ // Handle language change
78
+ const handleLanguageChange = async (newLanguage) => {
79
+ // Save current content first if dirty
80
+ if (state.isDirty) {
81
+ const confirmed = window.confirm('You have unsaved changes. Do you want to save them first?');
82
+ if (confirmed) {
83
+ await handleSave();
84
+ }
85
+ }
86
+ setCurrentLanguage(newLanguage);
87
+ dispatch({ type: 'SET_CURRENT_LANGUAGE', payload: newLanguage });
88
+ // Reload with new language
89
+ if (postId) {
90
+ try {
91
+ const response = await fetch(`/api/plugin-blog/${postId}?language=${newLanguage}`);
92
+ if (response && response.ok) {
93
+ const apiDoc = await response.json();
94
+ // Manually update state instead of going through loadPost
95
+ // This avoids re-triggering the usePostLoader hook
96
+ const blocks = apiDoc.contentBlocks || apiDoc.blocks || [];
97
+ dispatch({ type: 'SET_TITLE', payload: apiDoc.title || '' });
98
+ // Replace all blocks
99
+ // First clear, then set new blocks via LOAD_POST-like behavior
100
+ // We use a custom dispatch to update blocks without resetting postId
101
+ dispatch({
102
+ type: 'LOAD_POST',
103
+ payload: {
104
+ id: state.postId || apiDoc._id || apiDoc.id,
105
+ title: apiDoc.title || '',
106
+ slug: apiDoc.slug || state.slug,
107
+ blocks: blocks,
108
+ seo: apiDoc.seo || {},
109
+ publication: {
110
+ status: apiDoc.publicationData?.status === 'concept' ? 'draft' : (apiDoc.publicationData?.status || state.status),
111
+ date: apiDoc.publicationData?.date,
112
+ authorId: apiDoc.authorId,
113
+ },
114
+ metadata: {
115
+ featuredImage: apiDoc.image ? {
116
+ id: apiDoc.image.id || apiDoc.image.src,
117
+ alt: apiDoc.image.alt,
118
+ isCustom: apiDoc.image.isCustom,
119
+ } : state.metadata?.featuredImage,
120
+ categories: apiDoc.categoryTags?.category ? [apiDoc.categoryTags.category] : [],
121
+ tags: apiDoc.categoryTags?.tags || [],
122
+ excerpt: apiDoc.summary || '',
123
+ lang: newLanguage,
124
+ },
125
+ languages: apiDoc.languages,
126
+ createdAt: apiDoc.createdAt || new Date().toISOString(),
127
+ updatedAt: apiDoc.updatedAt || new Date().toISOString(),
128
+ }
129
+ });
130
+ // Update available languages
131
+ if (apiDoc.availableLanguages) {
132
+ const langs = [...apiDoc.availableLanguages];
133
+ if (!langs.includes(newLanguage)) {
134
+ langs.push(newLanguage);
135
+ }
136
+ setAvailableLanguages(langs);
137
+ }
138
+ // Reset hero block so it re-initializes from new blocks
139
+ setHeroBlock(null);
140
+ setTimeout(() => {
141
+ dispatch({ type: 'MARK_CLEAN' });
142
+ }, 100);
143
+ }
144
+ }
145
+ catch (error) {
146
+ console.error('Failed to switch language:', error);
147
+ }
148
+ }
149
+ };
150
+ // Handle adding a new language
151
+ const handleAddLanguage = async (newLanguage) => {
152
+ if (availableLanguages.includes(newLanguage))
153
+ return;
154
+ // Add the new language to the list
155
+ setAvailableLanguages([...availableLanguages, newLanguage]);
156
+ // Switch to the new language (it will copy from primary language)
157
+ await handleLanguageChange(newLanguage);
158
+ };
33
159
  // Track if we just loaded a post to prevent marking as dirty during cleanup
34
160
  const justLoadedRef = useRef(false);
35
161
  const previousIsLoadingRef = useRef(false);
@@ -199,7 +325,7 @@ export function CanvasEditorView({ postId, darkMode, backgroundColors: propsBack
199
325
  data: { ...defaultData },
200
326
  });
201
327
  };
202
- return (_jsx("div", { className: "h-full rounded-[2.5rem] w-full bg-dashboard-card text-dashboard-text flex flex-col font-sans transition-colors duration-300 overflow-hidden relative", children: _jsxs("main", { className: "flex flex-1 flex-col relative min-h-0", children: [_jsx(ErrorBanner, { error: saveError, onDismiss: () => setSaveError(null) }), _jsx(EditorHeader, { isLibraryOpen: isLibraryOpen, onLibraryToggle: () => setLibraryOpen(!isLibraryOpen), isPreviewMode: isPreviewMode, onPreviewToggle: () => setIsPreviewMode(!isPreviewMode), isSidebarOpen: isSidebarOpen, onSidebarToggle: () => setSidebarOpen(!isSidebarOpen), isSaving: isSaving, onSave: handleSave, onSaveError: (error) => {
328
+ return (_jsxs("div", { className: "h-full rounded-[2.5rem] w-full bg-dashboard-card text-dashboard-text flex flex-col font-sans transition-colors duration-300 overflow-hidden", children: [_jsx("header", { className: "overflow-visible flex-none shrink-0 z-10", children: _jsx(EditorHeader, { isLibraryOpen: isLibraryOpen, onLibraryToggle: () => setLibraryOpen(!isLibraryOpen), isPreviewMode: isPreviewMode, onPreviewToggle: () => setIsPreviewMode(!isPreviewMode), isSidebarOpen: isSidebarOpen, onSidebarToggle: () => setSidebarOpen(!isSidebarOpen), isSaving: isSaving, onSave: handleSave, onSaveError: (error) => {
203
329
  // Format error message for display
204
330
  if (error) {
205
331
  let formattedError = error;
@@ -211,5 +337,5 @@ export function CanvasEditorView({ postId, darkMode, backgroundColors: propsBack
211
337
  else {
212
338
  setSaveError(null);
213
339
  }
214
- }, autoSaveEnabled: autoSaveEnabled, onAutoSaveToggle: setAutoSaveEnabled, isDirty: state.isDirty, autoSaveCountdown: countdown, autoSaveStatus: saveStatus }), _jsxs("div", { className: "flex flex-1 relative overflow-hidden min-h-0 flex-nowrap", children: [!isPreviewMode && (_jsx("aside", { className: `transition-all duration-500 ease-[cubic-bezier(0.4,0,0.2,1)] border-r border-dashboard-border bg-dashboard-sidebar overflow-y-auto overflow-x-hidden h-full ${isLibraryOpen ? 'w-72' : 'w-0 opacity-0 pointer-events-none'}`, children: _jsx(EditorLibrary, { registeredBlocks: registeredBlocks, onAddBlock: handleAddBlockAtBottom }) })), _jsx(EditorCanvas, { isPreviewMode: isPreviewMode, heroBlock: heroBlock, heroBlockDefinition: heroBlockDefinition, contentBlocks: contentBlocks, title: state.title, siteId: siteId, locale: locale, darkMode: effectiveDarkMode, backgroundColors: effectiveBackgroundColors, featuredImage: state.metadata.featuredImage, onTitleChange: (title) => dispatch({ type: 'SET_TITLE', payload: title }), onHeroBlockUpdate: handleHeroBlockUpdate, onHeroBlockDelete: handleHeroBlockDelete, onBlockAdd: (type, index, containerId) => helpers.addBlock(type, index, containerId), onBlockUpdate: (id, data) => helpers.updateBlock(id, data), onBlockDelete: (id) => helpers.deleteBlock(id), onBlockMove: (id, newIndex, containerId) => helpers.moveBlock(id, newIndex, containerId) }), !isPreviewMode && (_jsx("aside", { className: `transition-all duration-500 ease-[cubic-bezier(0.4,0,0.2,1)] border-l border-dashboard-border bg-dashboard-sidebar overflow-y-auto overflow-x-hidden h-full ${isSidebarOpen ? 'w-80' : 'w-0 opacity-0 pointer-events-none'}`, children: _jsx(EditorSidebar, { slug: state.slug, seo: state.seo, metadata: state.metadata, heroBlock: heroBlock, status: state.status, onSEOUpdate: (seo) => dispatch({ type: 'SET_SEO', payload: seo }), onMetadataUpdate: (metadata) => dispatch({ type: 'SET_METADATA', payload: metadata }) }) }))] })] }) }));
340
+ }, autoSaveEnabled: autoSaveEnabled, onAutoSaveToggle: setAutoSaveEnabled, isDirty: state.isDirty, autoSaveCountdown: countdown, autoSaveStatus: saveStatus, languages: availableLanguages, currentLanguage: currentLanguage, onLanguageChange: handleLanguageChange, onAddLanguage: handleAddLanguage }) }), _jsx(ErrorBanner, { error: saveError, onDismiss: () => setSaveError(null) }), _jsx("main", { className: "flex flex-1 flex-col relative min-h-0 overflow-hidden", children: _jsxs("div", { className: "flex flex-1 relative overflow-hidden min-h-0 flex-nowrap", children: [!isPreviewMode && (_jsx("aside", { className: `transition-all duration-500 ease-[cubic-bezier(0.4,0,0.2,1)] border-r border-dashboard-border bg-dashboard-sidebar overflow-y-auto overflow-x-hidden h-full ${isLibraryOpen ? 'w-72' : 'w-0 opacity-0 pointer-events-none'}`, children: _jsx(EditorLibrary, { registeredBlocks: registeredBlocks, onAddBlock: handleAddBlockAtBottom }) })), _jsx(EditorCanvas, { isPreviewMode: isPreviewMode, heroBlock: heroBlock, heroBlockDefinition: heroBlockDefinition, contentBlocks: contentBlocks, title: state.title, siteId: siteId, locale: locale, darkMode: effectiveDarkMode, backgroundColors: effectiveBackgroundColors, featuredImage: state.metadata.featuredImage, onTitleChange: (title) => dispatch({ type: 'SET_TITLE', payload: title }), onHeroBlockUpdate: handleHeroBlockUpdate, onHeroBlockDelete: handleHeroBlockDelete, onBlockAdd: (type, index, containerId) => helpers.addBlock(type, index, containerId), onBlockUpdate: (id, data) => helpers.updateBlock(id, data), onBlockDelete: (id) => helpers.deleteBlock(id), onBlockMove: (id, newIndex, containerId) => helpers.moveBlock(id, newIndex, containerId) }), !isPreviewMode && (_jsx("aside", { className: `transition-all duration-500 ease-[cubic-bezier(0.4,0,0.2,1)] border-l border-dashboard-border bg-dashboard-sidebar overflow-y-auto overflow-x-hidden h-full ${isSidebarOpen ? 'w-80' : 'w-0 opacity-0 pointer-events-none'}`, children: _jsx(EditorSidebar, { slug: state.slug, seo: state.seo, metadata: state.metadata, heroBlock: heroBlock, status: state.status, onSEOUpdate: (seo) => dispatch({ type: 'SET_SEO', payload: seo }), onMetadataUpdate: (metadata) => dispatch({ type: 'SET_METADATA', payload: metadata }) }) }))] }) })] }));
215
341
  }
@@ -13,6 +13,10 @@ export interface EditorHeaderProps {
13
13
  isDirty?: boolean;
14
14
  autoSaveCountdown?: number | null;
15
15
  autoSaveStatus?: 'idle' | 'saving' | 'saved' | 'error';
16
+ languages?: string[];
17
+ currentLanguage?: string;
18
+ onLanguageChange?: (language: string) => void;
19
+ onAddLanguage?: (language: string) => void;
16
20
  }
17
- export declare function EditorHeader({ isLibraryOpen, onLibraryToggle, isPreviewMode, onPreviewToggle, isSidebarOpen, onSidebarToggle, isSaving, onSave, onSaveError, autoSaveEnabled, onAutoSaveToggle, isDirty, autoSaveCountdown, autoSaveStatus, }: EditorHeaderProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function EditorHeader({ isLibraryOpen, onLibraryToggle, isPreviewMode, onPreviewToggle, isSidebarOpen, onSidebarToggle, isSaving, onSave, onSaveError, autoSaveEnabled, onAutoSaveToggle, isDirty, autoSaveCountdown, autoSaveStatus, languages, currentLanguage, onLanguageChange, onAddLanguage, }: EditorHeaderProps): import("react/jsx-runtime").JSX.Element;
18
22
  //# sourceMappingURL=EditorHeader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EditorHeader.d.ts","sourceRoot":"","sources":["../../../src/views/CanvasEditor/EditorHeader.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;CAC1D;AAED,wBAAgB,YAAY,CAAC,EACzB,aAAa,EACb,eAAe,EACf,aAAa,EACb,eAAe,EACf,aAAa,EACb,eAAe,EACf,QAAQ,EACR,MAAM,EACN,WAAW,EACX,eAAuB,EACvB,gBAAgB,EAChB,OAAe,EACf,iBAAwB,EACxB,cAAuB,GAC1B,EAAE,iBAAiB,2CAmOnB"}
1
+ {"version":3,"file":"EditorHeader.d.ts","sourceRoot":"","sources":["../../../src/views/CanvasEditor/EditorHeader.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED,wBAAgB,YAAY,CAAC,EACzB,aAAa,EACb,eAAe,EACf,aAAa,EACb,eAAe,EACf,aAAa,EACb,eAAe,EACf,QAAQ,EACR,MAAM,EACN,WAAW,EACX,eAAuB,EACvB,gBAAgB,EAChB,OAAe,EACf,iBAAwB,EACxB,cAAuB,EACvB,SAAc,EACd,eAAsB,EACtB,gBAAgB,EAChB,aAAa,GAChB,EAAE,iBAAiB,2CAsSnB"}
@@ -1,14 +1,24 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useState } from 'react';
4
- import { ArrowLeft, Library, Settings2, Clock, Edit, Eye } from 'lucide-react';
4
+ import { ArrowLeft, Library, Settings2, Clock, Edit, Eye, Globe, ChevronDown } from 'lucide-react';
5
5
  import { useEditor } from '../../state/EditorContext';
6
6
  import { SaveConfirmationModal } from './SaveConfirmationModal';
7
- export function EditorHeader({ isLibraryOpen, onLibraryToggle, isPreviewMode, onPreviewToggle, isSidebarOpen, onSidebarToggle, isSaving, onSave, onSaveError, autoSaveEnabled = false, onAutoSaveToggle, isDirty = false, autoSaveCountdown = null, autoSaveStatus = 'idle', }) {
7
+ export function EditorHeader({ isLibraryOpen, onLibraryToggle, isPreviewMode, onPreviewToggle, isSidebarOpen, onSidebarToggle, isSaving, onSave, onSaveError, autoSaveEnabled = false, onAutoSaveToggle, isDirty = false, autoSaveCountdown = null, autoSaveStatus = 'idle', languages = [], currentLanguage = 'en', onLanguageChange, onAddLanguage, }) {
8
8
  const { state, dispatch } = useEditor();
9
9
  const [showConfirmModal, setShowConfirmModal] = useState(false);
10
10
  const [saveAsDraft, setSaveAsDraft] = useState(false);
11
11
  const [saveError, setSaveError] = useState(null);
12
+ const [showLanguageDropdown, setShowLanguageDropdown] = useState(false);
13
+ const languageLabels = {
14
+ en: 'English',
15
+ nl: 'Nederlands',
16
+ sv: 'Svenska',
17
+ de: 'Deutsch',
18
+ fr: 'Français',
19
+ es: 'Español',
20
+ };
21
+ const availableToAdd = ['en', 'nl', 'sv', 'de', 'fr', 'es'].filter(lang => !languages.includes(lang));
12
22
  const handleSaveDraftClick = () => {
13
23
  setSaveAsDraft(true);
14
24
  setSaveError(null); // Clear any previous errors
@@ -65,7 +75,7 @@ export function EditorHeader({ isLibraryOpen, onLibraryToggle, isPreviewMode, on
65
75
  throw error;
66
76
  }
67
77
  };
68
- return (_jsxs("header", { className: "flex items-center justify-between px-6 py-3 bg-dashboard-sidebar backdrop-blur-md border-b border-dashboard-border flex-none shrink-0", children: [_jsxs("div", { className: "flex items-center gap-6", children: [_jsx("button", { onClick: () => {
78
+ return (_jsxs("div", { className: "flex items-center justify-between px-6 py-3 bg-dashboard-sidebar backdrop-blur-md border-b border-dashboard-border flex-none shrink-0", children: [_jsxs("div", { className: "flex items-center gap-6", children: [_jsx("button", { onClick: () => {
69
79
  if (isDirty) {
70
80
  const confirmed = window.confirm('You have unsaved changes. Are you sure you want to leave? Your changes will be lost.');
71
81
  if (!confirmed) {
@@ -73,7 +83,15 @@ export function EditorHeader({ isLibraryOpen, onLibraryToggle, isPreviewMode, on
73
83
  }
74
84
  }
75
85
  window.location.href = '/dashboard/blog';
76
- }, className: "text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white transition-colors", children: _jsx(ArrowLeft, { size: 20, strokeWidth: 1.5 }) }), _jsx("div", { className: "h-4 w-[1px] bg-neutral-300 dark:border-neutral-700" }), _jsxs("button", { onClick: onLibraryToggle, className: `flex items-center gap-2 text-[10px] uppercase tracking-widest font-black transition-all ${isLibraryOpen ? 'text-dashboard-text' : 'text-neutral-500 dark:text-neutral-400'}`, children: [_jsx(Library, { size: 16, strokeWidth: 1.5 }), "Library"] })] }), _jsxs("div", { className: "flex items-center gap-4", children: [onAutoSaveToggle && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { onClick: () => onAutoSaveToggle(!autoSaveEnabled), className: `relative flex items-center gap-2 px-3 py-1.5 rounded-full text-[10px] uppercase tracking-widest font-bold transition-all ${autoSaveEnabled
86
+ }, className: "text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white transition-colors", children: _jsx(ArrowLeft, { size: 20, strokeWidth: 1.5 }) }), _jsx("div", { className: "h-4 w-[1px] bg-neutral-300 dark:border-neutral-700" }), _jsxs("button", { onClick: onLibraryToggle, className: `flex items-center gap-2 text-[10px] uppercase tracking-widest font-black transition-all ${isLibraryOpen ? 'text-dashboard-text' : 'text-neutral-500 dark:text-neutral-400'}`, children: [_jsx(Library, { size: 16, strokeWidth: 1.5 }), "Library"] }), languages.length > 0 && onLanguageChange && (_jsxs(_Fragment, { children: [_jsx("div", { className: "h-4 w-[1px] bg-neutral-300 dark:border-neutral-700" }), _jsxs("div", { className: "relative", children: [_jsxs("button", { onClick: () => setShowLanguageDropdown(!showLanguageDropdown), className: "flex items-center gap-2 px-3 py-2 text-[10px] uppercase tracking-widest font-bold bg-dashboard-card border border-dashboard-border rounded-lg text-neutral-600 dark:text-neutral-400 hover:text-dashboard-text hover:border-primary/50 transition-all shadow-sm", children: [_jsx(Globe, { size: 14, strokeWidth: 1.5 }), _jsx("span", { children: languageLabels[currentLanguage] || currentLanguage.toUpperCase() }), _jsx(ChevronDown, { size: 12, className: `transition-transform ${showLanguageDropdown ? 'rotate-180' : ''}` })] }), showLanguageDropdown && (_jsxs("div", { className: "absolute top-full left-0 mt-2 py-1 bg-dashboard-card border border-dashboard-border rounded-lg shadow-xl z-[9999] min-w-[160px] overflow-hidden", children: [_jsx("div", { className: "px-3 py-2 text-[9px] text-neutral-500 uppercase tracking-wider border-b border-dashboard-border", children: "Beschikbare Talen" }), languages.map(lang => (_jsx("button", { onClick: () => {
87
+ onLanguageChange(lang);
88
+ setShowLanguageDropdown(false);
89
+ }, className: `w-full text-left px-3 py-2 text-[10px] uppercase tracking-wider transition-colors ${lang === currentLanguage
90
+ ? 'bg-primary/10 text-primary font-bold'
91
+ : 'text-neutral-600 dark:text-neutral-400 hover:bg-dashboard-bg hover:text-dashboard-text'}`, children: languageLabels[lang] || lang.toUpperCase() }, lang))), availableToAdd.length > 0 && onAddLanguage && (_jsxs(_Fragment, { children: [_jsx("div", { className: "my-1 border-t border-dashboard-border" }), _jsx("div", { className: "px-3 py-1 text-[9px] text-neutral-500 uppercase tracking-wider", children: "Add Language" }), availableToAdd.slice(0, 3).map(lang => (_jsxs("button", { onClick: () => {
92
+ onAddLanguage(lang);
93
+ setShowLanguageDropdown(false);
94
+ }, className: "w-full text-left px-3 py-2 text-[10px] uppercase tracking-wider text-neutral-600 dark:text-neutral-400 hover:bg-dashboard-bg hover:text-dashboard-text transition-colors", children: ["+ ", languageLabels[lang] || lang.toUpperCase()] }, lang)))] }))] }))] })] }))] }), _jsxs("div", { className: "flex items-center gap-4", children: [onAutoSaveToggle && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { onClick: () => onAutoSaveToggle(!autoSaveEnabled), className: `relative flex items-center gap-2 px-3 py-1.5 rounded-full text-[10px] uppercase tracking-widest font-bold transition-all ${autoSaveEnabled
77
95
  ? 'bg-primary/20 text-primary border border-primary/30'
78
96
  : 'bg-dashboard-bg text-neutral-600 dark:text-neutral-400 border border-dashboard-border hover:text-neutral-950 dark:hover:text-white'}`, title: autoSaveEnabled ? 'Auto-save enabled (saves after 10s of inactivity)' : 'Click to enable auto-save', children: [_jsx(Clock, { size: 12, className: autoSaveEnabled && autoSaveStatus !== 'saving' ? 'animate-pulse' : '' }), _jsx("span", { children: "Auto-save" }), _jsx("span", { className: `ml-1 text-[9px] ${autoSaveEnabled ? 'text-primary' : 'text-neutral-500 dark:text-neutral-400'}`, children: autoSaveEnabled ? 'ON' : 'OFF' }), autoSaveEnabled && isDirty && (_jsxs("span", { className: "ml-1.5 text-[9px] font-bold tabular-nums", children: [autoSaveStatus === 'saving' && (_jsx("span", { className: "text-primary animate-pulse", children: "Saving..." })), autoSaveStatus === 'saved' && (_jsx("span", { className: "text-green-500 dark:text-green-400", children: "Saved!" })), autoSaveStatus === 'error' && (_jsx("span", { className: "text-red-500 dark:text-red-400", children: "Error" })), autoSaveStatus === 'idle' && autoSaveCountdown !== null && (_jsxs("span", { className: "text-primary/70", children: [autoSaveCountdown, "s"] }))] }))] }), isDirty && !autoSaveEnabled && (_jsx("span", { className: "text-[10px] text-amber-500 dark:text-amber-400 font-bold uppercase tracking-widest animate-pulse", children: "Unsaved" }))] })), _jsxs("div", { className: "flex items-center bg-dashboard-bg border border-dashboard-border rounded-full p-1 gap-1", children: [_jsxs("button", { onClick: () => {
79
97
  if (isPreviewMode) {
@@ -1,5 +1,5 @@
1
1
  import type { BlogPost } from '../../../types/post';
2
- export declare function usePostLoader(postId: string | undefined, currentPostId: string | null, loadPost: (post: BlogPost) => void, resetHeroBlock: () => void): {
2
+ export declare function usePostLoader(postId: string | undefined, currentPostId: string | null, loadPost: (post: BlogPost) => void, resetHeroBlock: () => void, language?: string): {
3
3
  isLoadingPost: boolean;
4
4
  };
5
5
  //# sourceMappingURL=usePostLoader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePostLoader.d.ts","sourceRoot":"","sources":["../../../../src/views/CanvasEditor/hooks/usePostLoader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,wBAAgB,aAAa,CACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,EAClC,cAAc,EAAE,MAAM,IAAI;;EA8B7B"}
1
+ {"version":3,"file":"usePostLoader.d.ts","sourceRoot":"","sources":["../../../../src/views/CanvasEditor/hooks/usePostLoader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,wBAAgB,aAAa,CACzB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,EAClC,cAAc,EAAE,MAAM,IAAI,EAC1B,QAAQ,CAAC,EAAE,MAAM;;EAwCpB"}
@@ -1,7 +1,10 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useEffect, useState, useRef } from 'react';
2
2
  import { apiToBlogPost } from '../../../lib/mappers/apiMapper';
3
- export function usePostLoader(postId, currentPostId, loadPost, resetHeroBlock) {
3
+ export function usePostLoader(postId, currentPostId, loadPost, resetHeroBlock, language) {
4
4
  const [isLoadingPost, setIsLoadingPost] = useState(false);
5
+ // Use a ref to track language so changes don't re-trigger initial load
6
+ const languageRef = useRef(language);
7
+ languageRef.current = language;
5
8
  useEffect(() => {
6
9
  if (postId && !currentPostId) {
7
10
  const loadPostData = async () => {
@@ -9,7 +12,11 @@ export function usePostLoader(postId, currentPostId, loadPost, resetHeroBlock) {
9
12
  setIsLoadingPost(true);
10
13
  // Reset hero block before loading new post so it gets re-initialized from the new post's blocks
11
14
  resetHeroBlock();
12
- const response = await fetch(`/api/plugin-blog/${postId}`);
15
+ const lang = languageRef.current;
16
+ const url = lang
17
+ ? `/api/plugin-blog/${postId}?language=${lang}`
18
+ : `/api/plugin-blog/${postId}`;
19
+ const response = await fetch(url);
13
20
  if (!response.ok) {
14
21
  throw new Error('Failed to load post');
15
22
  }
@@ -27,6 +34,9 @@ export function usePostLoader(postId, currentPostId, loadPost, resetHeroBlock) {
27
34
  };
28
35
  loadPostData();
29
36
  }
30
- }, [postId, currentPostId, loadPost, resetHeroBlock]);
37
+ // Only re-run on initial load (postId / currentPostId change), NOT on language change
38
+ // Language switching is handled separately by handleLanguageChange
39
+ // eslint-disable-next-line react-hooks/exhaustive-deps
40
+ }, [postId, currentPostId]);
31
41
  return { isLoadingPost };
32
42
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Language Flags Component
3
+ * Displays interactive language flags with custom tooltips
4
+ */
5
+ import { PostListItem } from '../../types/post';
6
+ interface LanguageFlagsProps {
7
+ post: PostListItem;
8
+ }
9
+ export declare function LanguageFlags({ post }: LanguageFlagsProps): import("react/jsx-runtime").JSX.Element | null;
10
+ export {};
11
+ //# sourceMappingURL=LanguageFlags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LanguageFlags.d.ts","sourceRoot":"","sources":["../../../src/views/PostManager/LanguageFlags.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,UAAU,kBAAkB;IACxB,IAAI,EAAE,YAAY,CAAC;CACtB;AAwBD,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,EAAE,kBAAkB,kDAiGzD"}