create-nextblock 0.2.78 → 0.8.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 (413) hide show
  1. package/bin/create-nextblock.js +793 -472
  2. package/package.json +1 -2
  3. package/scripts/sync-template.js +18 -1
  4. package/templates/nextblock-template/.browserslistrc +11 -0
  5. package/templates/nextblock-template/.swcrc +30 -30
  6. package/templates/nextblock-template/README.md +23 -114
  7. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -28
  8. package/templates/nextblock-template/app/(auth-pages)/sign-in/page.tsx +50 -25
  9. package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +111 -56
  10. package/templates/nextblock-template/app/(auth-pages)/two-factor/actions.ts +91 -0
  11. package/templates/nextblock-template/app/(auth-pages)/two-factor/components/TwoFactorForm.tsx +118 -0
  12. package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -0
  13. package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -0
  14. package/templates/nextblock-template/app/[slug]/PageClientContent.tsx +48 -28
  15. package/templates/nextblock-template/app/[slug]/page.tsx +63 -6
  16. package/templates/nextblock-template/app/[slug]/page.utils.ts +374 -157
  17. package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -0
  18. package/templates/nextblock-template/app/actions/consent.ts +57 -0
  19. package/templates/nextblock-template/app/actions/formActions.ts +130 -11
  20. package/templates/nextblock-template/app/actions/languageActions.ts +31 -30
  21. package/templates/nextblock-template/app/actions/package-actions.ts +183 -0
  22. package/templates/nextblock-template/app/actions/postActions.ts +146 -48
  23. package/templates/nextblock-template/app/actions/twoFactorEmail.ts +21 -0
  24. package/templates/nextblock-template/app/actions/visualEditingActions.test.ts +179 -0
  25. package/templates/nextblock-template/app/actions/visualEditingActions.ts +345 -0
  26. package/templates/nextblock-template/app/actions.ts +67 -12
  27. package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -0
  28. package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -0
  29. package/templates/nextblock-template/app/api/ai/global-agent/route.ts +965 -0
  30. package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -0
  31. package/templates/nextblock-template/app/api/checkout/route.ts +146 -0
  32. package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -0
  33. package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -0
  34. package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3413 -17
  35. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +7830 -0
  36. package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +35 -0
  37. package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -0
  38. package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -0
  39. package/templates/nextblock-template/app/api/draft/disable/route.ts +25 -0
  40. package/templates/nextblock-template/app/api/draft/route.ts +93 -0
  41. package/templates/nextblock-template/app/api/draft/start/route.ts +77 -0
  42. package/templates/nextblock-template/app/api/media/library/route.ts +65 -0
  43. package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -0
  44. package/templates/nextblock-template/app/api/media/record/route.ts +160 -0
  45. package/templates/nextblock-template/app/api/search/route.ts +43 -0
  46. package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -0
  47. package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -0
  48. package/templates/nextblock-template/app/api/webhooks/freemius/route.ts +34 -0
  49. package/templates/nextblock-template/app/api/webhooks/stripe/route.ts +27 -0
  50. package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +392 -128
  51. package/templates/nextblock-template/app/article/[slug]/page.tsx +179 -127
  52. package/templates/nextblock-template/app/article/[slug]/page.utils.ts +262 -77
  53. package/templates/nextblock-template/app/auth/callback/route.ts +31 -58
  54. package/templates/nextblock-template/app/cart/page.tsx +7 -0
  55. package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -0
  56. package/templates/nextblock-template/app/checkout/page.tsx +52 -0
  57. package/templates/nextblock-template/app/checkout/success/actions.ts +136 -0
  58. package/templates/nextblock-template/app/checkout/success/page.tsx +186 -0
  59. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +163 -33
  60. package/templates/nextblock-template/app/cms/blocks/actions.ts +424 -235
  61. package/templates/nextblock-template/app/cms/blocks/components/BackgroundSelector.tsx +212 -151
  62. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorArea.tsx +41 -20
  63. package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +152 -19
  64. package/templates/nextblock-template/app/cms/blocks/components/BlockTypeCard.tsx +25 -17
  65. package/templates/nextblock-template/app/cms/blocks/components/BlockTypeSelector.tsx +200 -18
  66. package/templates/nextblock-template/app/cms/blocks/components/ColumnEditor.tsx +33 -16
  67. package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -0
  68. package/templates/nextblock-template/app/cms/blocks/components/EditableBlock.tsx +37 -18
  69. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -67
  70. package/templates/nextblock-template/app/cms/blocks/components/SectionConfigPanel.tsx +108 -31
  71. package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -0
  72. package/templates/nextblock-template/app/cms/blocks/editors/FeaturedProductBlockEditor.tsx +31 -0
  73. package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +2 -2
  74. package/templates/nextblock-template/app/cms/blocks/editors/HeadingBlockEditor.tsx +1 -1
  75. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +29 -29
  76. package/templates/nextblock-template/app/cms/blocks/editors/PostsGridBlockEditor.tsx +14 -18
  77. package/templates/nextblock-template/app/cms/blocks/editors/ProductGridBlockEditor.tsx +41 -0
  78. package/templates/nextblock-template/app/cms/blocks/editors/SectionBlockEditor.tsx +318 -118
  79. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +98 -21
  80. package/templates/nextblock-template/app/cms/blocks/editors/VideoEmbedBlockEditor.tsx +1 -1
  81. package/templates/nextblock-template/app/cms/components/ContentLanguageSwitcher.tsx +27 -9
  82. package/templates/nextblock-template/app/cms/components/CopyContentFromLanguage.tsx +1 -1
  83. package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -0
  84. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -0
  85. package/templates/nextblock-template/app/cms/components/CortexGlobalAgentChat.tsx +1507 -0
  86. package/templates/nextblock-template/app/cms/components/DraftStatusActions.tsx +145 -0
  87. package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +244 -0
  88. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +38 -24
  89. package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -0
  90. package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -0
  91. package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -0
  92. package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -0
  93. package/templates/nextblock-template/app/cms/custom-blocks/components/BlockComposer.tsx +1522 -0
  94. package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -0
  95. package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -0
  96. package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -0
  97. package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -0
  98. package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -0
  99. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -98
  100. package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -0
  101. package/templates/nextblock-template/app/cms/dashboard/page.tsx +182 -154
  102. package/templates/nextblock-template/app/cms/import-export/ContentTransferControls.tsx +391 -0
  103. package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -0
  104. package/templates/nextblock-template/app/cms/layout.tsx +29 -10
  105. package/templates/nextblock-template/app/cms/media/UploadFolderContext.tsx +22 -22
  106. package/templates/nextblock-template/app/cms/media/actions.ts +45 -124
  107. package/templates/nextblock-template/app/cms/media/components/DeleteMediaButtonClient.tsx +1 -1
  108. package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
  109. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -64
  110. package/templates/nextblock-template/app/cms/media/components/MediaPickerDialog.tsx +227 -158
  111. package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +101 -89
  112. package/templates/nextblock-template/app/cms/media/page.tsx +1 -1
  113. package/templates/nextblock-template/app/cms/navigation/components/NavigationItemForm.tsx +2 -2
  114. package/templates/nextblock-template/app/cms/orders/[id]/MarkPaidButton.tsx +44 -0
  115. package/templates/nextblock-template/app/cms/orders/[id]/page.tsx +16 -0
  116. package/templates/nextblock-template/app/cms/orders/actions.ts +201 -0
  117. package/templates/nextblock-template/app/cms/orders/page.tsx +20 -0
  118. package/templates/nextblock-template/app/cms/orders/types.ts +20 -0
  119. package/templates/nextblock-template/app/cms/pages/[id]/edit/EditPageClient.tsx +156 -121
  120. package/templates/nextblock-template/app/cms/pages/[id]/edit/page.tsx +79 -26
  121. package/templates/nextblock-template/app/cms/pages/actions.ts +54 -38
  122. package/templates/nextblock-template/app/cms/pages/components/DeletePageButtonClient.tsx +1 -1
  123. package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +267 -116
  124. package/templates/nextblock-template/app/cms/pages/page.tsx +25 -18
  125. package/templates/nextblock-template/app/cms/payments/page.tsx +16 -0
  126. package/templates/nextblock-template/app/cms/posts/[id]/edit/page.tsx +132 -90
  127. package/templates/nextblock-template/app/cms/posts/actions.ts +71 -72
  128. package/templates/nextblock-template/app/cms/posts/components/DeletePostButtonClient.tsx +1 -1
  129. package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +256 -245
  130. package/templates/nextblock-template/app/cms/posts/new/page.tsx +1 -1
  131. package/templates/nextblock-template/app/cms/posts/page.tsx +20 -13
  132. package/templates/nextblock-template/app/cms/products/ClientNotionEditor.tsx +16 -0
  133. package/templates/nextblock-template/app/cms/products/ProductFormClientShell.tsx +56 -0
  134. package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +292 -0
  135. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -0
  136. package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -0
  137. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -0
  138. package/templates/nextblock-template/app/cms/products/new/page.tsx +143 -0
  139. package/templates/nextblock-template/app/cms/products/page.tsx +42 -0
  140. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -0
  141. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -0
  142. package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -0
  143. package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -0
  144. package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -0
  145. package/templates/nextblock-template/app/cms/revisions/RevisionHistoryButton.tsx +2 -2
  146. package/templates/nextblock-template/app/cms/revisions/actions.ts +285 -285
  147. package/templates/nextblock-template/app/cms/revisions/service.ts +19 -16
  148. package/templates/nextblock-template/app/cms/revisions/utils.ts +8 -3
  149. package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -0
  150. package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -0
  151. package/templates/nextblock-template/app/cms/settings/bot-protection/actions.ts +93 -0
  152. package/templates/nextblock-template/app/cms/settings/bot-protection/components/BotProtectionForm.tsx +129 -0
  153. package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -0
  154. package/templates/nextblock-template/app/cms/settings/copyright/actions.ts +1 -1
  155. package/templates/nextblock-template/app/cms/settings/copyright/components/CopyrightForm.tsx +2 -2
  156. package/templates/nextblock-template/app/cms/settings/copyright/page.tsx +1 -1
  157. package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +496 -0
  158. package/templates/nextblock-template/app/cms/settings/cortex-ai/StoredCortexAiSettingsClient.tsx +410 -0
  159. package/templates/nextblock-template/app/cms/settings/cortex-ai/actions.ts +248 -0
  160. package/templates/nextblock-template/app/cms/settings/cortex-ai/page.tsx +80 -0
  161. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -0
  162. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -0
  163. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -0
  164. package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +203 -44
  165. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -242
  166. package/templates/nextblock-template/app/cms/settings/global-css/actions.ts +65 -0
  167. package/templates/nextblock-template/app/cms/settings/global-css/components/GlobalCssForm.tsx +46 -0
  168. package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +24 -0
  169. package/templates/nextblock-template/app/cms/settings/languages/components/DeleteLanguageButton.tsx +1 -1
  170. package/templates/nextblock-template/app/cms/settings/languages/components/LanguageForm.tsx +2 -2
  171. package/templates/nextblock-template/app/cms/settings/languages/page.tsx +1 -1
  172. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  173. package/templates/nextblock-template/app/cms/settings/logos/actions.ts +82 -6
  174. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -0
  175. package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -18
  176. package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +20 -16
  177. package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -0
  178. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  179. package/templates/nextblock-template/app/cms/settings/logos/page.tsx +120 -82
  180. package/templates/nextblock-template/app/cms/settings/logos/types.ts +8 -8
  181. package/templates/nextblock-template/app/cms/settings/packages/activation-form.tsx +84 -0
  182. package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -0
  183. package/templates/nextblock-template/app/cms/settings/packages/page.tsx +49 -0
  184. package/templates/nextblock-template/app/cms/settings/privacy/actions.ts +53 -0
  185. package/templates/nextblock-template/app/cms/settings/privacy/components/PrivacyForm.tsx +196 -0
  186. package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +26 -0
  187. package/templates/nextblock-template/app/cms/settings/security/actions.ts +251 -0
  188. package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +453 -0
  189. package/templates/nextblock-template/app/cms/settings/security/page.tsx +13 -0
  190. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -0
  191. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -0
  192. package/templates/nextblock-template/app/cms/users/[id]/edit/page.tsx +28 -23
  193. package/templates/nextblock-template/app/cms/users/actions.ts +105 -40
  194. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +1 -1
  195. package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +65 -152
  196. package/templates/nextblock-template/app/cms/users/page.tsx +15 -10
  197. package/templates/nextblock-template/app/globals.css +9 -0
  198. package/templates/nextblock-template/app/layout.tsx +372 -120
  199. package/templates/nextblock-template/app/lib/seo.test.ts +52 -0
  200. package/templates/nextblock-template/app/lib/seo.ts +279 -0
  201. package/templates/nextblock-template/app/lib/site-settings.ts +87 -0
  202. package/templates/nextblock-template/app/lib/sitemap-utils.ts +224 -39
  203. package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -0
  204. package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -0
  205. package/templates/nextblock-template/app/lib/ucp/server.ts +1914 -0
  206. package/templates/nextblock-template/app/page.tsx +165 -73
  207. package/templates/nextblock-template/app/product/[slug]/page.tsx +433 -0
  208. package/templates/nextblock-template/app/profile/ProfileAccountSidebar.tsx +73 -0
  209. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -0
  210. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -0
  211. package/templates/nextblock-template/app/profile/account-data.ts +37 -0
  212. package/templates/nextblock-template/app/profile/account-links.ts +22 -0
  213. package/templates/nextblock-template/app/profile/account-types.ts +11 -0
  214. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -0
  215. package/templates/nextblock-template/app/profile/orders/[id]/CustomerOrderDetailPageClient.tsx +79 -0
  216. package/templates/nextblock-template/app/profile/orders/[id]/page.tsx +32 -0
  217. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -0
  218. package/templates/nextblock-template/app/profile/page.tsx +51 -0
  219. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -0
  220. package/templates/nextblock-template/app/profile/password/actions.ts +59 -0
  221. package/templates/nextblock-template/app/profile/password/page.tsx +27 -0
  222. package/templates/nextblock-template/app/providers.tsx +55 -17
  223. package/templates/nextblock-template/app/robots.txt/route.ts +11 -1
  224. package/templates/nextblock-template/app/sitemap.ts +128 -0
  225. package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -0
  226. package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -0
  227. package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -0
  228. package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -0
  229. package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -0
  230. package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -0
  231. package/templates/nextblock-template/components/AppShell.tsx +154 -0
  232. package/templates/nextblock-template/components/BlockRenderer.tsx +210 -64
  233. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -0
  234. package/templates/nextblock-template/components/CartTranslator.tsx +210 -0
  235. package/templates/nextblock-template/components/CurrentContentSetter.tsx +25 -0
  236. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -0
  237. package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -0
  238. package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -0
  239. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -0
  240. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -0
  241. package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -0
  242. package/templates/nextblock-template/components/GitHubLoginButton.tsx +36 -0
  243. package/templates/nextblock-template/components/GlobalSearch.tsx +557 -0
  244. package/templates/nextblock-template/components/Header.tsx +49 -41
  245. package/templates/nextblock-template/components/LanguageSwitcher.tsx +55 -32
  246. package/templates/nextblock-template/components/ResponsiveNav.tsx +138 -43
  247. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -8
  248. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -55
  249. package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +42 -37
  250. package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +6 -2
  251. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -0
  252. package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -0
  253. package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -84
  254. package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +17 -0
  255. package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +19 -0
  256. package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +262 -8
  257. package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +22 -0
  258. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +320 -37
  259. package/templates/nextblock-template/components/blocks/renderers/HeadingBlockRenderer.tsx +11 -8
  260. package/templates/nextblock-template/components/blocks/renderers/ImageBlockRenderer.tsx +12 -3
  261. package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +18 -13
  262. package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +90 -0
  263. package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +31 -0
  264. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +424 -55
  265. package/templates/nextblock-template/components/blocks/renderers/SectionSlider.tsx +137 -0
  266. package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -0
  267. package/templates/nextblock-template/components/blocks/renderers/TextBlockRenderer.tsx +37 -22
  268. package/templates/nextblock-template/components/blocks/renderers/VideoEmbedBlockRenderer.tsx +23 -15
  269. package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +1 -3
  270. package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +1 -3
  271. package/templates/nextblock-template/components/blocks/types.ts +7 -6
  272. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  273. package/templates/nextblock-template/components/form-message.tsx +32 -26
  274. package/templates/nextblock-template/components/header-auth.tsx +69 -17
  275. package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +127 -0
  276. package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +59 -0
  277. package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -0
  278. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -0
  279. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +464 -0
  280. package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
  281. package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -0
  282. package/templates/nextblock-template/components/visual-editing/NextblockVisualEditing.tsx +1172 -0
  283. package/templates/nextblock-template/context/AuthContext.tsx +23 -90
  284. package/templates/nextblock-template/context/CurrentContentContext.tsx +10 -4
  285. package/templates/nextblock-template/context/LanguageContext.tsx +16 -16
  286. package/templates/nextblock-template/context/language-rest-client.ts +31 -0
  287. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -0
  288. package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -0
  289. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -0
  290. package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +252 -0
  291. package/templates/nextblock-template/docs/05-DEVELOPER-GUIDE.md +238 -0
  292. package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +125 -0
  293. package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -0
  294. package/templates/nextblock-template/docs/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +1319 -0
  295. package/templates/nextblock-template/docs/09-LIVE-DRAFT-MODE.md +104 -0
  296. package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -0
  297. package/templates/nextblock-template/docs/README.md +34 -0
  298. package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12507 -0
  299. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -14
  300. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -0
  301. package/templates/nextblock-template/index.d.ts +2 -0
  302. package/templates/nextblock-template/lib/ai-block-generation.ts +339 -0
  303. package/templates/nextblock-template/lib/ai-client.ts +247 -0
  304. package/templates/nextblock-template/lib/ai-config.ts +81 -0
  305. package/templates/nextblock-template/lib/ai-cortex-widget-builder.ts +125 -0
  306. package/templates/nextblock-template/lib/ai-global-agent-custom-block-tools.ts +363 -0
  307. package/templates/nextblock-template/lib/ai-global-agent-db-tools.test.ts +405 -0
  308. package/templates/nextblock-template/lib/ai-global-agent-db-tools.ts +1228 -0
  309. package/templates/nextblock-template/lib/ai-global-agent-ecommerce.ts +5 -0
  310. package/templates/nextblock-template/lib/ai-global-agent-tools-stats.test.ts +223 -0
  311. package/templates/nextblock-template/lib/ai-global-agent-tools.test.ts +2183 -0
  312. package/templates/nextblock-template/lib/ai-global-agent-tools.ts +4807 -0
  313. package/templates/nextblock-template/lib/ai-key-crypto.test.ts +70 -0
  314. package/templates/nextblock-template/lib/ai-key-crypto.ts +132 -0
  315. package/templates/nextblock-template/lib/ai-model-catalog.test.ts +49 -0
  316. package/templates/nextblock-template/lib/ai-model-catalog.ts +41 -0
  317. package/templates/nextblock-template/lib/ai-model-registry.test.ts +231 -0
  318. package/templates/nextblock-template/lib/ai-model-registry.ts +522 -0
  319. package/templates/nextblock-template/lib/auth/cookies.ts +47 -0
  320. package/templates/nextblock-template/lib/auth/crypto.ts +42 -0
  321. package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -0
  322. package/templates/nextblock-template/lib/auth/twoFactor.ts +167 -0
  323. package/templates/nextblock-template/lib/auth-redirects.ts +46 -0
  324. package/templates/nextblock-template/lib/blocks/FeaturedProductBlock.tsx +94 -0
  325. package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +137 -0
  326. package/templates/nextblock-template/lib/blocks/README.md +13 -670
  327. package/templates/nextblock-template/lib/blocks/blockRegistry.ts +138 -56
  328. package/templates/nextblock-template/lib/blocks/blockTypes.ts +18 -0
  329. package/templates/nextblock-template/lib/blocks/ecommerce-block-schemas.ts +31 -0
  330. package/templates/nextblock-template/lib/cms-transfer/csv.test.ts +77 -0
  331. package/templates/nextblock-template/lib/cms-transfer/csv.ts +399 -0
  332. package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -0
  333. package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -0
  334. package/templates/nextblock-template/lib/cortex-widget-registry.test.ts +199 -0
  335. package/templates/nextblock-template/lib/cortex-widget-registry.ts +88 -0
  336. package/templates/nextblock-template/lib/cortex-widget-schema.test.tsx +237 -0
  337. package/templates/nextblock-template/lib/cortex-widget-schema.ts +393 -0
  338. package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -0
  339. package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -0
  340. package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -0
  341. package/templates/nextblock-template/lib/custom-block-r2-upload.ts +68 -0
  342. package/templates/nextblock-template/lib/custom-block-relation-registry.ts +256 -0
  343. package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -0
  344. package/templates/nextblock-template/lib/custom-block-relations.ts +279 -0
  345. package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -0
  346. package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -0
  347. package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -0
  348. package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -0
  349. package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -0
  350. package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -0
  351. package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -0
  352. package/templates/nextblock-template/lib/full-backup/server.ts +743 -0
  353. package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +45 -0
  354. package/templates/nextblock-template/lib/posts/readTime.ts +60 -0
  355. package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -0
  356. package/templates/nextblock-template/lib/privacy/settings.ts +103 -0
  357. package/templates/nextblock-template/lib/privacy/types.ts +67 -0
  358. package/templates/nextblock-template/lib/promotions/server.test.ts +74 -0
  359. package/templates/nextblock-template/lib/promotions/server.ts +741 -0
  360. package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -0
  361. package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -0
  362. package/templates/nextblock-template/lib/search/server.ts +585 -0
  363. package/templates/nextblock-template/lib/search/types.ts +27 -0
  364. package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -0
  365. package/templates/nextblock-template/lib/visual-editing/draft-content.ts +380 -0
  366. package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -0
  367. package/templates/nextblock-template/lib/visual-editing/draft-route.ts +82 -0
  368. package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -0
  369. package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -0
  370. package/templates/nextblock-template/lib/visual-editing/mutations.ts +190 -0
  371. package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -0
  372. package/templates/nextblock-template/lib/visual-editing/product-drafts.ts +511 -0
  373. package/templates/nextblock-template/lib/visual-editing/types.ts +122 -0
  374. package/templates/nextblock-template/lib/zod-config.ts +5 -0
  375. package/templates/nextblock-template/next.config.js +190 -66
  376. package/templates/nextblock-template/package.json +34 -30
  377. package/templates/nextblock-template/proxy.ts +435 -253
  378. package/templates/nextblock-template/public/images/NBcover.webp +0 -0
  379. package/templates/nextblock-template/public/images/cap.webp +0 -0
  380. package/templates/nextblock-template/public/images/commerce-plan.webp +0 -0
  381. package/templates/nextblock-template/public/images/commerce-square.webp +0 -0
  382. package/templates/nextblock-template/public/images/commerce-wide.webp +0 -0
  383. package/templates/nextblock-template/public/images/cortex-ai-square.webp +0 -0
  384. package/templates/nextblock-template/public/images/cortex-ai.webp +0 -0
  385. package/templates/nextblock-template/public/images/extensibility.webp +0 -0
  386. package/templates/nextblock-template/public/images/goals.webp +0 -0
  387. package/templates/nextblock-template/public/images/included.webp +0 -0
  388. package/templates/nextblock-template/public/images/nx-graph.webp +0 -0
  389. package/templates/nextblock-template/public/images/pants.webp +0 -0
  390. package/templates/nextblock-template/public/images/t-shirt.webp +0 -0
  391. package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -0
  392. package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +100 -0
  393. package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -0
  394. package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -0
  395. package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -0
  396. package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -0
  397. package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -0
  398. package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -0
  399. package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -0
  400. package/templates/nextblock-template/tailwind.config.js +1 -0
  401. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -0
  402. package/templates/nextblock-template/tools/deploy-supabase.js +69 -71
  403. package/templates/nextblock-template/tsconfig.json +52 -66
  404. package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
  405. package/templates/nextblock-template/types/jsdom.d.ts +6 -0
  406. package/templates/nextblock-template/app/force-styles.tsx +0 -31
  407. package/templates/nextblock-template/app/sitemap.xml/route.ts +0 -63
  408. package/templates/nextblock-template/components/blocks/renderers/HeroBlockRenderer.tsx +0 -273
  409. package/templates/nextblock-template/docs/How to Create a Custom Block.md +0 -149
  410. package/templates/nextblock-template/docs/cms-application-overview.md +0 -56
  411. package/templates/nextblock-template/docs/cms-architecture-overview.md +0 -73
  412. package/templates/nextblock-template/docs/files-structure.md +0 -426
  413. package/templates/nextblock-template/docs/tiptap-bundle-optimization-summary.md +0 -174
