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
@@ -1,9 +1,26 @@
1
- // app/article/[slug]/page.utils.ts
2
- import { createClient } from "@nextblock-cms/db/server";
1
+ // app/article/[slug]/page.utils.ts
2
+ import { createClient, getSsgSupabaseClient } from "@nextblock-cms/db/server";
3
3
  import type { Database } from "@nextblock-cms/db";
4
+ import { draftMode } from "next/headers";
5
+ import { resolveMediaUrl } from "../../../lib/media/resolveMediaUrl";
6
+ import { getContentDraft } from "../../../lib/visual-editing/draft-content";
7
+ import type { ContentDraftRow, DraftBlockSnapshot } from "../../../lib/visual-editing/types";
4
8
 
5
9
  type PostType = Database['public']['Tables']['posts']['Row'];
6
10
  type BlockType = Database['public']['Tables']['blocks']['Row'];
11
+ type PublicPostData = PostType & {
12
+ blocks: BlockType[];
13
+ language_code: string;
14
+ language_id: number;
15
+ translation_group_id: string;
16
+ feature_image_url?: string | null;
17
+ feature_image_blur_data_url?: string | null;
18
+ feature_image_width?: number | null;
19
+ feature_image_height?: number | null;
20
+ draft_id?: number | null;
21
+ };
22
+
23
+ // Define a more specific type for the content of an Image Block
7
24
 
8
25
  // Define a more specific type for the content of an Image Block
