create-nextblock 0.0.1

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 (206) hide show
  1. package/bin/create-nextblock.js +997 -0
  2. package/package.json +25 -0
  3. package/scripts/sync-template.js +284 -0
  4. package/templates/nextblock-template/.env.example +37 -0
  5. package/templates/nextblock-template/.swcrc +30 -0
  6. package/templates/nextblock-template/README.md +194 -0
  7. package/templates/nextblock-template/app/(auth-pages)/forgot-password/page.tsx +57 -0
  8. package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -0
  9. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +28 -0
  10. package/templates/nextblock-template/app/(auth-pages)/sign-in/page.tsx +67 -0
  11. package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +70 -0
  12. package/templates/nextblock-template/app/ToasterProvider.tsx +17 -0
  13. package/templates/nextblock-template/app/[slug]/PageClientContent.tsx +147 -0
  14. package/templates/nextblock-template/app/[slug]/page.tsx +145 -0
  15. package/templates/nextblock-template/app/[slug]/page.utils.ts +183 -0
  16. package/templates/nextblock-template/app/actions/email.ts +31 -0
  17. package/templates/nextblock-template/app/actions/formActions.ts +65 -0
  18. package/templates/nextblock-template/app/actions/languageActions.ts +130 -0
  19. package/templates/nextblock-template/app/actions/postActions.ts +80 -0
  20. package/templates/nextblock-template/app/actions.ts +146 -0
  21. package/templates/nextblock-template/app/api/process-image/route.ts +210 -0
  22. package/templates/nextblock-template/app/api/revalidate/route.ts +86 -0
  23. package/templates/nextblock-template/app/api/revalidate-log/route.ts +23 -0
  24. package/templates/nextblock-template/app/api/upload/presigned-url/route.ts +106 -0
  25. package/templates/nextblock-template/app/api/upload/proxy/route.ts +84 -0
  26. package/templates/nextblock-template/app/auth/callback/route.ts +58 -0
  27. package/templates/nextblock-template/app/blog/[slug]/PostClientContent.tsx +169 -0
  28. package/templates/nextblock-template/app/blog/[slug]/page.tsx +177 -0
  29. package/templates/nextblock-template/app/blog/[slug]/page.utils.ts +136 -0
  30. package/templates/nextblock-template/app/blog/page.tsx +77 -0
  31. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +321 -0
  32. package/templates/nextblock-template/app/cms/blocks/actions.ts +434 -0
  33. package/templates/nextblock-template/app/cms/blocks/components/BackgroundSelector.tsx +348 -0
  34. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorArea.tsx +567 -0
  35. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +98 -0
  36. package/templates/nextblock-template/app/cms/blocks/components/BlockTypeCard.tsx +58 -0
  37. package/templates/nextblock-template/app/cms/blocks/components/BlockTypeSelector.tsx +62 -0
  38. package/templates/nextblock-template/app/cms/blocks/components/ColumnEditor.tsx +276 -0
  39. package/templates/nextblock-template/app/cms/blocks/components/DeleteBlockButtonClient.tsx +47 -0
  40. package/templates/nextblock-template/app/cms/blocks/components/EditableBlock.tsx +182 -0
  41. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +120 -0
  42. package/templates/nextblock-template/app/cms/blocks/components/SectionConfigPanel.tsx +133 -0
  43. package/templates/nextblock-template/app/cms/blocks/components/SortableBlockItem.tsx +46 -0
  44. package/templates/nextblock-template/app/cms/blocks/editors/ButtonBlockEditor.tsx +85 -0
  45. package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +182 -0
  46. package/templates/nextblock-template/app/cms/blocks/editors/HeadingBlockEditor.tsx +111 -0
  47. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +150 -0
  48. package/templates/nextblock-template/app/cms/blocks/editors/PostsGridBlockEditor.tsx +79 -0
  49. package/templates/nextblock-template/app/cms/blocks/editors/SectionBlockEditor.tsx +337 -0
  50. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +81 -0
  51. package/templates/nextblock-template/app/cms/blocks/editors/VideoEmbedBlockEditor.tsx +64 -0
  52. package/templates/nextblock-template/app/cms/components/ConfirmationModal.tsx +51 -0
  53. package/templates/nextblock-template/app/cms/components/ContentLanguageSwitcher.tsx +145 -0
  54. package/templates/nextblock-template/app/cms/components/CopyContentFromLanguage.tsx +203 -0
  55. package/templates/nextblock-template/app/cms/components/LanguageFilterSelect.tsx +69 -0
  56. package/templates/nextblock-template/app/cms/dashboard/page.tsx +247 -0
  57. package/templates/nextblock-template/app/cms/layout.tsx +10 -0
  58. package/templates/nextblock-template/app/cms/media/UploadFolderContext.tsx +22 -0
  59. package/templates/nextblock-template/app/cms/media/[id]/edit/page.tsx +80 -0
  60. package/templates/nextblock-template/app/cms/media/actions.ts +577 -0
  61. package/templates/nextblock-template/app/cms/media/components/DeleteMediaButtonClient.tsx +53 -0
  62. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -0
  63. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -0
  64. package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +157 -0
  65. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +275 -0
  66. package/templates/nextblock-template/app/cms/media/components/MediaImage.tsx +70 -0
  67. package/templates/nextblock-template/app/cms/media/components/MediaPickerDialog.tsx +195 -0
  68. package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +362 -0
  69. package/templates/nextblock-template/app/cms/media/page.tsx +120 -0
  70. package/templates/nextblock-template/app/cms/navigation/[id]/edit/page.tsx +101 -0
  71. package/templates/nextblock-template/app/cms/navigation/actions.ts +358 -0
  72. package/templates/nextblock-template/app/cms/navigation/components/DeleteNavItemButton.tsx +52 -0
  73. package/templates/nextblock-template/app/cms/navigation/components/NavigationItemForm.tsx +248 -0
  74. package/templates/nextblock-template/app/cms/navigation/components/NavigationLanguageSwitcher.tsx +132 -0
  75. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +701 -0
  76. package/templates/nextblock-template/app/cms/navigation/components/SortableNavItem.tsx +98 -0
  77. package/templates/nextblock-template/app/cms/navigation/new/page.tsx +26 -0
  78. package/templates/nextblock-template/app/cms/navigation/page.tsx +102 -0
  79. package/templates/nextblock-template/app/cms/navigation/utils.ts +51 -0
  80. package/templates/nextblock-template/app/cms/pages/[id]/edit/EditPageClient.tsx +121 -0
  81. package/templates/nextblock-template/app/cms/pages/[id]/edit/page.tsx +79 -0
  82. package/templates/nextblock-template/app/cms/pages/actions.ts +241 -0
  83. package/templates/nextblock-template/app/cms/pages/components/DeletePageButtonClient.tsx +47 -0
  84. package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +253 -0
  85. package/templates/nextblock-template/app/cms/pages/new/page.tsx +52 -0
  86. package/templates/nextblock-template/app/cms/pages/page.tsx +232 -0
  87. package/templates/nextblock-template/app/cms/posts/[id]/edit/page.tsx +183 -0
  88. package/templates/nextblock-template/app/cms/posts/actions.ts +309 -0
  89. package/templates/nextblock-template/app/cms/posts/components/DeletePostButtonClient.tsx +55 -0
  90. package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +419 -0
  91. package/templates/nextblock-template/app/cms/posts/new/page.tsx +21 -0
  92. package/templates/nextblock-template/app/cms/posts/page.tsx +192 -0
  93. package/templates/nextblock-template/app/cms/revisions/JsonDiffView.tsx +86 -0
  94. package/templates/nextblock-template/app/cms/revisions/RevisionHistoryButton.tsx +201 -0
  95. package/templates/nextblock-template/app/cms/revisions/actions.ts +84 -0
  96. package/templates/nextblock-template/app/cms/revisions/service.ts +344 -0
  97. package/templates/nextblock-template/app/cms/revisions/utils.ts +127 -0
  98. package/templates/nextblock-template/app/cms/settings/copyright/actions.ts +68 -0
  99. package/templates/nextblock-template/app/cms/settings/copyright/components/CopyrightForm.tsx +78 -0
  100. package/templates/nextblock-template/app/cms/settings/copyright/page.tsx +32 -0
  101. package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +117 -0
  102. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +216 -0
  103. package/templates/nextblock-template/app/cms/settings/languages/[id]/edit/page.tsx +77 -0
  104. package/templates/nextblock-template/app/cms/settings/languages/actions.ts +261 -0
  105. package/templates/nextblock-template/app/cms/settings/languages/components/DeleteLanguageButton.tsx +76 -0
  106. package/templates/nextblock-template/app/cms/settings/languages/components/LanguageForm.tsx +167 -0
  107. package/templates/nextblock-template/app/cms/settings/languages/new/page.tsx +34 -0
  108. package/templates/nextblock-template/app/cms/settings/languages/page.tsx +156 -0
  109. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +19 -0
  110. package/templates/nextblock-template/app/cms/settings/logos/actions.ts +114 -0
  111. package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +177 -0
  112. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +11 -0
  113. package/templates/nextblock-template/app/cms/settings/logos/page.tsx +118 -0
  114. package/templates/nextblock-template/app/cms/settings/logos/types.ts +8 -0
  115. package/templates/nextblock-template/app/cms/users/[id]/edit/page.tsx +91 -0
  116. package/templates/nextblock-template/app/cms/users/actions.ts +156 -0
  117. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +71 -0
  118. package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +138 -0
  119. package/templates/nextblock-template/app/cms/users/page.tsx +183 -0
  120. package/templates/nextblock-template/app/favicon.ico +0 -0
  121. package/templates/nextblock-template/app/globals.css +401 -0
  122. package/templates/nextblock-template/app/layout.tsx +191 -0
  123. package/templates/nextblock-template/app/lib/sitemap-utils.ts +68 -0
  124. package/templates/nextblock-template/app/page.tsx +109 -0
  125. package/templates/nextblock-template/app/providers.tsx +43 -0
  126. package/templates/nextblock-template/app/robots.txt/route.ts +19 -0
  127. package/templates/nextblock-template/app/sitemap.xml/route.ts +63 -0
  128. package/templates/nextblock-template/app/unauthorized/page.tsx +27 -0
  129. package/templates/nextblock-template/backup/backup_2025-06-19.sql +8057 -0
  130. package/templates/nextblock-template/backup/backup_2025-06-20.sql +8159 -0
  131. package/templates/nextblock-template/backup/backup_2025-07-08.sql +8411 -0
  132. package/templates/nextblock-template/backup/backup_2025-07-09.sql +8442 -0
  133. package/templates/nextblock-template/backup/backup_2025-07-10.sql +8442 -0
  134. package/templates/nextblock-template/backup/backup_2025-10-01.sql +8803 -0
  135. package/templates/nextblock-template/backup/backup_2025-10-02.sql +9749 -0
  136. package/templates/nextblock-template/components/BlockRenderer.tsx +119 -0
  137. package/templates/nextblock-template/components/FooterNavigation.tsx +33 -0
  138. package/templates/nextblock-template/components/Header.tsx +42 -0
  139. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -0
  140. package/templates/nextblock-template/components/LanguageSwitcher.tsx +103 -0
  141. package/templates/nextblock-template/components/ResponsiveNav.tsx +372 -0
  142. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +17 -0
  143. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +93 -0
  144. package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +180 -0
  145. package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -0
  146. package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +69 -0
  147. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +98 -0
  148. package/templates/nextblock-template/components/blocks/renderers/HeadingBlockRenderer.tsx +41 -0
  149. package/templates/nextblock-template/components/blocks/renderers/HeroBlockRenderer.tsx +240 -0
  150. package/templates/nextblock-template/components/blocks/renderers/ImageBlockRenderer.tsx +79 -0
  151. package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +33 -0
  152. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +189 -0
  153. package/templates/nextblock-template/components/blocks/renderers/TextBlockRenderer.tsx +31 -0
  154. package/templates/nextblock-template/components/blocks/renderers/VideoEmbedBlockRenderer.tsx +59 -0
  155. package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +51 -0
  156. package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +40 -0
  157. package/templates/nextblock-template/components/blocks/types.ts +8 -0
  158. package/templates/nextblock-template/components/env-var-warning.tsx +33 -0
  159. package/templates/nextblock-template/components/form-message.tsx +26 -0
  160. package/templates/nextblock-template/components/header-auth.tsx +71 -0
  161. package/templates/nextblock-template/components/submit-button.tsx +23 -0
  162. package/templates/nextblock-template/components/theme-switcher.tsx +78 -0
  163. package/templates/nextblock-template/context/AuthContext.tsx +138 -0
  164. package/templates/nextblock-template/context/CurrentContentContext.tsx +42 -0
  165. package/templates/nextblock-template/context/LanguageContext.tsx +206 -0
  166. package/templates/nextblock-template/docs/cms-application-overview.md +56 -0
  167. package/templates/nextblock-template/docs/cms-architecture-overview.md +73 -0
  168. package/templates/nextblock-template/docs/files-structure.md +426 -0
  169. package/templates/nextblock-template/docs/tiptap-bundle-optimization-summary.md +174 -0
  170. package/templates/nextblock-template/eslint.config.mjs +28 -0
  171. package/templates/nextblock-template/index.d.ts +5 -0
  172. package/templates/nextblock-template/lib/blocks/README.md +670 -0
  173. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +1001 -0
  174. package/templates/nextblock-template/lib/ui/ColorPicker.ts +1 -0
  175. package/templates/nextblock-template/lib/ui/ConfirmationDialog.ts +1 -0
  176. package/templates/nextblock-template/lib/ui/CustomSelectWithInput.ts +1 -0
  177. package/templates/nextblock-template/lib/ui/Skeleton.ts +1 -0
  178. package/templates/nextblock-template/lib/ui/avatar.ts +1 -0
  179. package/templates/nextblock-template/lib/ui/badge.ts +1 -0
  180. package/templates/nextblock-template/lib/ui/button.ts +1 -0
  181. package/templates/nextblock-template/lib/ui/card.ts +1 -0
  182. package/templates/nextblock-template/lib/ui/checkbox.ts +1 -0
  183. package/templates/nextblock-template/lib/ui/dialog.ts +1 -0
  184. package/templates/nextblock-template/lib/ui/dropdown-menu.ts +1 -0
  185. package/templates/nextblock-template/lib/ui/input.ts +1 -0
  186. package/templates/nextblock-template/lib/ui/label.ts +1 -0
  187. package/templates/nextblock-template/lib/ui/popover.ts +1 -0
  188. package/templates/nextblock-template/lib/ui/progress.ts +1 -0
  189. package/templates/nextblock-template/lib/ui/select.ts +1 -0
  190. package/templates/nextblock-template/lib/ui/separator.ts +1 -0
  191. package/templates/nextblock-template/lib/ui/table.ts +1 -0
  192. package/templates/nextblock-template/lib/ui/textarea.ts +1 -0
  193. package/templates/nextblock-template/lib/ui/tooltip.ts +1 -0
  194. package/templates/nextblock-template/lib/ui/ui.ts +1 -0
  195. package/templates/nextblock-template/middleware.ts +206 -0
  196. package/templates/nextblock-template/next-env.d.ts +6 -0
  197. package/templates/nextblock-template/next.config.js +99 -0
  198. package/templates/nextblock-template/package.json +52 -0
  199. package/templates/nextblock-template/postcss.config.js +6 -0
  200. package/templates/nextblock-template/project.json +7 -0
  201. package/templates/nextblock-template/public/.gitkeep +0 -0
  202. package/templates/nextblock-template/scripts/backfill-image-meta.ts +149 -0
  203. package/templates/nextblock-template/scripts/backup.js +53 -0
  204. package/templates/nextblock-template/scripts/test-bundle-optimization.js +114 -0
  205. package/templates/nextblock-template/tailwind.config.ts +19 -0
  206. package/templates/nextblock-template/tsconfig.json +62 -0