@@ -3,7 +3,8 @@
3
3
  import React from 'react';
4
4
  import { Label } from "@nextblock-cms/ui";
5
5
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@nextblock-cms/ui";
6
- import type { SectionBlockContent } from "@/lib/blocks/blockRegistry";
6
+ import { Checkbox, Input } from "@nextblock-cms/ui";
7
+ import { SectionBlockContent } from '../../../../lib/blocks/blockRegistry';
7
8
  import BackgroundSelector from './BackgroundSelector';
8
9
 
9
10
  interface SectionConfigPanelProps {
@@ -55,18 +56,18 @@ export default function SectionConfigPanel({ content, onChange }: SectionConfigP
55
56
  };
56
57
 
57
58
  return (
58
- <div className="space-y-4">
59
- <div className="flex items-center justify-between">
60
- <h3 className="text-lg font-medium text-gray-900 dark:text-gray-100">Section Configuration</h3>
59
+ <div className="rounded-lg border bg-card p-4 shadow-sm space-y-4">
60
+ <div className="flex items-start justify-between gap-4 border-b border-muted/50 pb-2 mb-1">
61
+ <h2 className="text-sm font-bold tracking-tight text-gray-900 dark:text-gray-100">Section Configuration</h2>
61
62
  </div>
62
63
 
63
- <>
64
- <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
64
+ <div className="space-y-4 pt-1">
65
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
65
66
  {/* Container Type */}
66
- <div className="space-y-2">
67
- <Label htmlFor="container-type">Container Type</Label>
67
+ <div className="space-y-1.5">
68
+ <Label htmlFor="container-type" className="text-sm font-medium text-gray-700 dark:text-gray-300">Container Type</Label>
68
69
  <Select value={content.container_type} onValueChange={handleContainerTypeChange}>
69
- <SelectTrigger id="container-type">
70
+ <SelectTrigger id="container-type" className="h-9 text-sm">
70
71
  <SelectValue placeholder="Select container type" />
71
72
  </SelectTrigger>
72
73
  <SelectContent>
@@ -80,10 +81,10 @@ export default function SectionConfigPanel({ content, onChange }: SectionConfigP
80
81
  </div>
81
82
 
82
83
  {/* Desktop Columns */}
83
- <div className="space-y-2">
84
- <Label htmlFor="desktop-columns">Desktop Columns</Label>
84
+ <div className="space-y-1.5">
85
+ <Label htmlFor="desktop-columns" className="text-sm font-medium text-gray-700 dark:text-gray-300">Desktop Columns</Label>
85
86
  <Select value={content.responsive_columns?.desktop?.toString()} onValueChange={handleDesktopColumnsChange}>
86
- <SelectTrigger id="desktop-columns">
87
+ <SelectTrigger id="desktop-columns" className="h-9 text-sm">
87
88
  <SelectValue placeholder="Select columns" />
88
89
  </SelectTrigger>
89
90
  <SelectContent>
@@ -96,10 +97,10 @@ export default function SectionConfigPanel({ content, onChange }: SectionConfigP
96
97
  </div>
97
98
 
98
99
  {/* Column Gap */}
99
- <div className="space-y-2">
100
- <Label htmlFor="column-gap">Column Gap</Label>
100
+ <div className="space-y-1.5">
101
+ <Label htmlFor="column-gap" className="text-sm font-medium text-gray-700 dark:text-gray-300">Column Gap</Label>
101
102
  <Select value={content.column_gap} onValueChange={handleColumnGapChange}>
102
- <SelectTrigger id="column-gap">
103
+ <SelectTrigger id="column-gap" className="h-9 text-sm">
103
104
  <SelectValue placeholder="Select gap" />
104
105
  </SelectTrigger>
105
106
  <SelectContent>
@@ -113,13 +114,13 @@ export default function SectionConfigPanel({ content, onChange }: SectionConfigP
113
114
  </div>
114
115
 
115
116
  {/* Vertical Alignment */}
116
- <div className="space-y-2">
117
- <Label htmlFor="vertical-alignment">Vertical Alignment</Label>
117
+ <div className="space-y-1.5">
118
+ <Label htmlFor="vertical-alignment" className="text-sm font-medium text-gray-700 dark:text-gray-300">Vertical Alignment</Label>
118
119
  <Select
119
120
  value={content.vertical_alignment || 'start'}
120
121
  onValueChange={(value: any) => onChange({ ...content, vertical_alignment: value })}
121
122
  >
122
- <SelectTrigger id="vertical-alignment">
123
+ <SelectTrigger id="vertical-alignment" className="h-9 text-sm">
123
124
  <SelectValue placeholder="Select alignment" />
124
125
  </SelectTrigger>
125
126
  <SelectContent>
@@ -132,20 +133,96 @@ export default function SectionConfigPanel({ content, onChange }: SectionConfigP
132
133
  </div>
133
134
  </div>
134
135
 
135
- {/* Background Configuration */}
136
- <div className="space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700">
137
- <h4 className="text-md font-medium text-gray-900 dark:text-gray-100">Background</h4>
138
- <BackgroundSelector
139
- background={content.background || { type: 'none' }}
140
- onChange={(newBackground) => {
141
- onChange({
142
- ...content,
143
- background: newBackground,
144
- });
145
- }}
146
- />
136
+ {/* Hero & Slider Toggles & Settings in a compact single flex flow */}
137
+ <div className="flex flex-wrap items-center gap-x-6 gap-y-3 pt-3 border-t border-muted/50">
138
+ {/* 1. Hero Section */}
139
+ <div className="flex items-center space-x-2">
140
+ <Checkbox
141
+ id="is-hero"
142
+ checked={content.is_hero || false}
143
+ onCheckedChange={(checked) => onChange({ ...content, is_hero: !!checked })}
144
+ />
145
+ <Label htmlFor="is-hero" className="text-sm font-medium cursor-pointer text-gray-700 dark:text-gray-300">
146
+ Hero Section (Prioritized image loading)
147
+ </Label>
148
+ </div>
149
+
150
+ {/* 2. Enable Slider */}
151
+ <div className="flex items-center space-x-2">
152
+ <Checkbox
153
+ id="slider"
154
+ checked={content.slider || false}
155
+ onCheckedChange={(checked) => onChange({ ...content, slider: !!checked })}
156
+ />
157
+ <Label htmlFor="slider" className="text-sm font-medium cursor-pointer text-gray-700 dark:text-gray-300">
158
+ Enable Slider (Carousel layout)
159
+ </Label>
160
+ </div>
161
+
162
+ <div className="h-4 w-px bg-muted/60 hidden md:block" />
163
+
164
+ {/* 3. Enable Autoplay */}
165
+ <div className={`flex items-center space-x-2 transition-opacity duration-200 ${!content.slider ? 'opacity-40' : ''}`}>
166
+ <Checkbox
167
+ id="autoplay"
168
+ disabled={!content.slider}
169
+ checked={content.slider && (content.autoplay || false)}
170
+ onCheckedChange={(checked) => onChange({ ...content, autoplay: !!checked })}
171
+ className={!content.slider ? 'cursor-not-allowed' : ''}
172
+ />
173
+ <Label
174
+ htmlFor="autoplay"
175
+ className={`text-sm font-medium cursor-pointer ${!content.slider ? 'text-muted-foreground/60 cursor-not-allowed' : 'text-gray-700 dark:text-gray-300'}`}
176
+ >
177
+ Enable Autoplay
178
+ </Label>
179
+ </div>
180
+
181
+ {/* 4. Autoplay Interval */}
182
+ <div className={`flex items-center gap-x-2 transition-opacity duration-200 ${(!content.slider || !content.autoplay) ? 'opacity-40' : ''}`}>
183
+ <Label
184
+ htmlFor="timeframe"
185
+ className={`text-sm font-medium whitespace-nowrap ${(!content.slider || !content.autoplay) ? 'text-muted-foreground/60 cursor-not-allowed' : 'text-gray-700 dark:text-gray-300'}`}
186
+ >
187
+ Interval:
188
+ </Label>
189
+ <div className="flex items-center space-x-1.5">
190
+ <Input
191
+ id="timeframe"
192
+ type="number"
193
+ min={1}
194
+ disabled={!content.slider || !content.autoplay}
195
+ value={content.timeframe !== undefined ? content.timeframe : 5}
196
+ onChange={(e) => {
197
+ const val = parseInt(e.target.value);
198
+ onChange({ ...content, timeframe: isNaN(val) || val <= 0 ? 5 : val });
199
+ }}
200
+ placeholder="5"
201
+ className={`h-9 w-16 text-center text-sm ${(!content.slider || !content.autoplay) ? 'cursor-not-allowed bg-muted/40' : ''}`}
202
+ />
203
+ <span className={`text-sm text-muted-foreground ${(!content.slider || !content.autoplay) ? 'cursor-not-allowed' : ''}`}>
204
+ seconds
205
+ </span>
206
+ </div>
207
+ </div>
147
208
  </div>
148
- </>
209
+
210
+ {/* Background Configuration */}
211
+ {!content.slider && (
212
+ <div className="space-y-3 pt-3 border-t border-muted/50">
213
+ <h3 className="text-sm font-bold text-gray-900 dark:text-gray-100">Background</h3>
214
+ <BackgroundSelector
215
+ background={content.background || { type: 'none' }}
216
+ onChange={(newBackground) => {
217
+ onChange({
218
+ ...content,
219
+ background: newBackground,
220
+ });
221
+ }}
222
+ />
223
+ </div>
224
+ )}
225
+ </div>
149
226
  </div>
150
227
  );
151
228
  }
@@ -0,0 +1,167 @@
1
+ // app/cms/blocks/editors/DynamicCustomBlockEditor.tsx
2
+ "use client";
3
+
4
+ import React, { useEffect, useState } from "react";
5
+ import { Label } from "@nextblock-cms/ui";
6
+ import { Input } from "@nextblock-cms/ui";
7
+ import { Textarea } from "@nextblock-cms/ui";
8
+ import { BlockEditorProps } from "../components/BlockEditorModal";
9
+ import { DBRelationSelect } from "../../custom-blocks/components/DBRelationSelect";
10
+ import { ImageR2Picker } from "../../custom-blocks/components/ImageR2Picker";
11
+ import type { CustomBlockField } from "@nextblock-cms/utils";
12
+
13
+ type CustomBlockDefinitionResponse = {
14
+ id: string;
15
+ slug: string;
16
+ name: string;
17
+ fields: CustomBlockField[];
18
+ };
19
+
20
+ // Fallback raw-JSON editor for block types with no custom definition.
21
+ function JsonFallbackEditor({
22
+ content,
23
+ onChange,
24
+ }: {
25
+ content: Record<string, any>;
26
+ onChange: (next: any) => void;
27
+ }) {
28
+ const [value, setValue] = useState(() => JSON.stringify(content ?? {}, null, 2));
29
+ const [error, setError] = useState<string | null>(null);
30
+
31
+ useEffect(() => {
32
+ setValue(JSON.stringify(content ?? {}, null, 2));
33
+ setError(null);
34
+ }, [content]);
35
+
36
+ return (
37
+ <div className="space-y-2 p-4 border rounded-xl bg-card/50">
38
+ <Label htmlFor="custom-block-json-fallback" className="text-xs font-semibold">
39
+ Block Content (JSON)
40
+ </Label>
41
+ <Textarea
42
+ id="custom-block-json-fallback"
43
+ value={value}
44
+ spellCheck={false}
45
+ className="min-h-[320px] font-mono text-xs"
46
+ onChange={(e) => {
47
+ const next = e.target.value;
48
+ setValue(next);
49
+ try {
50
+ onChange(JSON.parse(next));
51
+ setError(null);
52
+ } catch (parseError) {
53
+ setError(parseError instanceof Error ? parseError.message : "Invalid JSON");
54
+ }
55
+ }}
56
+ />
57
+ {error && <p className="text-xs text-destructive">{error}</p>}
58
+ </div>
59
+ );
60
+ }
61
+
62
+ export default function DynamicCustomBlockEditor({
63
+ block,
64
+ content,
65
+ onChange,
66
+ }: BlockEditorProps<Record<string, any>>) {
67
+ const [definition, setDefinition] = useState<CustomBlockDefinitionResponse | null>(null);
68
+ const [loading, setLoading] = useState(true);
69
+
70
+ useEffect(() => {
71
+ async function loadDefinition() {
72
+ try {
73
+ const response = await fetch("/api/custom-blocks/editor-definitions");
74
+ if (response.ok) {
75
+ const payload = await response.json();
76
+ const blockType = block.block_type || (block as any).type;
77
+ const def = payload.definitions?.find((d: any) => d.slug === blockType);
78
+ if (def) {
79
+ setDefinition(def);
80
+ }
81
+ }
82
+ } catch (err) {
83
+ console.error("Failed to load custom block definition for editor:", err);
84
+ } finally {
85
+ setLoading(false);
86
+ }
87
+ }
88
+ loadDefinition();
89
+ }, [block.block_type, (block as any).type]);
90
+
91
+ if (loading) {
92
+ return <div className="p-4 text-center text-sm text-muted-foreground">Loading custom block settings...</div>;
93
+ }
94
+
95
+ // No matching custom block definition (e.g. a non-custom block type routed
96
+ // here as a fallback). Degrade gracefully to a raw JSON editor instead of an
97
+ // error so content stays editable.
98
+ if (!definition) {
99
+ return <JsonFallbackEditor content={content} onChange={onChange} />;
100
+ }
101
+
102
+ const handleFieldChange = (key: string, val: any) => {
103
+ onChange({ ...content, [key]: val });
104
+ };
105
+
106
+ return (
107
+ <div className="space-y-4 p-4 border rounded-xl bg-card/50">
108
+ <div className="pb-2 border-b">
109
+ <h4 className="text-sm font-bold text-slate-800 dark:text-slate-200">{definition.name}</h4>
110
+ <p className="text-xs text-muted-foreground">Custom Block Properties</p>
111
+ </div>
112
+
113
+ {definition.fields.map((field) => {
114
+ const fieldVal = content[field.key];
115
+ const fieldId = `custom-block-editor-${definition.slug}-${field.key}`;
116
+
117
+ return (
118
+ <div key={field.key} className="space-y-1 pt-2">
119
+ <Label htmlFor={fieldId} className="text-xs font-semibold text-slate-700 dark:text-slate-300">
120
+ {field.label} {field.required && <span className="text-destructive">*</span>}
121
+ </Label>
122
+
123
+ {field.type === "rich-text" ? (
124
+ <Textarea
125
+ id={fieldId}
126
+ value={fieldVal || ""}
127
+ onChange={(e) => handleFieldChange(field.key, e.target.value)}
128
+ placeholder={field.placeholder}
129
+ className="min-h-24 text-sm"
130
+ />
131
+ ) : field.type === "image_r2" ? (
132
+ <ImageR2Picker
133
+ value={fieldVal && typeof fieldVal === "object" && "url" in fieldVal ? fieldVal : null}
134
+ onChange={(val) => handleFieldChange(field.key, val)}
135
+ accept={field.accept}
136
+ maxBytes={field.max_bytes}
137
+ />
138
+ ) : field.type === "db_relation" ? (
139
+ <DBRelationSelect
140
+ table={field.table}
141
+ value={
142
+ field.multiple
143
+ ? Array.isArray(fieldVal) ? fieldVal.map(String) : []
144
+ : fieldVal ? String(fieldVal) : null
145
+ }
146
+ onChange={(val) => handleFieldChange(field.key, val)}
147
+ multiple={field.multiple}
148
+ displayColumn={field.display_column}
149
+ valueColumn={field.value_column}
150
+ filters={field.filters}
151
+ />
152
+ ) : (
153
+ <Input
154
+ id={fieldId}
155
+ type="text"
156
+ value={fieldVal || ""}
157
+ onChange={(e) => handleFieldChange(field.key, e.target.value)}
158
+ placeholder={field.placeholder}
159
+ className="h-9"
160
+ />
161
+ )}
162
+ </div>
163
+ );
164
+ })}
165
+ </div>
166
+ );
167
+ }
@@ -0,0 +1,31 @@
1
+ "use client";
2
+
3
+ import { Label, Input } from "@nextblock-cms/ui";
4
+ import { BlockEditorProps } from '../components/BlockEditorModal';
5
+ import { FeaturedProductBlockContent } from '../../../../lib/blocks/ecommerce-block-schemas';
6
+
7
+ export default function FeaturedProductBlockEditor({ content, onChange }: BlockEditorProps<Partial<FeaturedProductBlockContent>>) {
8
+
9
+ const handleChange = (field: keyof FeaturedProductBlockContent, value: any) => {
10
+ onChange({ ...content, [field]: value });
11
+ };
12
+
13
+ return (
14
+ <div className="space-y-4 p-3 border-t mt-2">
15
+ <div>
16
+ <Label htmlFor="fp-id">Product ID (UUID)</Label>
17
+ <Input
18
+ id="fp-id"
19
+ value={content.productId || ""}
20
+ onChange={(e) => handleChange('productId', e.target.value)}
21
+ placeholder="e.g. 123e4567-e89b..."
22
+ className="mt-1"
23
+ />
24
+ <p className="text-xs text-muted-foreground mt-1">
25
+ Copy the Product ID from the Products table.
26
+ </p>
27
+ </div>
28
+ {/* Additional fields like showBackground can be added here */}
29
+ </div>
30
+ );
31
+ }
@@ -10,8 +10,8 @@ import { Input } from '@nextblock-cms/ui';
10
10
  import { Label } from '@nextblock-cms/ui';
11
11
  import { Checkbox } from '@nextblock-cms/ui';
12
12
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@nextblock-cms/ui';
13
- import { BlockEditorProps } from '@/app/cms/blocks/components/BlockEditorModal';
14
- import type { FormBlockContent, FormField, FormFieldOption } from '@/lib/blocks/blockRegistry';
13
+ import { BlockEditorProps } from '../components/BlockEditorModal';
14
+ import { FormBlockContent, FormField, FormFieldOption } from '../../../../lib/blocks/blockRegistry';
15
15
 
16
16
  // Sub-component for a single editable form field in the editor
17
17
  const SortableFormField = ({ field, index, onUpdate, onDelete }: { field: FormField, index: number, onUpdate: (index: number, field: FormField) => void, onDelete: (index: number) => void }) => {
@@ -5,7 +5,7 @@ import React from "react";
5
5
  import { Label } from "@nextblock-cms/ui";
6
6
  import { Input } from "@nextblock-cms/ui";
7
7
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@nextblock-cms/ui";
8
- import type { HeadingBlockContent } from "@/lib/blocks/blockRegistry";
8
+ import { HeadingBlockContent } from '../../../../lib/blocks/blockRegistry';
9
9
  import { BlockEditorProps } from '../components/BlockEditorModal';
10
10
 
11
11
  export default function HeadingBlockEditor({ content, onChange }: BlockEditorProps<Partial<HeadingBlockContent>>) {
@@ -18,11 +18,10 @@ export type ImageBlockContent = {
18
18
  height: number | null;
19
19
  blur_data_url: string | null;
20
20
  };
21
- import { ImageIcon, X as XIcon } from 'lucide-react';
22
- import MediaPickerDialog from "@/app/cms/media/components/MediaPickerDialog"; // Import the upload form
23
- import { BlockEditorProps } from '../components/BlockEditorModal';
24
-
25
- const R2_BASE_URL = process.env.NEXT_PUBLIC_R2_BASE_URL || "";
21
+ import { ImageIcon, X as XIcon } from 'lucide-react';
22
+ import MediaPickerDialog from "../../media/components/MediaPickerDialog"; // Import the upload form
23
+ import { BlockEditorProps } from '../components/BlockEditorModal';
24
+ import { resolveMediaUrl } from '../../../../lib/media/resolveMediaUrl';
26
25
 
27
26
  export default function ImageBlockEditor({ content, onChange }: BlockEditorProps<Partial<ImageBlockContent>>) {
28
27
  const [selectedMediaObjectKey, setSelectedMediaObjectKey] = useState<string | null | undefined>(content.object_key);
@@ -84,21 +83,22 @@ export default function ImageBlockEditor({ content, onChange }: BlockEditorProps
84
83
  setSelectedMediaObjectKey(null);
85
84
  onChange({ media_id: null, object_key: null, alt_text: "", caption: "", width: null, height: null, blur_data_url: null });
86
85
  };
87
-
88
- const displayObjectKey = content.object_key || selectedMediaObjectKey;
89
-
90
- return (
91
- <div className="space-y-3 p-3 border-t mt-2">
92
- <Label>Image</Label>
93
- <div className="mt-1 p-3 border rounded-md bg-muted/30 min-h-[120px] flex flex-col items-center justify-center">
94
- {isLoadingMediaDetails && <p>Loading image details...</p>}
95
- {!isLoadingMediaDetails && displayObjectKey && typeof content.width === 'number' && typeof content.height === 'number' && content.width > 0 && content.height > 0 ? (
96
- <div className="relative group inline-block" style={{ maxWidth: content.width, maxHeight: 200 }}> {/* Max height for editor preview consistency */}
97
- <Image
98
- src={`${R2_BASE_URL}/${displayObjectKey}`}
99
- alt={content.alt_text || "Selected image"}
100
- width={content.width}
101
- height={content.height}
86
+
87
+ const displayObjectKey = content.object_key || selectedMediaObjectKey;
88
+ const displayImageUrl = resolveMediaUrl(displayObjectKey);
89
+
90
+ return (
91
+ <div className="space-y-3 p-3 border-t mt-2">
92
+ <Label>Image</Label>
93
+ <div className="mt-1 p-3 border rounded-md bg-muted/30 min-h-[120px] flex flex-col items-center justify-center">
94
+ {isLoadingMediaDetails && <p>Loading image details...</p>}
95
+ {!isLoadingMediaDetails && displayImageUrl && typeof content.width === 'number' && typeof content.height === 'number' && content.width > 0 && content.height > 0 ? (
96
+ <div className="relative group inline-block" style={{ maxWidth: content.width, maxHeight: 200 }}> {/* Max height for editor preview consistency */}
97
+ <Image
98
+ src={displayImageUrl}
99
+ alt={content.alt_text || "Selected image"}
100
+ width={content.width}
101
+ height={content.height}
102
102
  className="rounded-md object-contain" // Removed max-h-40, relying on width/height and parent max-height
103
103
  style={{ maxHeight: '200px' }} // Ensure image does not exceed this height in preview
104
104
  placeholder={content.blur_data_url ? "blur" : "empty"}
@@ -108,15 +108,15 @@ export default function ImageBlockEditor({ content, onChange }: BlockEditorProps
108
108
  type="button" variant="destructive" size="icon"
109
109
  className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity h-6 w-6"
110
110
  onClick={handleRemoveImage} title="Remove Image"
111
- > <XIcon className="h-3 w-3" /> </Button>
112
- </div>
113
- ) : !isLoadingMediaDetails && displayObjectKey ? ( // Fallback if width/height are missing but key exists
114
- <div className="relative group inline-block">
115
- <Image
116
- src={`${R2_BASE_URL}/${displayObjectKey}`}
117
- alt={content.alt_text || "Selected image"}
118
- width={300}
119
- height={200}
111
+ > <XIcon className="h-3 w-3" /> </Button>
112
+ </div>
113
+ ) : !isLoadingMediaDetails && displayImageUrl ? ( // Fallback if width/height are missing but key exists
114
+ <div className="relative group inline-block">
115
+ <Image
116
+ src={displayImageUrl}
117
+ alt={content.alt_text || "Selected image"}
118
+ width={300}
119
+ height={200}
120
120
  className="rounded-md object-contain max-h-40 block"
121
121
  style={{ maxHeight: '200px' }}
122
122
  />
@@ -1,9 +1,8 @@
1
1
  // app/cms/blocks/editors/PostsGridBlockEditor.tsx
2
- import React, { useState, useEffect } from 'react';
2
+ import React from 'react';
3
3
  import { BlockEditorProps } from '../components/BlockEditorModal';
4
4
  import { Input } from '@nextblock-cms/ui';
5
5
  import { Label } from '@nextblock-cms/ui';
6
- // import { useToast } from "@nextblock-cms/ui"; // Assuming you have a toast component - Removed for now
7
6
 
8
7
  interface PostsGridBlockContent {
9
8
  title?: string;
@@ -13,20 +12,17 @@ interface PostsGridBlockContent {
13
12
  }
14
13
 
15
14
  const PostsGridBlockEditor: React.FC<BlockEditorProps<PostsGridBlockContent>> = ({ content, onChange }) => {
16
- const [currentTitle, setCurrentTitle] = useState(content.title || 'Recent Posts');
17
- const [currentPostsPerPage, setCurrentPostsPerPage] = useState(content.postsPerPage || 6);
18
- const [currentColumns, setCurrentColumns] = useState(content.columns || 3);
15
+ const currentTitle = content.title || 'Recent Posts';
16
+ const currentPostsPerPage = content.postsPerPage || 6;
17
+ const currentColumns = content.columns || 3;
19
18
  const showPagination = content.showPagination === undefined ? true : content.showPagination;
20
19
 
21
- useEffect(() => {
22
- const newContentPayload = {
23
- title: currentTitle,
24
- postsPerPage: Number(currentPostsPerPage),
25
- columns: Number(currentColumns),
26
- showPagination: showPagination,
27
- };
28
- onChange(newContentPayload);
29
- }, [currentTitle, currentPostsPerPage, currentColumns, showPagination, onChange]);
20
+ const handleChange = (field: keyof PostsGridBlockContent, value: any) => {
21
+ onChange({
22
+ ...content,
23
+ [field]: value,
24
+ });
25
+ };
30
26
 
31
27
  return (
32
28
  <div className="space-y-4 p-4 border rounded-md">
@@ -37,7 +33,7 @@ const PostsGridBlockEditor: React.FC<BlockEditorProps<PostsGridBlockContent>> =
37
33
  <Input
38
34
  id="posts-grid-title"
39
35
  value={currentTitle}
40
- onChange={(e) => setCurrentTitle(e.target.value)}
36
+ onChange={(e) => handleChange('title', e.target.value)}
41
37
  placeholder="Enter title for the posts grid"
42
38
  />
43
39
  </div>
@@ -48,7 +44,7 @@ const PostsGridBlockEditor: React.FC<BlockEditorProps<PostsGridBlockContent>> =
48
44
  id="posts-grid-per-page"
49
45
  type="number"
50
46
  value={currentPostsPerPage}
51
- onChange={(e) => setCurrentPostsPerPage(parseInt(e.target.value, 10))}
47
+ onChange={(e) => handleChange('postsPerPage', parseInt(e.target.value, 10))}
52
48
  min="1"
53
49
  />
54
50
  </div>
@@ -59,9 +55,9 @@ const PostsGridBlockEditor: React.FC<BlockEditorProps<PostsGridBlockContent>> =
59
55
  id="posts-grid-columns"
60
56
  type="number"
61
57
  value={currentColumns}
62
- onChange={(e) => setCurrentColumns(parseInt(e.target.value, 10))}
58
+ onChange={(e) => handleChange('columns', parseInt(e.target.value, 10))}
63
59
  min="1"
64
- max="6" // Example max, adjust as needed
60
+ max="6"
65
61
  />
66
62
  </div>
67
63
 
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ import React from 'react';
4
+ import { Label, Input } from "@nextblock-cms/ui";
5
+ import { BlockEditorProps } from '../components/BlockEditorModal';
6
+ import { ProductGridBlockContent } from '../../../../lib/blocks/ecommerce-block-schemas';
7
+
8
+ export default function ProductGridBlockEditor({ content, onChange }: BlockEditorProps<Partial<ProductGridBlockContent>>) {
9
+
10
+ const handleChange = (field: keyof ProductGridBlockContent, value: any) => {
11
+ onChange({ ...content, [field]: value });
12
+ };
13
+
14
+ return (
15
+ <div className="space-y-4 p-3 border-t mt-2">
16
+ <div>
17
+ <Label htmlFor="pg-limit">Item Limit</Label>
18
+ <Input
19
+ id="pg-limit"
20
+ type="number"
21
+ min={1}
22
+ max={20}
23
+ value={content.limit || 6}
24
+ onChange={(e) => handleChange('limit', parseInt(e.target.value) || 6)}
25
+ className="mt-1"
26
+ />
27
+ </div>
28
+ <div>
29
+ <Label htmlFor="pg-title">Grid Title (Optional)</Label>
30
+ <Input
31
+ id="pg-title"
32
+ value={content.title || ""}
33
+ onChange={(e) => handleChange('title', e.target.value)}
34
+ placeholder="New Arrivals"
35
+ className="mt-1"
36
+ />
37
+ </div>
38
+ {/* Type selection could be added here, currently defaulting to 'latest' */}
39
+ </div>
40
+ );
41
+ }