create-nextblock 0.2.30 → 0.2.33

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 (23) hide show
  1. package/package.json +1 -1
  2. package/scripts/sync-template.js +70 -52
  3. package/templates/nextblock-template/app/[slug]/page.tsx +55 -55
  4. package/templates/nextblock-template/app/cms/blocks/actions.ts +15 -15
  5. package/templates/nextblock-template/app/cms/blocks/components/BackgroundSelector.tsx +14 -12
  6. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorArea.tsx +24 -21
  7. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +1 -1
  8. package/templates/nextblock-template/app/cms/blocks/components/ColumnEditor.tsx +42 -24
  9. package/templates/nextblock-template/app/cms/blocks/components/EditableBlock.tsx +16 -16
  10. package/templates/nextblock-template/app/cms/blocks/editors/SectionBlockEditor.tsx +56 -35
  11. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +1 -1
  12. package/templates/nextblock-template/app/cms/media/actions.ts +47 -47
  13. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +3 -3
  14. package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +1 -1
  15. package/templates/nextblock-template/app/cms/media/page.tsx +3 -3
  16. package/templates/nextblock-template/app/cms/revisions/JsonDiffView.tsx +8 -7
  17. package/templates/nextblock-template/app/cms/revisions/RevisionHistoryButton.tsx +16 -10
  18. package/templates/nextblock-template/app/cms/revisions/service.ts +9 -9
  19. package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +1 -1
  20. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +1 -0
  21. package/templates/nextblock-template/eslint.config.mjs +14 -10
  22. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +29 -29
  23. package/templates/nextblock-template/package.json +5 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nextblock",
3
- "version": "0.2.30",
3
+ "version": "0.2.33",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -36,16 +36,17 @@ const UI_PROXY_MODULES = [
36
36
  'ui',
37
37
  ];
38
38
 
39
- const IGNORED_SEGMENTS = new Set([
40
- 'node_modules',
41
- '.git',
42
- '.next',
43
- 'dist',
44
- 'tmp',
45
- 'coverage',
46
- 'backup',
47
- 'backups',
48
- ]);
39
+ const IGNORED_SEGMENTS = new Set([
40
+ 'node_modules',
41
+ '.git',
42
+ '.next',
43
+ 'dist',
44
+ 'tmp',
45
+ 'coverage',
46
+ 'backup',
47
+ 'backups',
48
+ ]);
49
+
49
50
 