9
26
  export type ImageBlockContent = {
@@ -12,57 +29,264 @@ export type ImageBlockContent = {
12
29
  blur_data_url?: string | null;
13
30
  };
14
31
 
15
- interface SectionOrHeroBlockContent {
16
- [key: string]: unknown;
17
- background?: {
18
- type?: 'image' | 'color';
19
- image?: {
20
- media_id?: string;
21
- object_key?: string;
22
- blur_data_url?: string;
23
- };
32
+ function hasMetaValue(draft: ContentDraftRow, key: string) {
33
+ return Object.prototype.hasOwnProperty.call(draft.meta, key);
34
+ }
35
+
36
+ function draftString(draft: ContentDraftRow, key: string, fallback: string): string {
37
+ const value = draft.meta[key];
38
+ return typeof value === "string" ? value : fallback;
39
+ }
40
+
41
+ function draftNullableString(
42
+ draft: ContentDraftRow,
43
+ key: string,
44
+ fallback: string | null
45
+ ): string | null {
46
+ if (!hasMetaValue(draft, key)) {
47
+ return fallback;
48
+ }
49
+
50
+ const value = draft.meta[key];
51
+ return typeof value === "string" ? value : null;
52
+ }
53
+
54
+ function draftNumber(draft: ContentDraftRow, key: string, fallback: number): number {
55
+ const value = draft.meta[key];
56
+ return typeof value === "number" ? value : fallback;
57
+ }
58
+
59
+ function draftBlockToPostBlock(
60
+ block: DraftBlockSnapshot,
61
+ postId: number,
62
+ fallbackUpdatedAt: string
63
+ ): BlockType {
64
+ return {
65
+ id: block.id ?? -(block.order + 1),
66
+ page_id: null,
67
+ post_id: postId,
68
+ product_id: null,
69
+ language_id: block.language_id,
70
+ block_type: block.block_type,
71
+ content: block.content,
72
+ order: block.order,
73
+ created_at: block.created_at ?? fallbackUpdatedAt,
74
+ updated_at: block.updated_at ?? fallbackUpdatedAt,
75
+ };
76
+ }
77
+
78
+ function applyDraftToPost(post: any, draft: ContentDraftRow) {
79
+ const languageId = draftNumber(draft, "language_id", post.language_id);
80
+
81
+ return {
82
+ ...post,
83
+ title: draftString(draft, "title", post.title),
84
+ slug: draftString(draft, "slug", post.slug),
85
+ language_id: languageId,
86
+ languages: languageId === post.language_id ? post.languages : null,
87
+ status: draftString(draft, "status", post.status) as PostType["status"],
88
+ meta_title: draftNullableString(draft, "meta_title", post.meta_title),
89
+ meta_description: draftNullableString(draft, "meta_description", post.meta_description),
90
+ label: draftNullableString(draft, "label", post.label),
91
+ excerpt: draftNullableString(draft, "excerpt", post.excerpt),
92
+ subtitle: draftNullableString(draft, "subtitle", post.subtitle),
93
+ published_at: draftNullableString(draft, "published_at", post.published_at),
94
+ feature_image_id: draftNullableString(draft, "feature_image_id", post.feature_image_id),
95
+ translation_group_id: draftString(
96
+ draft,
97
+ "translation_group_id",
98
+ post.translation_group_id
99
+ ),
100
+ blocks: draft.blocks.map((block) =>
101
+ draftBlockToPostBlock(block, post.id, draft.updated_at || post.updated_at)
102
+ ),
24
103
  };
25
104
  }
26
- // Includes logic to fetch object_key for image blocks.
27
- const buildMediaUrl = (objectKey?: string | null) => {
28
- if (!objectKey) return null;
29
- if (objectKey.startsWith('/')) return objectKey;
30
- const base = process.env.NEXT_PUBLIC_R2_BASE_URL || '';
31
- return base ? `${base}/${objectKey}` : objectKey;
32
- };
33
-
34
- export async function getPostDataBySlug(slug: string): Promise<(PostType & { blocks: BlockType[]; language_code: string; language_id: number; translation_group_id: string; feature_image_url?: string | null; feature_image_blur_data_url?: string | null; }) | null> {
35
- const supabase = createClient();
36
-
37
- const { data: postData, error: postError } = await supabase
105
+
106
+ function extractMediaIdsFromBlock(block: any): string[] {
107
+ const ids: string[] = [];
108
+ if (!block || !block.block_type) return ids;
109
+
110
+ if (block.block_type === 'image') {
111
+ const mediaId = block.content?.media_id;
112
+ if (mediaId && typeof mediaId === 'string') {
113
+ ids.push(mediaId);
114
+ }
115
+ } else if (block.block_type === 'section') {
116
+ const content = block.content;
117
+ if (content) {
118
+ if (content.background?.type === 'image' && content.background.image?.media_id) {
119
+ ids.push(content.background.image.media_id);
120
+ }
121
+ if (Array.isArray(content.column_blocks)) {
122
+ for (const col of content.column_blocks) {
123
+ if (Array.isArray(col)) {
124
+ for (const nestedBlock of col) {
125
+ ids.push(...extractMediaIdsFromBlock(nestedBlock));
126
+ }
127
+ }
128
+ }
129
+ }
130
+ if (Array.isArray(content.slides)) {
131
+ for (const slide of content.slides) {
132
+ if (slide.background?.type === 'image' && slide.background.image?.media_id) {
133
+ ids.push(slide.background.image.media_id);
134
+ }
135
+ if (Array.isArray(slide.column_blocks)) {
136
+ for (const col of slide.column_blocks) {
137
+ if (Array.isArray(col)) {
138
+ for (const nestedBlock of col) {
139
+ ids.push(...extractMediaIdsFromBlock(nestedBlock));
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ return ids;
149
+ }
150
+
151
+ function mapMediaDataToBlock(
152
+ block: any,
153
+ mediaMap: Map<string, { object_key: string; blur_data_url?: string | null }>
154
+ ): any {
155
+ if (!block || !block.block_type) return block;
156
+
157
+ if (block.block_type === 'image') {
158
+ const content = block.content;
159
+ if (content?.media_id) {
160
+ const mediaData = mediaMap.get(content.media_id);
161
+ if (mediaData) {
162
+ return {
163
+ ...block,
164
+ content: {
165
+ ...content,
166
+ object_key: mediaData.object_key,
167
+ blur_data_url: mediaData.blur_data_url,
168
+ },
169
+ };
170
+ }
171
+ }
172
+ } else if (block.block_type === 'section') {
173
+ const content = block.content;
174
+ if (!content) return block;
175
+
176
+ const updatedContent = { ...content };
177
+
178
+ if (content.background?.type === 'image' && content.background.image?.media_id) {
179
+ const mediaData = mediaMap.get(content.background.image.media_id);
180
+ if (mediaData) {
181
+ updatedContent.background = {
182
+ ...content.background,
183
+ image: {
184
+ ...content.background.image,
185
+ object_key: mediaData.object_key,
186
+ blur_data_url: mediaData.blur_data_url,
187
+ },
188
+ };
189
+ }
190
+ }
191
+
192
+ if (Array.isArray(content.column_blocks)) {
193
+ updatedContent.column_blocks = content.column_blocks.map((col: any) => {
194
+ if (Array.isArray(col)) {
195
+ return col.map((nestedBlock: any) => mapMediaDataToBlock(nestedBlock, mediaMap));
196
+ }
197
+ return col;
198
+ });
199
+ }
200
+
201
+ if (Array.isArray(content.slides)) {
202
+ updatedContent.slides = content.slides.map((slide: any) => {
203
+ const updatedSlide = { ...slide };
204
+
205
+ if (slide.background?.type === 'image' && slide.background.image?.media_id) {
206
+ const mediaData = mediaMap.get(slide.background.image.media_id);
207
+ if (mediaData) {
208
+ updatedSlide.background = {
209
+ ...slide.background,
210
+ image: {
211
+ ...slide.background.image,
212
+ object_key: mediaData.object_key,
213
+ blur_data_url: mediaData.blur_data_url,
214
+ },
215
+ };
216
+ }
217
+ }
218
+
219
+ if (Array.isArray(slide.column_blocks)) {
220
+ updatedSlide.column_blocks = slide.column_blocks.map((col: any) => {
221
+ if (Array.isArray(col)) {
222
+ return col.map((nestedBlock: any) => mapMediaDataToBlock(nestedBlock, mediaMap));
223
+ }
224
+ return col;
225
+ });
226
+ }
227
+
228
+ return updatedSlide;
229
+ });
230
+ }
231
+
232
+ return {
233
+ ...block,
234
+ content: updatedContent,
235
+ };
236
+ }
237
+
238
+ return block;
239
+ }
240
+
241
+ export async function getPostDataBySlug(slug: string): Promise<PublicPostData | null> {
242
+ const draft = await draftMode();
243
+ const isDraftModeEnabled = draft.isEnabled;
244
+ const supabase = isDraftModeEnabled ? createClient() : getSsgSupabaseClient();
245
+
246
+ let postQuery = supabase
38
247
  .from("posts")
39
248
  .select(`
40
249
  *,
41
250
  languages!inner (id, code),
42
251
  blocks (*),
43
- media ( object_key, blur_data_url )
252
+ media ( object_key, blur_data_url, width, height )
44
253
  `)
45
254
  .eq("slug", slug) // Find the post by its unique slug for this language
46
- .eq("status", "published")
47
- .or(`published_at.is.null,published_at.lte.${new Date().toISOString()}`) // Check published_at
48
- .order('order', { foreignTable: 'blocks', ascending: true })
49
- .maybeSingle();
255
+ .order('order', { foreignTable: 'blocks', ascending: true });
256
+
257
+ if (!isDraftModeEnabled) {
258
+ postQuery = postQuery
259
+ .eq("status", "published")
260
+ .or(`published_at.is.null,published_at.lte.${new Date().toISOString()}`); // Check published_at
261
+ }
262
+
263
+ const { data: initialPostData, error: postError } = await postQuery.maybeSingle();
264
+ let postData = initialPostData;
50
265
 
51
266
  if (postError || !postData) {
52
267
  if(postError) console.error(`Error fetching post data for slug '${slug}':`, postError);
53
268
  return null;
54
269
  }
55
270
 
271
+ const contentDraft = isDraftModeEnabled
272
+ ? await getContentDraft("post", postData.id)
273
+ : null;
274
+
275
+ if (contentDraft) {
276
+ postData = applyDraftToPost(postData, contentDraft);
277
+ }
278
+
56
279
  // Ensure language information is correctly extracted
57
- const langInfo = postData.languages as unknown as { id: number; code: string };
280
+ let langInfo = postData.languages as unknown as { id: number; code: string } | null;
58
281
  if (!langInfo || !langInfo.id || !langInfo.code) {
59
282
  console.error(`Language information missing or incomplete for post slug '${slug}'. DB response:`, postData.languages);
60
283
  if (!postData.language_id) return null;
61
284
  const {data: fallbackLang} = await supabase.from("languages").select("code").eq("id", postData.language_id).single();
62
285
  if (!fallbackLang) return null;
63
- Object.assign(langInfo, {id: postData.language_id, code: fallbackLang.code });
286
+ langInfo = { id: postData.language_id, code: fallbackLang.code };
64
287
  }
65
288
 
289
+
66
290
  if (!postData.translation_group_id) {
67
291
  console.error(`Post with slug '${slug}' is missing a translation_group_id.`);
68
292
  return null;
@@ -71,19 +295,8 @@ export async function getPostDataBySlug(slug: string): Promise<(PostType & { blo
71
295
  let blocksWithMediaData: BlockType[] = postData.blocks || [];
72
296
  if (blocksWithMediaData.length > 0) {
73
297
  const mediaIds = blocksWithMediaData
74
- .map(block => {
75
- if (block.block_type === 'image') {
76
- return (block.content as ImageBlockContent)?.media_id;
77
- }
78
- if (block.block_type === 'section' || block.block_type === 'hero') {
79
- const content = block.content as SectionOrHeroBlockContent;
80
- if (content.background?.type === 'image' && content.background?.image?.media_id) {
81
- return content.background.image.media_id;
82
- }
83
- }
84
- return null;
85
- })
86
- .filter((id): id is string => id !== null && typeof id === 'string');
298
+ .flatMap(block => extractMediaIdsFromBlock(block))
299
+ .filter((id): id is string => id !== null && typeof id === 'string' && id !== '');
87
300
 
88
301
  if (mediaIds.length > 0) {
89
302
  const { data: mediaItems, error: mediaError } = await supabase
@@ -95,38 +308,7 @@ export async function getPostDataBySlug(slug: string): Promise<(PostType & { blo
95
308
  console.error("SSG (Posts): Error fetching media items for blocks:", mediaError);
96
309
  } else if (mediaItems) {
97
310
  const mediaMap = new Map(mediaItems.map(m => [m.id, { object_key: m.object_key, blur_data_url: m.blur_data_url }]));
98
- blocksWithMediaData = blocksWithMediaData.map(block => {
99
- if (block.block_type === 'image') {
100
- const content = block.content as ImageBlockContent;
101
- if (content.media_id) {
102
- const mediaData = mediaMap.get(content.media_id);
103
- if (mediaData) {
104
- return { ...block, content: { ...content, object_key: mediaData.object_key, blur_data_url: mediaData.blur_data_url } };
105
- }
106
- }
107
- }
108
- if (block.block_type === 'section' || block.block_type === 'hero') {
109
- const content = block.content as SectionOrHeroBlockContent;
110
- if (content.background?.type === 'image' && content.background?.image?.media_id) {
111
- const mediaData = mediaMap.get(content.background.image.media_id);
112
- if (mediaData) {
113
- const newContent = {
114
- ...content,
115
- background: {
116
- ...content.background,
117
- image: {
118
- ...content.background.image,
119
- object_key: mediaData.object_key,
120
- blur_data_url: mediaData.blur_data_url,
121
- },
122
- },
123
- };
124
- return { ...block, content: newContent };
125
- }
126
- }
127
- }
128
- return block;
129
- });
311
+ blocksWithMediaData = blocksWithMediaData.map(block => mapMediaDataToBlock(block, mediaMap));
130
312
  }
131
313
  }
132
314
  }
@@ -137,7 +319,10 @@ export async function getPostDataBySlug(slug: string): Promise<(PostType & { blo
137
319
  language_code: langInfo.code,
138
320
  language_id: langInfo.id,
139
321
  translation_group_id: postData.translation_group_id,
140
- feature_image_url: buildMediaUrl(postData.media?.object_key),
322
+ feature_image_url: resolveMediaUrl(postData.media?.object_key),
141
323
  feature_image_blur_data_url: postData.media?.blur_data_url,
142
- } as (PostType & { blocks: BlockType[]; language_code: string; language_id: number; translation_group_id: string; feature_image_url?: string | null; feature_image_blur_data_url?: string | null; });
324
+ feature_image_width: postData.media?.width ?? null,
325
+ feature_image_height: postData.media?.height ?? null,
326
+ draft_id: contentDraft?.id ?? null,
327
+ } as PublicPostData;
143
328
  }
@@ -1,58 +1,31 @@
1
- import { createClient, getProfileWithRoleServerSide } from "@nextblock-cms/db/server";
2
- import { NextResponse } from "next/server";
3
- import type { Database } from "@nextblock-cms/db";
4
-
5
- export type UserRole = Database['public']['Enums']['user_role'];
6
-
7
- export async function GET(request: Request) {
8
- const requestUrl = new URL(request.url);
9
- const code = requestUrl.searchParams.get("code");
10
- const origin = requestUrl.origin;
11
- const initialRedirectTo = requestUrl.searchParams.get("redirect_to")?.toString();
12
- const dashboardPath = "/cms/dashboard";
13
- const homePath = "/";
14
-
15
- if (code) {
16
- const supabase = await createClient();
17
- const { data: { user } , error: exchangeError } = await supabase.auth.exchangeCodeForSession(code);
18
-
19
- if (exchangeError) {
20
- console.error("Auth callback: Error exchanging code for session:", exchangeError.message);
21
- // Fallback: if initialRedirectTo is valid (not dashboard) use it, else home
22
- if (initialRedirectTo && initialRedirectTo !== dashboardPath && !initialRedirectTo.startsWith(dashboardPath + '/')) {
23
- return NextResponse.redirect(`${origin}${initialRedirectTo}`);
24
- }
25
- return NextResponse.redirect(`${origin}${homePath}`);
26
- }
27
-
28
- if (user) {
29
- const profile = await getProfileWithRoleServerSide(user.id);
30
- const role = profile?.role as UserRole | null;
31
-
32
- if (role === 'ADMIN' || role === 'WRITER') {
33
- return NextResponse.redirect(`${origin}${dashboardPath}`);
34
- } else {
35
- // For USER, null, or other roles:
36
- // Redirect to initialRedirectTo if it's present and NOT the dashboard.
37
- // Otherwise, redirect to the homepage.
38
- if (initialRedirectTo && initialRedirectTo !== dashboardPath && !initialRedirectTo.startsWith(dashboardPath + '/')) {
39
- return NextResponse.redirect(`${origin}${initialRedirectTo}`);
40
- }
41
- return NextResponse.redirect(`${origin}${homePath}`);
42
- }
43
- } else {
44
- // User object is null after successful exchange
45
- console.warn("Auth callback: User object is null after code exchange.");
46
- if (initialRedirectTo && initialRedirectTo !== dashboardPath && !initialRedirectTo.startsWith(dashboardPath + '/')) {
47
- return NextResponse.redirect(`${origin}${initialRedirectTo}`);
48
- }
49
- return NextResponse.redirect(`${origin}${homePath}`);
50
- }
51
- }
52
-
53
- // If no code parameter:
54
- if (initialRedirectTo && initialRedirectTo !== dashboardPath && !initialRedirectTo.startsWith(dashboardPath + '/')) {
55
- return NextResponse.redirect(`${origin}${initialRedirectTo}`);
56
- }
57
- return NextResponse.redirect(`${origin}${homePath}`);
58
- }
1
+ import { createClient, getProfileWithRoleServerSide } from "@nextblock-cms/db/server";
2
+ import { NextResponse } from "next/server";
3
+ import { resolvePostAuthRedirect } from "../../../lib/auth-redirects";
4
+
5
+ export async function GET(request: Request) {
6
+ const requestUrl = new URL(request.url);
7
+ const code = requestUrl.searchParams.get("code");
8
+ const origin = requestUrl.origin;
9
+ const initialRedirectTo = requestUrl.searchParams.get("redirect_to")?.toString();
10
+
11
+ if (code) {
12
+ const supabase = await createClient();
13
+ const { data: { user } , error: exchangeError } = await supabase.auth.exchangeCodeForSession(code);
14
+
15
+ if (exchangeError) {
16
+ console.error("Auth callback: Error exchanging code for session:", exchangeError.message);
17
+ return NextResponse.redirect(`${origin}${initialRedirectTo || '/'}`);
18
+ }
19
+
20
+ if (user) {
21
+ const profile = await getProfileWithRoleServerSide(user.id);
22
+ const targetPath = resolvePostAuthRedirect(profile, initialRedirectTo);
23
+ return NextResponse.redirect(`${origin}${targetPath}`);
24
+ }
25
+
26
+ console.warn("Auth callback: User object is null after code exchange.");
27
+ return NextResponse.redirect(`${origin}/`);
28
+ }
29
+
30
+ return NextResponse.redirect(`${origin}${initialRedirectTo || '/'}`);
31
+ }
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+
3
+ import { Cart } from '@nextblock-cms/ecommerce';
4
+
5
+ export default function CartPage() {
6
+ return <Cart />;
7
+ }
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+ import { useCartStore, type CartItem } from '@nextblock-cms/ecommerce';
5
+
6
+ interface UcpCartHydratorProps {
7
+ items: CartItem[];
8
+ }
9
+
10
+ export function UcpCartHydrator({ items }: UcpCartHydratorProps) {
11
+ useEffect(() => {
12
+ if (items.length === 0) {
13
+ return;
14
+ }
15
+
16
+ useCartStore.getState().setItems(items);
17
+ }, [items]);
18
+
19
+ return null;
20
+ }
@@ -0,0 +1,52 @@
1
+ import { Checkout } from '@nextblock-cms/ecommerce';
2
+ import { createClient } from '@nextblock-cms/db/server';
3
+ import { getDefaultUserAddresses } from '@nextblock-cms/ecommerce/server';
4
+ import { getUcpCartCheckoutItems } from '../lib/ucp/server';
5
+ import { UcpCartHydrator } from './UcpCartHydrator';
6
+
7
+ interface CheckoutPageProps {
8
+ searchParams?: Promise<{
9
+ ucp_cart?: string;
10
+ cart?: string;
11
+ }>;
12
+ }
13
+
14
+ export default async function CheckoutPage({ searchParams }: CheckoutPageProps) {
15
+ const resolvedSearchParams = await searchParams;
16
+ const ucpCartId = resolvedSearchParams?.ucp_cart || resolvedSearchParams?.cart || null;
17
+ const ucpCartItems = await getUcpCartCheckoutItems(ucpCartId);
18
+ const supabase = createClient();
19
+ const {
20
+ data: { user },
21
+ } = await supabase.auth.getUser();
22
+
23
+ if (!user) {
24
+ return (
25
+ <>
26
+ <UcpCartHydrator items={ucpCartItems} />
27
+ <Checkout initialCustomer={{ isAuthenticated: false }} />
28
+ </>
29
+ );
30
+ }
31
+
32
+ const [{ data: profile }, { billingAddress, shippingAddress }] = await Promise.all([
33
+ supabase.from('profiles').select('full_name, phone').eq('id', user.id).single(),
34
+ getDefaultUserAddresses(user.id, supabase),
35
+ ]);
36
+
37
+ return (
38
+ <>
39
+ <UcpCartHydrator items={ucpCartItems} />
40
+ <Checkout
41
+ initialCustomer={{
42
+ isAuthenticated: true,
43
+ email: user.email,
44
+ fullName: profile?.full_name || null,
45
+ phone: profile?.phone || null,
46
+ billingAddress,
47
+ shippingAddress,
48
+ }}
49
+ />
50
+ </>
51
+ );
52
+ }