@@ -0,0 +1,670 @@
1
+ # Block Registry System
2
+
3
+ ## Overview
4
+
5
+ The Block Registry System is a centralized, registry-based architecture for managing content blocks in the CMS. This system replaces the previous switch-statement approach with a more scalable, maintainable solution that uses dynamic imports and a single source of truth for block definitions.
6
+
7
+ ### Key Benefits
8
+
9
+ - **Centralized Configuration**: All block types are defined in one place ([`blockRegistry.ts`](./blockRegistry.ts))
10
+ - **Dynamic Loading**: Components are loaded on-demand using Next.js dynamic imports
11
+ - **Type Safety**: Full TypeScript support with proper interface declarations
12
+ - **Scalability**: Easy to add new block types without modifying core systems
13
+ - **Maintainability**: Clear separation of concerns and consistent patterns
14
+ - **Performance**: Components are only loaded when needed
15
+ - **Better IDE Support**: Proper TypeScript interfaces with IntelliSense and compile-time checking
16
+
17
+ ## Quick Start: Adding a New Block Type
18
+
19
+ Follow these steps to add a new block type (e.g., "Video Embed"). With the enhanced registry system, you only need to update the registry and create component files!
20
+
21
+ ### Step 1: Define the Block Type with Proper TypeScript Interface
22
+
23
+ Add your new block type to the registry in [`lib/blocks/blockRegistry.ts`](./blockRegistry.ts):
24
+
25
+ ```typescript
26
+ // 1. Add the TypeScript interface at the top of the file
27
+ /**
28
+ * Content interface for video embed blocks
29
+ * Embeds videos from popular platforms with customizable playback options
30
+ */
31
+ export interface VideoEmbedBlockContent {
32
+ /** The video URL (YouTube, Vimeo, etc.) */
33
+ url: string;
34
+ /** Optional title for the video */
35
+ title?: string;
36
+ /** Whether the video should autoplay */
37
+ autoplay?: boolean;
38
+ /** Whether to show video controls */
39
+ controls?: boolean;
40
+ }
41
+
42
+ // 2. Add to availableBlockTypes array
43
+ export const availableBlockTypes = [
44
+ "text",
45
+ "heading",
46
+ "image",
47
+ "button",
48
+ "posts_grid",
49
+ "video_embed" // Add your new type
50
+ ] as const;
51
+
52
+ // 3. Add to blockRegistry object with complete definition
53
+ export const blockRegistry: Record<BlockType, BlockDefinition> = {
54
+ // ... existing blocks ...
55
+
56
+ video_embed: {
57
+ type: "video_embed",
58
+ label: "Video Embed",
59
+ initialContent: {
60
+ url: "",
61
+ title: "",
62
+ autoplay: false,
63
+ controls: true
64
+ } as VideoEmbedBlockContent,
65
+ editorComponentFilename: "VideoEmbedBlockEditor.tsx",
66
+ rendererComponentFilename: "VideoEmbedBlockRenderer.tsx",
67
+ contentSchema: {
68
+ url: {
69
+ type: 'string',
70
+ required: true,
71
+ description: 'The video URL (YouTube, Vimeo, etc.)',
72
+ default: '',
73
+ },
74
+ title: {
75
+ type: 'string',
76
+ required: false,
77
+ description: 'Optional title for the video',
78
+ default: '',
79
+ },
80
+ autoplay: {
81
+ type: 'boolean',
82
+ required: false,
83
+ description: 'Whether the video should autoplay',
84
+ default: false,
85
+ },
86
+ controls: {
87
+ type: 'boolean',
88
+ required: false,
89
+ description: 'Whether to show video controls',
90
+ default: true,
91
+ },
92
+ },
93
+ documentation: {
94
+ description: 'Embeds videos from popular platforms with customizable playback options',
95
+ examples: [
96
+ '{ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", title: "Rick Roll", controls: true }',
97
+ '{ url: "https://vimeo.com/123456789", autoplay: false, controls: true }',
98
+ ],
99
+ useCases: [
100
+ 'Tutorial and educational videos',
101
+ 'Product demonstrations',
102
+ 'Marketing and promotional content',
103
+ ],
104
+ notes: [
105
+ 'Supports YouTube, Vimeo, and other major video platforms',
106
+ 'Autoplay may be restricted by browser policies',
107
+ 'Videos are responsive and adapt to container width',
108
+ ],
109
+ },
110
+ },
111
+ };
112
+ ```
113
+
114
+ ### Step 2: Create the Editor Component
115
+
116
+ Create [`app/cms/blocks/editors/VideoEmbedBlockEditor.tsx`](../../app/cms/blocks/editors/VideoEmbedBlockEditor.tsx):
117
+
118
+ ```typescript
119
+ "use client";
120
+
121
+ import React from 'react';
122
+ import { Label } from "@nextblock-cms/ui";
123
+ import { Input } from "@nextblock-cms/ui";
124
+ import { Checkbox } from "@nextblock-cms/ui";
125
+ import { generateDefaultContent, VideoEmbedBlockContent } from "@/lib/blocks/blockRegistry";
126
+
127
+ interface VideoEmbedBlockEditorProps {
128
+ content: Partial<VideoEmbedBlockContent>;
129
+ onChange: (newContent: VideoEmbedBlockContent) => void;
130
+ }
131
+
132
+ export default function VideoEmbedBlockEditor({ content, onChange }: VideoEmbedBlockEditorProps) {
133
+ // Get default content from registry
134
+ const defaultContent = generateDefaultContent("video_embed") as VideoEmbedBlockContent;
135
+
136
+ const handleChange = (field: keyof VideoEmbedBlockContent, value: any) => {
137
+ onChange({
138
+ ...defaultContent,
139
+ ...content,
140
+ [field]: value,
141
+ });
142
+ };
143
+
144
+ return (
145
+ <div className="space-y-4 p-3 border-t mt-2">
146
+ <div>
147
+ <Label htmlFor="video-url">Video URL</Label>
148
+ <Input
149
+ id="video-url"
150
+ type="url"
151
+ value={content.url || ""}
152
+ onChange={(e) => handleChange("url", e.target.value)}
153
+ placeholder="https://www.youtube.com/watch?v=..."
154
+ />
155
+ </div>
156
+
157
+ <div>
158
+ <Label htmlFor="video-title">Title (Optional)</Label>
159
+ <Input
160
+ id="video-title"
161
+ value={content.title || ""}
162
+ onChange={(e) => handleChange("title", e.target.value)}
163
+ placeholder="Video title"
164
+ />
165
+ </div>
166
+
167
+ <div className="flex items-center space-x-2">
168
+ <Checkbox
169
+ id="autoplay"
170
+ checked={content.autoplay || false}
171
+ onCheckedChange={(checked) => handleChange("autoplay", checked)}
172
+ />
173
+ <Label htmlFor="autoplay">Autoplay</Label>
174
+ </div>
175
+
176
+ <div className="flex items-center space-x-2">
177
+ <Checkbox
178
+ id="controls"
179
+ checked={content.controls !== false}
180
+ onCheckedChange={(checked) => handleChange("controls", checked)}
181
+ />
182
+ <Label htmlFor="controls">Show Controls</Label>
183
+ </div>
184
+ </div>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ### Step 3: Create the Renderer Component
190
+
191
+ Create [`components/blocks/renderers/VideoEmbedBlockRenderer.tsx`](../../components/blocks/renderers/VideoEmbedBlockRenderer.tsx):
192
+
193
+ ```typescript
194
+ import React from "react";
195
+ import { validateBlockContent, VideoEmbedBlockContent } from "@/lib/blocks/blockRegistry";
196
+
197
+ interface VideoEmbedBlockRendererProps {
198
+ content: VideoEmbedBlockContent;
199
+ languageId: number;
200
+ }
201
+
202
+ const VideoEmbedBlockRenderer: React.FC<VideoEmbedBlockRendererProps> = ({
203
+ content,
204
+ languageId,
205
+ }) => {
206
+ // Optional: Validate content against registry schema
207
+ const validation = validateBlockContent("video_embed", content);
208
+ if (!validation.isValid) {
209
+ console.warn("Invalid video embed content:", validation.errors);
210
+ }
211
+
212
+ if (!content.url) {
213
+ return null;
214
+ }
215
+
216
+ // Convert YouTube URLs to embed format
217
+ const getEmbedUrl = (url: string) => {
218
+ const youtubeRegex = /(?:youtube\.com\/watch\?v=|youtu\.be\/)([^&\n?#]+)/;
219
+ const match = url.match(youtubeRegex);
220
+
221
+ if (match) {
222
+ const videoId = match[1];
223
+ const params = new URLSearchParams();
224
+ if (content.autoplay) params.set('autoplay', '1');
225
+ if (!content.controls) params.set('controls', '0');
226
+
227
+ return `https://www.youtube.com/embed/${videoId}?${params.toString()}`;
228
+ }
229
+
230
+ return url; // Return original URL if not YouTube
231
+ };
232
+
233
+ return (
234
+ <div className="my-4">
235
+ {content.title && (
236
+ <h3 className="text-lg font-semibold mb-2">{content.title}</h3>
237
+ )}
238
+ <div className="relative aspect-video">
239
+ <iframe
240
+ src={getEmbedUrl(content.url)}
241
+ title={content.title || "Video"}
242
+ className="w-full h-full rounded-lg"
243
+ allowFullScreen
244
+ loading="lazy"
245
+ />
246
+ </div>
247
+ </div>
248
+ );
249
+ };
250
+
251
+ export default VideoEmbedBlockRenderer;
252
+ ```
253
+
254
+ ### Step 4: Test Your New Block
255
+
256
+ 1. Restart your development server to ensure all changes are loaded
257
+ 2. Navigate to a page or post editor in the CMS
258
+ 3. Click "Add Block" and select "Video Embed"
259
+ 4. Configure the video settings and save
260
+ 5. View the page/post to see your rendered video block
261
+
262
+ **That's it!** The registry system handles all the TypeScript interface definitions and validation automatically.
263
+
264
+ ## Architecture
265
+
266
+ ### Enhanced Registry System
267
+
268
+ The new enhanced registry system provides:
269
+
270
+ - **Proper TypeScript Interfaces**: Each block has a real TypeScript interface declared at the top of the file
271
+ - **Better IDE Support**: Full IntelliSense, syntax highlighting, and compile-time checking
272
+ - **Runtime Validation**: Content can be validated against schema definitions
273
+ - **Auto-Generated Documentation**: Comprehensive documentation is built into each block definition
274
+ - **Utility Functions**: Helper functions for type generation, validation, and content management
275
+ - **Single Source of Truth**: All block information is centralized in the registry
276
+
277
+ ### File Structure
278
+
279
+ ```
280
+ lib/blocks/
281
+ ├── blockRegistry.ts # Enhanced central registry with proper TypeScript interfaces
282
+ └── README.md # This documentation
283
+
284
+ app/cms/blocks/editors/ # Editor components for CMS
285
+ ├── TextBlockEditor.tsx
286
+ ├── HeadingBlockEditor.tsx
287
+ ├── ImageBlockEditor.tsx
288
+ ├── ButtonBlockEditor.tsx
289
+ ├── PostsGridBlockEditor.tsx
290
+ └── [YourNewBlock]Editor.tsx
291
+
292
+ components/blocks/renderers/ # Renderer components for frontend
293
+ ├── TextBlockRenderer.tsx
294
+ ├── HeadingBlockRenderer.tsx
295
+ ├── ImageBlockRenderer.tsx
296
+ ├── ButtonBlockRenderer.tsx
297
+ ├── PostsGridBlockRenderer.tsx
298
+ └── [YourNewBlock]Renderer.tsx
299
+
300
+ utils/supabase/types.ts # Core database types (block content types no longer needed here)
301
+ ```
302
+
303
+ ### Core Components
304
+
305
+ #### 1. Enhanced Block Registry ([`blockRegistry.ts`](./blockRegistry.ts))
306
+
307
+ The central registry now contains:
308
+ - **Proper TypeScript Interfaces**: Real interface declarations at the top of the file
309
+ - **Block Type Definitions**: Available block types as a const array
310
+ - **Complete Block Definitions**: Configuration and schemas for each block type
311
+ - **Content Schemas**: Structured property definitions with validation rules
312
+ - **Documentation**: Built-in examples, use cases, and implementation notes
313
+ - **Utility Functions**: Comprehensive helper functions for validation and content management
314
+
315
+ #### 2. Dynamic Block Renderer ([`components/BlockRenderer.tsx`](../../components/BlockRenderer.tsx))
316
+
317
+ Handles dynamic loading and rendering of blocks:
318
+ - Uses Next.js `dynamic()` for code splitting
319
+ - Provides loading states and error handling
320
+ - Supports SSR (Server-Side Rendering)
321
+
322
+ #### 3. Block Actions ([`app/cms/blocks/actions.ts`](../../app/cms/blocks/actions.ts))
323
+
324
+ Server actions for block CRUD operations:
325
+ - Uses registry for validation and initial content
326
+ - Handles authorization and database operations
327
+ - Provides revalidation for updated content
328
+
329
+ ## API Reference
330
+
331
+ ### TypeScript Interfaces
332
+
333
+ All block content interfaces are now properly declared at the top of [`blockRegistry.ts`](./blockRegistry.ts):
334
+
335
+ ```typescript
336
+ export interface TextBlockContent {
337
+ html_content: string;
338
+ }
339
+
340
+ export interface HeadingBlockContent {
341
+ level: 1 | 2 | 3 | 4 | 5 | 6;
342
+ text_content: string;
343
+ }
344
+
345
+ export interface ImageBlockContent {
346
+ media_id: string | null;
347
+ object_key?: string | null;
348
+ alt_text?: string;
349
+ caption?: string;
350
+ width?: number | null;
351
+ height?: number | null;
352
+ }
353
+
354
+ export interface ButtonBlockContent {
355
+ text: string;
356
+ url: string;
357
+ variant?: 'default' | 'outline' | 'secondary' | 'ghost' | 'link';
358
+ size?: 'default' | 'sm' | 'lg';
359
+ }
360
+
361
+ export interface PostsGridBlockContent {
362
+ postsPerPage: number;
363
+ columns: number;
364
+ showPagination: boolean;
365
+ title?: string;
366
+ }
367
+ ```
368
+
369
+ ### Enhanced BlockDefinition Interface
370
+
371
+ ```typescript
372
+ interface BlockDefinition<T = any> {
373
+ type: BlockType; // Unique identifier
374
+ label: string; // Display name in CMS
375
+ initialContent: T; // Default content structure (properly typed)
376
+ editorComponentFilename: string; // Editor component filename
377
+ rendererComponentFilename: string; // Renderer component filename
378
+ previewComponentFilename?: string; // Optional preview component
379
+ contentSchema: Record<string, ContentPropertyDefinition>; // Structured schema
380
+ documentation?: { // Optional documentation
381
+ description?: string;
382
+ examples?: string[];
383
+ useCases?: string[];
384
+ notes?: string[];
385
+ };
386
+ }
387
+ ```
388
+
389
+ ### Registry Functions
390
+
391
+ #### Core Registry Functions
392
+
393
+ #### `getBlockDefinition(blockType: BlockType)`
394
+ Returns the complete block definition for a given type.
395
+
396
+ ```typescript
397
+ const definition = getBlockDefinition("text");
398
+ // Returns: { type: "text", label: "HTML Block", initialContent: {...}, ... }
399
+ ```
400
+
401
+ #### `getInitialContent(blockType: BlockType)`
402
+ Returns the initial content structure for a new block.
403
+
404
+ ```typescript
405
+ const content = getInitialContent("heading");
406
+ // Returns: { level: 1, text_content: "New Heading" }
407
+ ```
408
+
409
+ #### `getBlockLabel(blockType: BlockType)`
410
+ Returns the user-friendly label for a block type.
411
+
412
+ ```typescript
413
+ const label = getBlockLabel("image");
414
+ // Returns: "Image"
415
+ ```
416
+
417
+ #### `isValidBlockType(blockType: string)`
418
+ Type guard to check if a string is a valid block type.
419
+
420
+ ```typescript
421
+ if (isValidBlockType(userInput)) {
422
+ // userInput is now typed as BlockType
423
+ const definition = getBlockDefinition(userInput);
424
+ }
425
+ ```
426
+
427
+ #### Enhanced Registry Functions
428
+
429
+ #### `getContentSchema(blockType: BlockType)`
430
+ Returns the structured content schema for validation and documentation.
431
+
432
+ ```typescript
433
+ const schema = getContentSchema("heading");
434
+ // Returns: { level: { type: 'union', required: true, ... }, text_content: { ... } }
435
+ ```
436
+
437
+ #### `getBlockDocumentation(blockType: BlockType)`
438
+ Returns documentation including description, examples, and use cases.
439
+
440
+ ```typescript
441
+ const docs = getBlockDocumentation("image");
442
+ // Returns: { description: "...", examples: [...], useCases: [...], notes: [...] }
443
+ ```
444
+
445
+ #### Validation and Utility Functions
446
+
447
+ #### `validateBlockContent(blockType: BlockType, content: Record<string, any>)`
448
+ Validates block content against its schema definition.
449
+
450
+ ```typescript
451
+ const validation = validateBlockContent("button", { text: "Click me" });
452
+ // Returns: { isValid: false, errors: ["Required property 'url' is missing"], warnings: [] }
453
+ ```
454
+
455
+ #### `getPropertyDefinition(blockType: BlockType, propertyName: string)`
456
+ Returns the definition for a specific property of a block type.
457
+
458
+ ```typescript
459
+ const propDef = getPropertyDefinition("heading", "level");
460
+ // Returns: { type: 'union', required: true, description: "...", constraints: { ... } }
461
+ ```
462
+
463
+ #### `getPropertyNames(blockType: BlockType)`
464
+ Returns all property names for a block type.
465
+
466
+ ```typescript
467
+ const props = getPropertyNames("image");
468
+ // Returns: ["media_id", "object_key", "alt_text", "caption", "width", "height"]
469
+ ```
470
+
471
+ #### `getRequiredProperties(blockType: BlockType)`
472
+ Returns only the required property names for a block type.
473
+
474
+ ```typescript
475
+ const required = getRequiredProperties("text");
476
+ // Returns: ["html_content"]
477
+ ```
478
+
479
+ #### `generateDefaultContent(blockType: BlockType)`
480
+ Generates complete default content including all properties with defaults.
481
+
482
+ ```typescript
483
+ const defaults = generateDefaultContent("button");
484
+ // Returns: { text: "Click Me", url: "#", variant: "default", size: "default" }
485
+ ```
486
+
487
+ ### Component Interfaces
488
+
489
+ #### Editor Component Props
490
+ ```typescript
491
+ interface BlockEditorProps<T> {
492
+ content: Partial<T>;
493
+ onChange: (newContent: T) => void;
494
+ }
495
+ ```
496
+
497
+ #### Renderer Component Props
498
+ ```typescript
499
+ interface BlockRendererProps<T> {
500
+ content: T;
501
+ languageId: number;
502
+ block?: Block; // Optional, for blocks that need full block data
503
+ }
504
+ ```
505
+
506
+ ## Migration Guide
507
+
508
+ ### What Changed
509
+
510
+ The system has evolved to use proper TypeScript interfaces:
511
+
512
+ **Before (String-based interfaces):**
513
+ ```typescript
514
+ contentInterface: `interface TextBlockContent {
515
+ /** Raw HTML content for the text block */
516
+ html_content: string;
517
+ }`,
518
+ ```
519
+
520
+ **After (Proper TypeScript interfaces):**
521
+ ```typescript
522
+ // At the top of blockRegistry.ts
523
+ export interface TextBlockContent {
524
+ /** Raw HTML content for the text block */
525
+ html_content: string;
526
+ }
527
+
528
+ // In the registry
529
+ text: {
530
+ type: "text",
531
+ label: "HTML Block",
532
+ initialContent: { html_content: "<p>New text block...</p>" } as TextBlockContent,
533
+ // ... rest of definition (no contentInterface property)
534
+ }
535
+ ```
536
+
537
+ ### Benefits of the New System
538
+
539
+ 1. **Better IDE Support**: Full IntelliSense, syntax highlighting, and error checking
540
+ 2. **Compile-time Type Safety**: TypeScript can catch type errors at build time
541
+ 3. **Cleaner Code**: No more string-based interface definitions
542
+ 4. **Standard TypeScript Patterns**: Uses conventional TypeScript interface declarations
543
+ 5. **Better Refactoring Support**: IDEs can properly rename and refactor interface properties
544
+ 6. **Import/Export Support**: Interfaces can be imported and used across files
545
+
546
+ ### Key Improvements
547
+
548
+ - **Proper Interface Declarations**: TypeScript interfaces are declared at the top of the file
549
+ - **Type-safe Initial Content**: Initial content is properly typed with `as InterfaceName`
550
+ - **Removed contentInterface Property**: No longer needed since we have real interfaces
551
+ - **Enhanced Type Union**: `AllBlockContent` provides a discriminated union of all block types
552
+ - **Better Component Typing**: Components can import and use the actual interfaces
553
+
554
+ ### Breaking Changes
555
+
556
+ - The `contentInterface` property has been removed from `BlockDefinition`
557
+ - The `getContentInterface()` function has been removed
558
+ - The `getAllContentInterfaces()` and `generateSpecificBlockContentUnion()` functions have been removed
559
+ - Components should now import interfaces directly from the registry
560
+
561
+ ### Migration Benefits
562
+
563
+ **Before (Required changes to add a new block):**
564
+ 1. Update `availableBlockTypes` in `blockRegistry.ts`
565
+ 2. Add block definition with string-based interface to `blockRegistry`
566
+ 3. Create editor component with manually defined types
567
+ 4. Create renderer component with manually defined types
568
+
569
+ **After (Required changes to add a new block):**
570
+ 1. Add proper TypeScript interface at the top of `blockRegistry.ts`
571
+ 2. Update `availableBlockTypes` in `blockRegistry.ts`
572
+ 3. Add block definition with typed initial content to `blockRegistry`
573
+ 4. Create editor component importing the interface
574
+ 5. Create renderer component importing the interface
575
+
576
+ **Benefits:**
577
+ - Better type safety and IDE support
578
+ - Standard TypeScript patterns
579
+ - Easier refactoring and maintenance
580
+ - Compile-time error checking
581
+
582
+ ## TypeScript Best Practices
583
+
584
+ ### 1. Import Interfaces from Registry
585
+ ```typescript
586
+ // Always import interfaces from the registry
587
+ import { VideoEmbedBlockContent } from "@/lib/blocks/blockRegistry";
588
+
589
+ // Don't redefine interfaces in components
590
+ type VideoEmbedBlockContent = { // ❌ Don't do this
591
+ url: string;
592
+ // ...
593
+ };
594
+ ```
595
+
596
+ ### 2. Use Type Assertions for Generated Content
597
+ ```typescript
598
+ // Use type assertions when getting generated content
599
+ const defaults = generateDefaultContent("video_embed") as VideoEmbedBlockContent;
600
+ const initialContent = getInitialContent("button") as ButtonBlockContent;
601
+ ```
602
+
603
+ ### 3. Leverage Runtime Validation
604
+ ```typescript
605
+ // Validate content in development
606
+ if (process.env.NODE_ENV === 'development') {
607
+ const validation = validateBlockContent(blockType, content);
608
+ if (!validation.isValid) {
609
+ console.warn(`Invalid ${blockType} content:`, validation.errors);
610
+ }
611
+ }
612
+ ```
613
+
614
+ ### 4. Use Property Definitions for Dynamic UIs
615
+ ```typescript
616
+ // Build forms dynamically from registry schema
617
+ const schema = getContentSchema(blockType);
618
+ const formFields = Object.entries(schema).map(([name, def]) => ({
619
+ name,
620
+ type: def.type,
621
+ required: def.required,
622
+ description: def.description,
623
+ default: def.default,
624
+ }));
625
+ ```
626
+
627
+ ## Best Practices
628
+
629
+ ### 1. Naming Conventions
630
+ - Use PascalCase for component filenames: `VideoEmbedBlockEditor.tsx`
631
+ - Use snake_case for block types: `video_embed`
632
+ - Use descriptive labels: "Video Embed" instead of "Video"
633
+ - Use PascalCase for interface names: `VideoEmbedBlockContent`
634
+
635
+ ### 2. Interface Design
636
+ - Keep interfaces focused and minimal
637
+ - Use optional properties appropriately
638
+ - Include JSDoc comments for better documentation
639
+ - Use union types for constrained values
640
+
641
+ ### 3. Content Structure
642
+ - Keep initial content simple and minimal
643
+ - Use sensible defaults for optional properties
644
+ - Ensure content structure matches TypeScript interfaces
645
+ - Type initial content with `as InterfaceName`
646
+
647
+ ### 4. Component Design
648
+ - Import interfaces from the registry
649
+ - Keep editor components focused on editing functionality
650
+ - Keep renderer components focused on display
651
+ - Use consistent prop interfaces across similar components
652
+
653
+ ### 5. Error Handling
654
+ - Provide fallbacks for missing or invalid content
655
+ - Show helpful error messages in development
656
+ - Gracefully handle dynamic import failures
657
+ - Use runtime validation in development mode
658
+
659
+ ## Summary
660
+
661
+ The enhanced block registry system with proper TypeScript interfaces transforms the development experience by:
662
+
663
+ - **Proper Type Safety**: Real TypeScript interfaces with compile-time checking
664
+ - **Better IDE Support**: Full IntelliSense, syntax highlighting, and refactoring support
665
+ - **Cleaner Code**: Standard TypeScript patterns instead of string-based interfaces
666
+ - **Centralized Everything**: All block information in one place
667
+ - **Runtime Validation**: Content validation against schema definitions
668
+ - **Developer Experience**: Clear patterns and comprehensive tooling
669
+
670
+ This system achieves the "one registry file + component folder" ideal while providing superior TypeScript support and maintaining all existing functionality for validation, documentation, and tooling.