50
51
  async function ensureTemplateSync() {
51
52
  const sourceExists = await fs.pathExists(SOURCE_DIR);
@@ -64,7 +65,7 @@ async function ensureTemplateSync() {
64
65
  );
65
66
 
66
67
  await fs.ensureDir(TARGET_DIR);
67
- await fs.emptyDir(TARGET_DIR);
68
+ await emptyDirWithRetry(TARGET_DIR);
68
69
 
69
70
  await fs.copy(SOURCE_DIR, TARGET_DIR, {
70
71
  dereference: true,
@@ -79,10 +80,10 @@ async function ensureTemplateSync() {
79
80
  },
80
81
  });
81
82
 
82
- await ensureEnvExample();
83
- await ensureTemplateGitignore();
84
- await ensureGlobalStyles();
85
- await ensureClientTranslations();
83
+ await ensureEnvExample();
84
+ await ensureTemplateGitignore();
85
+ await ensureGlobalStyles();
86
+ await ensureClientTranslations();
86
87
  await sanitizeBlockEditorImports();
87
88
  await sanitizeUiImports();
88
89
  await ensureUiProxies();
@@ -92,7 +93,7 @@ async function ensureTemplateSync() {
92
93
  console.log(chalk.green('Template sync complete.'));
93
94
  }
94
95
 
95
- async function ensureEnvExample() {
96
+ async function ensureEnvExample() {
96
97
  const envTargets = [
97
98
  resolve(REPO_ROOT, '.env.example'),
98
99
  resolve(REPO_ROOT, '.env.exemple'),
@@ -118,42 +119,42 @@ NEXT_PUBLIC_URL=http://localhost:3000
118
119
  `;
119
120
 
120
121
  await fs.writeFile(destination, placeholder);
121
- }
122
-
123
- async function ensureTemplateGitignore() {
124
- const destination = resolve(TARGET_DIR, '.gitignore');
125
- const content = `.DS_Store
126
- node_modules
127
- dist
128
- .next
129
- out
130
- build
131
- coverage
132
- *.log
133
- logs
134
- npm-debug.log*
135
- yarn-debug.log*
136
- yarn-error.log*
137
- pnpm-debug.log*
138
-
139
- .env
140
- .env.*
141
- .env.local
142
- .env.development.local
143
- .env.production.local
144
- .env.test.local
145
-
146
- .vscode
147
- .idea
148
- .swp
149
- *.sw?
150
-
151
- supabase/.temp
152
- supabase/.branches
153
- `;
154
- await fs.outputFile(destination, content);
155
- }
156
-
122
+ }
123
+
124
+ async function ensureTemplateGitignore() {
125
+ const destination = resolve(TARGET_DIR, '.gitignore');
126
+ const content = `.DS_Store
127
+ node_modules
128
+ dist
129
+ .next
130
+ out
131
+ build
132
+ coverage
133
+ *.log
134
+ logs
135
+ npm-debug.log*
136
+ yarn-debug.log*
137
+ yarn-error.log*
138
+ pnpm-debug.log*
139
+
140
+ .env
141
+ .env.*
142
+ .env.local
143
+ .env.development.local
144
+ .env.production.local
145
+ .env.test.local
146
+
147
+ .vscode
148
+ .idea
149
+ .swp
150
+ *.sw?
151
+
152
+ supabase/.temp
153
+ supabase/.branches
154
+ `;
155
+ await fs.outputFile(destination, content);
156
+ }
157
+
157
158
  async function ensureGlobalStyles() {
158
159
  const destination = resolve(TARGET_DIR, 'app/globals.css');
159
160
 
@@ -312,3 +313,20 @@ ensureTemplateSync().catch((error) => {
312
313
  console.error(chalk.red(error instanceof Error ? error.message : String(error)));
313
314
  process.exit(1);
314
315
  });
316
+
317
+ async function emptyDirWithRetry(dir, retries = 5, delay = 1000) {
318
+ for (let i = 0; i < retries; i++) {
319
+ try {
320
+ await fs.emptyDir(dir);
321
+ return;
322
+ } catch (err) {
323
+ if (i === retries - 1) throw err;
324
+ if (err.code === 'EBUSY' || err.code === 'EPERM') {
325
+ console.log(chalk.yellow(`Locked file encountered. Retrying in ${delay}ms... (${i + 1}/${retries})`));
326
+ await new Promise((resolve) => setTimeout(resolve, delay));
327
+ } else {
328
+ throw err;
329
+ }
330
+ }
331
+ }
332
+ }
@@ -1,18 +1,18 @@
1
1
  // app/[slug]/page.tsx
2
2
  import React from 'react';
3
3
  import { getSsgSupabaseClient } from "@nextblock-cms/db/server";
4
- import { notFound } from "next/navigation";
5
- import type { Metadata } from 'next';
4
+ import { notFound } from "next/navigation";
5
+ import type { Metadata } from 'next';
6
6
  import PageClientContent from "./PageClientContent";
7
- import { getPageDataBySlug } from "./page.utils";
8
- import BlockRenderer from "../../components/BlockRenderer";
9
- import type { HeroBlockContent } from '../../lib/blocks/blockRegistry';
10
- import { cookies, headers } from "next/headers";
7
+ import { getPageDataBySlug } from "./page.utils";
8
+ import BlockRenderer from "../../components/BlockRenderer";
9
+ import type { HeroBlockContent } from '../../lib/blocks/blockRegistry';
10
+ import { cookies, headers } from "next/headers";
11
11
 
12
- export const dynamicParams = true;
13
- export const revalidate = 360;
14
- export const dynamic = 'force-dynamic'; // keeps per-request locale; paired with short revalidate
15
- export const fetchCache = 'force-no-store';
12
+ export const dynamicParams = true;
13
+ export const revalidate = 360;
14
+ export const dynamic = 'force-dynamic'; // keeps per-request locale; paired with short revalidate
15
+ export const fetchCache = 'force-no-store';
16
16
 
17
17
  interface ResolvedPageParams {
18
18
  slug: string;
@@ -40,30 +40,30 @@ export async function generateStaticParams(): Promise<ResolvedPageParams[]> {
40
40
  console.error("SSG: Error fetching page slugs for static params:", error);
41
41
  return [];
42
42
  }
43
- return pages.map((page) => ({ slug: page.slug }));
43
+ return pages.map((page: { slug: string }) => ({ slug: page.slug }));
44
44
  }
45
45
 
46
- export async function generateMetadata(
47
- { params: paramsPromise }: PageProps,
48
- ): Promise<Metadata> {
49
- const params = await paramsPromise;
50
- let preferredLocale: string | undefined;
51
- try {
52
- const store = await cookies();
53
- preferredLocale = store.get("NEXT_USER_LOCALE")?.value || store.get("NEXT_LOCALE")?.value;
54
- } catch {
55
- preferredLocale = undefined;
56
- }
57
- if (!preferredLocale) {
58
- try {
59
- const hdrs = await headers();
60
- const al = hdrs.get("accept-language");
61
- if (al) preferredLocale = al.split(",")[0]?.split("-")[0];
62
- } catch {
63
- // ignore header lookup errors
64
- }
65
- }
66
- const pageData = await getPageDataBySlug(params.slug, preferredLocale);
46
+ export async function generateMetadata(
47
+ { params: paramsPromise }: PageProps,
48
+ ): Promise<Metadata> {
49
+ const params = await paramsPromise;
50
+ let preferredLocale: string | undefined;
51
+ try {
52
+ const store = await cookies();
53
+ preferredLocale = store.get("NEXT_USER_LOCALE")?.value || store.get("NEXT_LOCALE")?.value;
54
+ } catch {
55
+ preferredLocale = undefined;
56
+ }
57
+ if (!preferredLocale) {
58
+ try {
59
+ const hdrs = await headers();
60
+ const al = hdrs.get("accept-language");
61
+ if (al) preferredLocale = al.split(",")[0]?.split("-")[0];
62
+ } catch {
63
+ // ignore header lookup errors
64
+ }
65
+ }
66
+ const pageData = await getPageDataBySlug(params.slug, preferredLocale);
67
67
 
68
68
  if (!pageData) {
69
69
  return { title: "Page Not Found" };
@@ -87,8 +87,8 @@ export async function generateMetadata(
87
87
 
88
88
  const alternates: { [key: string]: string } = {};
89
89
  if (languages && pageTranslations) {
90
- pageTranslations.forEach(pt => {
91
- const langInfo = languages.find(l => l.id === pt.language_id);
90
+ pageTranslations.forEach((pt: { language_id: string; slug: string }) => {
91
+ const langInfo = languages.find((l: { id: string; code: string }) => l.id === pt.language_id);
92
92
  if (langInfo) {
93
93
  alternates[langInfo.code] = `${siteUrl}/${pt.slug}`;
94
94
  }
@@ -105,25 +105,25 @@ export async function generateMetadata(
105
105
  };
106
106
  }
107
107
 
108
- export default async function DynamicPage({ params: paramsPromise }: PageProps) {
109
- const params = await paramsPromise;
110
- let preferredLocale: string | undefined;
111
- try {
112
- const store = await cookies();
113
- preferredLocale = store.get("NEXT_USER_LOCALE")?.value || store.get("NEXT_LOCALE")?.value;
114
- } catch {
115
- preferredLocale = undefined;
116
- }
117
- if (!preferredLocale) {
118
- try {
119
- const hdrs = await headers();
120
- const al = hdrs.get("accept-language");
121
- if (al) preferredLocale = al.split(",")[0]?.split("-")[0];
122
- } catch {
123
- // ignore header lookup errors
124
- }
125
- }
126
- const pageData = await getPageDataBySlug(params.slug, preferredLocale);
108
+ export default async function DynamicPage({ params: paramsPromise }: PageProps) {
109
+ const params = await paramsPromise;
110
+ let preferredLocale: string | undefined;
111
+ try {
112
+ const store = await cookies();
113
+ preferredLocale = store.get("NEXT_USER_LOCALE")?.value || store.get("NEXT_LOCALE")?.value;
114
+ } catch {
115
+ preferredLocale = undefined;
116
+ }
117
+ if (!preferredLocale) {
118
+ try {
119
+ const hdrs = await headers();
120
+ const al = hdrs.get("accept-language");
121
+ if (al) preferredLocale = al.split(",")[0]?.split("-")[0];
122
+ } catch {
123
+ // ignore header lookup errors
124
+ }
125
+ }
126
+ const pageData = await getPageDataBySlug(params.slug, preferredLocale);
127
127
 
128
128
  if (!pageData) {
129
129
  notFound();
@@ -151,7 +151,7 @@ export default async function DynamicPage({ params: paramsPromise }: PageProps)
151
151
  const r2BaseUrl = process.env.NEXT_PUBLIC_R2_BASE_URL || "";
152
152
 
153
153
  if (pageData && pageData.blocks && r2BaseUrl) {
154
- const heroBlock = pageData.blocks.find(block => block.block_type === 'hero');
154
+ const heroBlock = pageData.blocks.find((block: { block_type: string; content: unknown }) => block.block_type === 'hero');
155
155
  if (heroBlock) {
156
156
  const heroContent = heroBlock.content as unknown as HeroBlockContent;
157
157
  if (
@@ -177,4 +177,4 @@ export default async function DynamicPage({ params: paramsPromise }: PageProps)
177
177
  </PageClientContent>
178
178
  </>
179
179
  );
180
- }
180
+ }
@@ -5,22 +5,22 @@ import { createClient } from "@nextblock-cms/db/server";
5
5
  import { revalidatePath } from "next/cache";
6
6
  import type { Database, Json } from "@nextblock-cms/db";
7
7
  import { getInitialContent, isValidBlockType } from "../../../lib/blocks/blockRegistry";
8
- import { getFullPageContent, getFullPostContent } from "../revisions/utils";
8
+ import { getFullPageContent, getFullPostContent, type FullPageContent, type FullPostContent } from "../revisions/utils";
9
9
  import { createPageRevision, createPostRevision } from "../revisions/service";
10
10
 
11
11
  type Block = Database['public']['Tables']['blocks']['Row'];
12
12
  type BlockType = Database['public']['Tables']['blocks']['Row']['block_type'];
13
13
 
14
14
  // Helper to verify user can edit the parent (page/post)
15
- async function canEditParent(
16
- supabase: ReturnType<typeof createClient>,
17
- userId: string,
18
- pageId?: number | null,
19
- postId?: number | null
20
- ): Promise<boolean> {
21
- void pageId;
22
- void postId;
23
- const { data: profile } = await supabase
15
+ async function canEditParent(
16
+ supabase: ReturnType<typeof createClient>,
17
+ userId: string,
18
+ pageId?: number | null,
19
+ postId?: number | null
20
+ ): Promise<boolean> {
21
+ void pageId;
22
+ void postId;
23
+ const { data: profile } = await supabase
24
24
  .from("profiles")
25
25
  .select("role")
26
26
  .eq("id", userId)
@@ -160,7 +160,7 @@ export async function updateBlock(blockId: number, newContent: unknown, pageId?:
160
160
  return { error: "Block not found." };
161
161
  }
162
162
 
163
- let prevContentAggregate: Awaited<ReturnType<typeof getFullPageContent>> | Awaited<ReturnType<typeof getFullPostContent>> | null = null;
163
+ let prevContentAggregate: FullPageContent | FullPostContent | null = null;
164
164
  if (existingBlock.page_id) {
165
165
  prevContentAggregate = await getFullPageContent(existingBlock.page_id);
166
166
  } else if (existingBlock.post_id) {
@@ -189,7 +189,7 @@ export async function updateBlock(blockId: number, newContent: unknown, pageId?:
189
189
  } else if (existingBlock.post_id) {
190
190
  const nextContentAggregate = await getFullPostContent(existingBlock.post_id, { overrideBlockId: blockId, overrideBlockContent: newContent });
191
191
  if (nextContentAggregate) {
192
- await createPostRevision(existingBlock.post_id, user.id, prevContentAggregate as any, nextContentAggregate as any);
192
+ await createPostRevision(existingBlock.post_id, user.id, prevContentAggregate as FullPostContent, nextContentAggregate as FullPostContent);
193
193
  }
194
194
  }
195
195
  }
@@ -250,7 +250,7 @@ export async function deleteBlock(blockId: number, pageId?: number | null, postI
250
250
  return { error: "Block not found." };
251
251
  }
252
252
 
253
- let previousAggregate: Awaited<ReturnType<typeof getFullPageContent>> | Awaited<ReturnType<typeof getFullPostContent>> | null = null;
253
+ let previousAggregate: FullPageContent | FullPostContent | null = null;
254
254
  if (existingBlock.page_id) {
255
255
  previousAggregate = await getFullPageContent(existingBlock.page_id);
256
256
  } else if (existingBlock.post_id) {
@@ -274,7 +274,7 @@ export async function deleteBlock(blockId: number, pageId?: number | null, postI
274
274
  } else if (existingBlock.post_id) {
275
275
  const nextAggregate = await getFullPostContent(existingBlock.post_id, { excludeDeletedBlockId: blockId });
276
276
  if (nextAggregate) {
277
- await createPostRevision(existingBlock.post_id, user.id, previousAggregate as any, nextAggregate as any);
277
+ await createPostRevision(existingBlock.post_id, user.id, previousAggregate as FullPostContent, nextAggregate as FullPostContent);
278
278
  }
279
279
  }
280
280
  }
@@ -418,7 +418,7 @@ export async function copyBlocksFromLanguage(
418
418
  console.warn("Could not fetch target post slug for revalidation:", postError);
419
419
  } else {
420
420
  targetSlug = postData.slug;
421
- if (targetSlug) revalidatePath(`/article/${targetSlug}`);
421
+ if (targetSlug) revalidatePath(`/article/${targetSlug}`);
422
422
  }
423
423
  revalidatePath(`/cms/posts/${parentId}/edit`); // Revalidate edit page
424
424
  }
@@ -21,6 +21,8 @@ interface BackgroundSelectorProps {
21
21
  onChange: (newBackground: SectionBlockContent["background"]) => void;
22
22
  }
23
23
 
24
+ type ChangeEventLike = { target: { name: string; value: string } };
25
+
24
26
  export default function BackgroundSelector({ background, onChange }: BackgroundSelectorProps) {
25
27
 
26
28
  const backgroundType = background?.type || "none";
@@ -127,12 +129,12 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
127
129
  }
128
130
  };
129
131
 
130
- const handleBackgroundPropertyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
132
+ const handleBackgroundPropertyChange = (e: ChangeEventLike) => {
131
133
  const { name, value } = e.target;
132
134
  onChange({ ...background, [name]: value });
133
135
  };
134
136
 
135
- const handleOverlayGradientChange = (e: React.ChangeEvent<HTMLInputElement>) => {
137
+ const handleOverlayGradientChange = (e: ChangeEventLike) => {
136
138
  const { name, value } = e.target;
137
139
  if (background?.type === "image" && background.image) {
138
140
  const { image } = background;
@@ -170,7 +172,7 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
170
172
  <div className="space-y-4">
171
173
  <div className="grid gap-2">
172
174
  <Label>Background Type</Label>
173
- <Select value={backgroundType} onValueChange={(v) => handleTypeChange(v as any)}>
175
+ <Select value={backgroundType} onValueChange={(v) => handleTypeChange(v as SectionBlockContent["background"]["type"])}>
174
176
  <SelectTrigger className="w-full max-w-[250px]"><SelectValue placeholder="Select type" /></SelectTrigger>
175
177
  <SelectContent>
176
178
  <SelectItem value="none">None</SelectItem>
@@ -184,7 +186,7 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
184
186
  <Label htmlFor="min_height">Minimum Height (e.g., 250px)</Label>
185
187
  <div className="flex items-center gap-2">
186
188
  <Input id="min_height" name="min_height" value={minHeight} onChange={(e) => setMinHeight(e.target.value)} placeholder="e.g., 250px" className="max-w-[200px]" />
187
- <Button type="button" variant="ghost" size="icon" onClick={() => handleBackgroundPropertyChange({ target: { name: "min_height", value: minHeight } } as any)} disabled={!hasMinHeightChanged} title="Save Minimum Height">
189
+ <Button type="button" variant="ghost" size="icon" onClick={() => handleBackgroundPropertyChange({ target: { name: "min_height", value: minHeight } })} disabled={!hasMinHeightChanged} title="Save Minimum Height">
188
190
  <Save className={cn("h-5 w-5", hasMinHeightChanged && "text-green-600")} />
189
191
  </Button>
190
192
  </div>
@@ -272,7 +274,7 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
272
274
  ]}
273
275
  />
274
276
  </div>
275
- <Button size="icon" variant="ghost" onClick={() => handleOverlayGradientChange({ target: { name: "direction", value: overlayDirection } } as any)} disabled={!hasOverlayDirectionChanged} title="Save Overlay Direction">
277
+ <Button size="icon" variant="ghost" onClick={() => handleOverlayGradientChange({ target: { name: "direction", value: overlayDirection } })} disabled={!hasOverlayDirectionChanged} title="Save Overlay Direction">
276
278
  <Save className={cn("h-5 w-5 mt-[1.3rem]", hasOverlayDirectionChanged && "text-green-600")} />
277
279
  </Button>
278
280
  </div>
@@ -280,12 +282,12 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
280
282
  <ColorPicker
281
283
  label="Start Color"
282
284
  color={selectedImage.overlay.gradient?.stops?.[0]?.color || "rgba(0,0,0,0.5)"}
283
- onChange={(color) => handleOverlayGradientChange({ target: { name: "startColor", value: color } } as any)}
285
+ onChange={(color) => handleOverlayGradientChange({ target: { name: "startColor", value: color } })}
284
286
  />
285
287
  <ColorPicker
286
288
  label="End Color"
287
289
  color={selectedImage.overlay.gradient?.stops?.[1]?.color || "rgba(0,0,0,0)"}
288
- onChange={(color) => handleOverlayGradientChange({ target: { name: "endColor", value: color } } as any)}
290
+ onChange={(color) => handleOverlayGradientChange({ target: { name: "endColor", value: color } })}
289
291
  />
290
292
  </div>
291
293
  </div>
@@ -300,7 +302,7 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
300
302
  label="Direction"
301
303
  tooltipContent="Select a preset or enter a custom angle like '45deg' or 'to top left'. See MDN's linear-gradient docs for more options."
302
304
  value={background.gradient?.direction || "to right"}
303
- onChange={(value: string) => handleBackgroundGradientChange({ target: { name: "direction", value } } as any)}
305
+ onChange={(value: string) => handleBackgroundGradientChange({ target: { name: "direction", value } })}
304
306
  options={[
305
307
  { value: "to right", label: "To Right" },
306
308
  { value: "to left", label: "To Left" },
@@ -315,12 +317,12 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
315
317
  <ColorPicker
316
318
  label="Start Color"
317
319
  color={background.gradient?.stops?.[0]?.color || "#3b82f6"}
318
- onChange={(color) => handleBackgroundGradientChange({ target: { name: "startColor", value: color } } as any)}
320
+ onChange={(color) => handleBackgroundGradientChange({ target: { name: "startColor", value: color } })}
319
321
  />
320
322
  <ColorPicker
321
323
  label="End Color"
322
324
  color={background.gradient?.stops?.[1]?.color || "#8b5cf6"}
323
- onChange={(color) => handleBackgroundGradientChange({ target: { name: "endColor", value: color } } as any)}
325
+ onChange={(color) => handleBackgroundGradientChange({ target: { name: "endColor", value: color } })}
324
326
  />
325
327
  </div>
326
328
  </div>
@@ -328,8 +330,8 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
328
330
  </div>
329
331
  </TooltipProvider>
330
332
  );
331
- const handleBackgroundGradientChange = (e: React.ChangeEvent<HTMLInputElement>) => {
332
- const { name, value } = e.target as any;
333
+ const handleBackgroundGradientChange = (e: ChangeEventLike) => {
334
+ const { name, value } = e.target;
333
335
  if (backgroundType !== 'gradient') return;
334
336
  const current = background.gradient || { type: 'linear' as const, direction: 'to right', stops: [ { color: '#3b82f6', position: 0 }, { color: '#8b5cf6', position: 100 } ] };
335
337
  if (name === 'direction') {
@@ -82,7 +82,7 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
82
82
  const [activeBlock, setActiveBlock] = useState<Block | null>(null);
83
83
  const [insertionIndex, setInsertionIndex] = useState<number | null>(null);
84
84
  const [editingNestedBlockInfo, setEditingNestedBlockInfo] = useState<EditingNestedBlockInfo | null>(null);
85
- const [NestedBlockEditorComponent, setNestedBlockEditorComponent] = useState<ComponentType<any> | null>(null);
85
+ const [NestedBlockEditorComponent, setNestedBlockEditorComponent] = useState<ComponentType<Record<string, unknown>> | null>(null);
86
86
  const [tempNestedBlockContent, setTempNestedBlockContent] = useState<Json | null>(null);
87
87
 
88
88
  useEffect(() => {
@@ -129,41 +129,41 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
129
129
  debouncedSave(updatedBlock);
130
130
  };
131
131
 
132
- const DynamicTextBlockEditor = dynamic(() => import('../editors/TextBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
133
- const DynamicHeadingBlockEditor = dynamic(() => import('../editors/HeadingBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
134
- const DynamicImageBlockEditor = dynamic(() => import('../editors/ImageBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
135
- const DynamicButtonBlockEditor = dynamic(() => import('../editors/ButtonBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
136
- const DynamicPostsGridBlockEditor = dynamic(() => import('../editors/PostsGridBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
137
- const DynamicVideoEmbedBlockEditor = dynamic(() => import('../editors/VideoEmbedBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
138
- const DynamicSectionBlockEditor = dynamic(() => import('../editors/SectionBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
132
+ const DynamicTextBlockEditor = dynamic(() => import('../editors/TextBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
133
+ const DynamicHeadingBlockEditor = dynamic(() => import('../editors/HeadingBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
134
+ const DynamicImageBlockEditor = dynamic(() => import('../editors/ImageBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
135
+ const DynamicButtonBlockEditor = dynamic(() => import('../editors/ButtonBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
136
+ const DynamicPostsGridBlockEditor = dynamic(() => import('../editors/PostsGridBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
137
+ const DynamicVideoEmbedBlockEditor = dynamic(() => import('../editors/VideoEmbedBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
138
+ const DynamicSectionBlockEditor = dynamic(() => import('../editors/SectionBlockEditor').then(mod => mod.default), { loading: () => <p>Loading editor...</p> });
139
139
 
140
140
  useEffect(() => {
141
141
  if (editingNestedBlockInfo) {
142
142
  const blockType = editingNestedBlockInfo.blockData.block_type;
143
- let SelectedEditor: React.ComponentType<any> | null = null;
143
+ let SelectedEditor: React.ComponentType<Record<string, unknown>> | null = null;
144
144
 
145
145
  try {
146
146
  switch (blockType) {
147
147
  case 'text':
148
- SelectedEditor = DynamicTextBlockEditor;
148
+ SelectedEditor = DynamicTextBlockEditor as unknown as ComponentType<Record<string, unknown>>;
149
149
  break;
150
150
  case 'heading':
151
- SelectedEditor = DynamicHeadingBlockEditor;
151
+ SelectedEditor = DynamicHeadingBlockEditor as unknown as ComponentType<Record<string, unknown>>;
152
152
  break;
153
153
  case 'image':
154
- SelectedEditor = DynamicImageBlockEditor;
154
+ SelectedEditor = DynamicImageBlockEditor as unknown as ComponentType<Record<string, unknown>>;
155
155
  break;
156
156
  case 'button':
157
- SelectedEditor = DynamicButtonBlockEditor;
157
+ SelectedEditor = DynamicButtonBlockEditor as unknown as ComponentType<Record<string, unknown>>;
158
158
  break;
159
159
  case 'posts_grid':
160
- SelectedEditor = DynamicPostsGridBlockEditor;
160
+ SelectedEditor = DynamicPostsGridBlockEditor as unknown as ComponentType<Record<string, unknown>>;
161
161
  break;
162
162
  case 'video_embed':
163
- SelectedEditor = DynamicVideoEmbedBlockEditor;
163
+ SelectedEditor = DynamicVideoEmbedBlockEditor as unknown as ComponentType<Record<string, unknown>>;
164
164
  break;
165
165
  case 'section':
166
- SelectedEditor = DynamicSectionBlockEditor;
166
+ SelectedEditor = DynamicSectionBlockEditor as unknown as ComponentType<Record<string, unknown>>;
167
167
  break;
168
168
  default:
169
169
  console.warn(`No dynamic editor configured for nested block type: ${blockType}`);
@@ -389,7 +389,10 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
389
389
  parentBlockId: parentBlockIdStr,
390
390
  columnIndex,
391
391
  blockIndexInColumn,
392
- blockData: nestedBlockData,
392
+ blockData: {
393
+ ...nestedBlockData,
394
+ content: nestedBlockData.content as unknown as Json
395
+ },
393
396
  });
394
397
  } else {
395
398
  console.error("Nested block not found at specified indices:", { parentBlockIdStr, columnIndex, blockIndexInColumn });
@@ -444,7 +447,7 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
444
447
  onContentChange={handleContentChange}
445
448
  onDelete={async (blockIdToDelete) => {
446
449
  startTransition(async () => {
447
- const result = await import("../actions").then(({ deleteBlock }) =>
450
+ const result = await import("../actions").then(({ deleteBlock }) =>
448
451
  deleteBlock(
449
452
  blockIdToDelete,
450
453
  parentType === "page" ? parentId : null,
@@ -472,8 +475,8 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
472
475
  <EditableBlock
473
476
  block={activeBlock}
474
477
  className="h-full"
475
- onDelete={() => {}} // eslint-disable-line @typescript-eslint/no-empty-function
476
- onContentChange={() => {}} // eslint-disable-line @typescript-eslint/no-empty-function
478
+ onDelete={() => {}}
479
+ onContentChange={() => {}}
477
480
  />
478
481
  </div>
479
482
  ) : null}
@@ -522,7 +525,7 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
522
525
  if (blockType === "posts_grid") {
523
526
  const fullBlockForEditor: Block = {
524
527
  block_type: editingNestedBlockInfo.blockData.block_type,
525
- content: tempNestedBlockContent,
528
+ content: tempNestedBlockContent as Json,
526
529
  id: 0, // Temporary ID for nested blocks
527
530
  language_id: languageId,
528
531
  order: 0, // Temporary order for nested blocks
@@ -33,7 +33,7 @@ type BlockEditorModalProps = {
33
33
  isOpen: boolean;
34
34
  onClose: () => void;
35
35
  onSave: (updatedContent: unknown) => void;
36
- EditorComponent: LazyExoticComponent<ComponentType<BlockEditorProps<unknown>>>;
36
+ EditorComponent: LazyExoticComponent<ComponentType<any>>;
37
37
  };
38
38
 
39
39
  export function BlockEditorModal({