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
@@ -0,0 +1,391 @@
1
+ "use client";
2
+
3
+ import { useRef, useState, useTransition } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { toast } from "sonner";
6
+ import {
7
+ Alert,
8
+ AlertDescription,
9
+ Badge,
10
+ Button,
11
+ Checkbox,
12
+ Dialog,
13
+ DialogContent,
14
+ DialogDescription,
15
+ DialogFooter,
16
+ DialogHeader,
17
+ DialogTitle,
18
+ Input,
19
+ Label,
20
+ Select,
21
+ SelectContent,
22
+ SelectItem,
23
+ SelectTrigger,
24
+ SelectValue,
25
+ Spinner,
26
+ Tooltip,
27
+ TooltipContent,
28
+ TooltipProvider,
29
+ TooltipTrigger,
30
+ } from "@nextblock-cms/ui";
31
+ import { Download, FileDown, FileSpreadsheet, HelpCircle, Upload } from "lucide-react";
32
+
33
+ import type {
34
+ CmsContentType,
35
+ CmsImportApplyMode,
36
+ CmsImportConflictMode,
37
+ CmsImportSummary,
38
+ } from "../../../lib/cms-transfer/types";
39
+ import {
40
+ applyCmsCsvImportAction,
41
+ dryRunCmsCsvImportAction,
42
+ exportCmsCsvAction,
43
+ getCmsCsvTemplateAction,
44
+ } from "./actions";
45
+
46
+ interface ContentTransferControlsProps {
47
+ contentType: CmsContentType;
48
+ label: string;
49
+ languageId?: number;
50
+ }
51
+
52
+ function downloadTextFile(fileName: string, mimeType: string, content: string) {
53
+ const blob = new Blob([content], { type: `${mimeType};charset=utf-8` });
54
+ const url = URL.createObjectURL(blob);
55
+ const link = document.createElement("a");
56
+ link.href = url;
57
+ link.download = fileName;
58
+ document.body.appendChild(link);
59
+ link.click();
60
+ link.remove();
61
+ URL.revokeObjectURL(url);
62
+ }
63
+
64
+ function readFileAsText(file: File) {
65
+ return new Promise<string>((resolve, reject) => {
66
+ const reader = new FileReader();
67
+ reader.onload = () => resolve(String(reader.result || ""));
68
+ reader.onerror = () => reject(reader.error || new Error("Failed to read file."));
69
+ reader.readAsText(file);
70
+ });
71
+ }
72
+
73
+ function getMinimumRequirementText(contentType: CmsContentType) {
74
+ if (contentType === "products") {
75
+ return "For partial product updates, keep either id or language_code + sku. Blank fields are preserved only for live overwrite imports.";
76
+ }
77
+
78
+ const noun = contentType === "posts" ? "post" : "page";
79
+ return `For partial ${noun} updates, keep either id or language_code + slug. Blank fields are preserved only for live overwrite imports.`;
80
+ }
81
+
82
+ function SummaryPanel({ summary }: { summary: CmsImportSummary | null }) {
83
+ if (!summary) return null;
84
+
85
+ return (
86
+ <div className="space-y-3 rounded-md border bg-muted/20 p-3">
87
+ <div className="flex flex-wrap gap-2">
88
+ <Badge variant={summary.success ? "default" : "destructive"}>
89
+ {summary.success ? "Ready" : "Needs Fixes"}
90
+ </Badge>
91
+ <Badge variant="outline">{summary.totalRows} rows</Badge>
92
+ <Badge variant="outline">{summary.created} create</Badge>
93
+ <Badge variant="outline">{summary.updated} update</Badge>
94
+ {summary.warnings.length > 0 ? (
95
+ <Badge variant="secondary">{summary.warnings.length} warnings</Badge>
96
+ ) : null}
97
+ </div>
98
+
99
+ {summary.errors.length > 0 ? (
100
+ <Alert variant="destructive">
101
+ <AlertDescription>
102
+ {summary.errors.slice(0, 5).map((error) => (
103
+ <span key={`${error.row}-${error.message}`} className="block">
104
+ Row {error.row}: {error.message}
105
+ </span>
106
+ ))}
107
+ {summary.errors.length > 5 ? (
108
+ <span className="block">And {summary.errors.length - 5} more errors.</span>
109
+ ) : null}
110
+ </AlertDescription>
111
+ </Alert>
112
+ ) : null}
113
+
114
+ {summary.warnings.length > 0 ? (
115
+ <Alert>
116
+ <AlertDescription>
117
+ {summary.warnings.slice(0, 4).map((warning) => (
118
+ <span key={`${warning.row}-${warning.message}`} className="block">
119
+ Row {warning.row}: {warning.message}
120
+ </span>
121
+ ))}
122
+ {summary.warnings.length > 4 ? (
123
+ <span className="block">And {summary.warnings.length - 4} more warnings.</span>
124
+ ) : null}
125
+ </AlertDescription>
126
+ </Alert>
127
+ ) : null}
128
+ </div>
129
+ );
130
+ }
131
+
132
+ export function ContentTransferControls({
133
+ contentType,
134
+ label,
135
+ languageId,
136
+ }: ContentTransferControlsProps) {
137
+ const router = useRouter();
138
+ const fileInputRef = useRef<HTMLInputElement>(null);
139
+ const [open, setOpen] = useState(false);
140
+ const [csv, setCsv] = useState("");
141
+ const [fileName, setFileName] = useState("");
142
+ const [conflictMode, setConflictMode] =
143
+ useState<CmsImportConflictMode>("overwrite_existing");
144
+ const [applyMode, setApplyMode] = useState<CmsImportApplyMode>("draft");
145
+ const [ignoreBlankFields, setIgnoreBlankFields] = useState(false);
146
+ const [summary, setSummary] = useState<CmsImportSummary | null>(null);
147
+ const [isPending, startTransition] = useTransition();
148
+ const canIgnoreBlankFields = conflictMode === "overwrite_existing" && applyMode === "live";
149
+ const effectiveIgnoreBlankFields = canIgnoreBlankFields && ignoreBlankFields;
150
+
151
+ const downloadTemplate = () => {
152
+ startTransition(async () => {
153
+ const result = await getCmsCsvTemplateAction(contentType);
154
+ if (!result.success || !result.content || !result.fileName || !result.mimeType) {
155
+ toast.error(result.error || "Failed to download template.");
156
+ return;
157
+ }
158
+ downloadTextFile(result.fileName, result.mimeType, result.content);
159
+ });
160
+ };
161
+
162
+ const exportCsv = () => {
163
+ startTransition(async () => {
164
+ const result = await exportCmsCsvAction({ contentType, languageId });
165
+ if (!result.success || !result.content || !result.fileName || !result.mimeType) {
166
+ toast.error(result.error || "Failed to export CSV.");
167
+ return;
168
+ }
169
+ downloadTextFile(result.fileName, result.mimeType, result.content);
170
+ });
171
+ };
172
+
173
+ const reviewImport = () => {
174
+ if (!csv.trim()) {
175
+ toast.error("Choose a CSV file first.");
176
+ return;
177
+ }
178
+
179
+ startTransition(async () => {
180
+ const result = await dryRunCmsCsvImportAction({
181
+ contentType,
182
+ csv,
183
+ conflictMode,
184
+ applyMode,
185
+ ignoreBlankFields: effectiveIgnoreBlankFields,
186
+ });
187
+ setSummary(result);
188
+ if (result.success) {
189
+ toast.success("Import review is ready.");
190
+ } else {
191
+ toast.error("Import has errors to fix.");
192
+ }
193
+ });
194
+ };
195
+
196
+ const applyImport = () => {
197
+ if (!summary?.success || !csv.trim()) return;
198
+
199
+ startTransition(async () => {
200
+ const result = await applyCmsCsvImportAction({
201
+ contentType,
202
+ csv,
203
+ conflictMode,
204
+ applyMode,
205
+ ignoreBlankFields: effectiveIgnoreBlankFields,
206
+ });
207
+ setSummary(result);
208
+ if (!result.success) {
209
+ toast.error("Import failed validation.");
210
+ return;
211
+ }
212
+
213
+ toast.success(`${label} import complete.`);
214
+ setOpen(false);
215
+ setCsv("");
216
+ setFileName("");
217
+ setSummary(null);
218
+ router.refresh();
219
+ });
220
+ };
221
+
222
+ return (
223
+ <div className="flex flex-wrap items-center gap-2">
224
+ <Button type="button" variant="outline" size="sm" onClick={exportCsv} disabled={isPending}>
225
+ <Download className="mr-2 h-4 w-4" />
226
+ Export CSV
227
+ </Button>
228
+ <Dialog open={open} onOpenChange={setOpen}>
229
+ <Button type="button" variant="outline" size="sm" onClick={() => setOpen(true)}>
230
+ <Upload className="mr-2 h-4 w-4" />
231
+ Import CSV
232
+ </Button>
233
+ <DialogContent className="max-w-2xl">
234
+ <DialogHeader>
235
+ <DialogTitle>Import {label}</DialogTitle>
236
+ <DialogDescription>
237
+ Review the CSV before applying changes.
238
+ </DialogDescription>
239
+ </DialogHeader>
240
+
241
+ <div className="space-y-4">
242
+ <div className="space-y-2">
243
+ <div className="flex flex-wrap items-center justify-between gap-2">
244
+ <Label htmlFor={`${contentType}-csv-file`}>CSV file</Label>
245
+ <Button
246
+ type="button"
247
+ variant="outline"
248
+ size="sm"
249
+ onClick={downloadTemplate}
250
+ disabled={isPending}
251
+ >
252
+ <FileSpreadsheet className="mr-2 h-4 w-4" />
253
+ Download Template
254
+ </Button>
255
+ </div>
256
+ <Input
257
+ id={`${contentType}-csv-file`}
258
+ ref={fileInputRef}
259
+ type="file"
260
+ accept=".csv,text/csv"
261
+ onChange={async (event) => {
262
+ const file = event.target.files?.[0];
263
+ setSummary(null);
264
+ if (!file) return;
265
+ setFileName(file.name);
266
+ try {
267
+ setCsv(await readFileAsText(file));
268
+ } catch {
269
+ toast.error("Failed to read CSV file.");
270
+ }
271
+ }}
272
+ />
273
+ {fileName ? (
274
+ <p className="text-xs text-muted-foreground">{fileName}</p>
275
+ ) : null}
276
+ </div>
277
+
278
+ <div className="grid gap-3 sm:grid-cols-2">
279
+ <div className="space-y-2">
280
+ <Label>Existing records</Label>
281
+ <Select
282
+ value={conflictMode}
283
+ onValueChange={(value) => {
284
+ setConflictMode(value as CmsImportConflictMode);
285
+ if (value !== "overwrite_existing") setIgnoreBlankFields(false);
286
+ setSummary(null);
287
+ }}
288
+ >
289
+ <SelectTrigger>
290
+ <SelectValue />
291
+ </SelectTrigger>
292
+ <SelectContent>
293
+ <SelectItem value="overwrite_existing">Overwrite matches</SelectItem>
294
+ <SelectItem value="create_new">Create new copies</SelectItem>
295
+ </SelectContent>
296
+ </Select>
297
+ </div>
298
+ <div className="space-y-2">
299
+ <Label>Apply as</Label>
300
+ <Select
301
+ value={applyMode}
302
+ onValueChange={(value) => {
303
+ setApplyMode(value as CmsImportApplyMode);
304
+ if (value !== "live") setIgnoreBlankFields(false);
305
+ setSummary(null);
306
+ }}
307
+ >
308
+ <SelectTrigger>
309
+ <SelectValue />
310
+ </SelectTrigger>
311
+ <SelectContent>
312
+ <SelectItem value="draft">Drafts</SelectItem>
313
+ <SelectItem value="live">Live content</SelectItem>
314
+ </SelectContent>
315
+ </Select>
316
+ </div>
317
+ </div>
318
+
319
+ <div className="rounded-md border bg-muted/20 p-3">
320
+ <div className="flex items-start gap-3">
321
+ <Checkbox
322
+ id={`${contentType}-ignore-blank-fields`}
323
+ checked={ignoreBlankFields}
324
+ disabled={!canIgnoreBlankFields}
325
+ onCheckedChange={(checked) => {
326
+ setIgnoreBlankFields(Boolean(checked));
327
+ setSummary(null);
328
+ }}
329
+ />
330
+ <div className="space-y-1">
331
+ <div className="flex items-center gap-2">
332
+ <Label
333
+ htmlFor={`${contentType}-ignore-blank-fields`}
334
+ className={!canIgnoreBlankFields ? "text-muted-foreground" : undefined}
335
+ >
336
+ Ignore blank fields
337
+ </Label>
338
+ <TooltipProvider>
339
+ <Tooltip>
340
+ <TooltipTrigger asChild>
341
+ <button
342
+ type="button"
343
+ className="inline-flex h-5 w-5 items-center justify-center rounded-full text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
344
+ aria-label="Minimum CSV columns"
345
+ >
346
+ <HelpCircle className="h-4 w-4" />
347
+ </button>
348
+ </TooltipTrigger>
349
+ <TooltipContent className="max-w-xs">
350
+ {getMinimumRequirementText(contentType)}
351
+ </TooltipContent>
352
+ </Tooltip>
353
+ </TooltipProvider>
354
+ </div>
355
+ <p className="text-xs text-muted-foreground">
356
+ When enabled, empty CSV cells are skipped instead of clearing existing values.
357
+ {contentType === "products"
358
+ ? " Use live overwrite imports for partial updates like SKU, language, and price."
359
+ : " Use live overwrite imports for partial content updates."}
360
+ </p>
361
+ </div>
362
+ </div>
363
+ </div>
364
+
365
+ <SummaryPanel summary={summary} />
366
+ </div>
367
+
368
+ <DialogFooter className="gap-2 sm:gap-0">
369
+ <Button
370
+ type="button"
371
+ variant="outline"
372
+ onClick={reviewImport}
373
+ disabled={isPending || !csv.trim()}
374
+ >
375
+ {isPending ? <Spinner className="mr-2 h-4 w-4" /> : <FileDown className="mr-2 h-4 w-4" />}
376
+ Review Import
377
+ </Button>
378
+ <Button
379
+ type="button"
380
+ onClick={applyImport}
381
+ disabled={isPending || !summary?.success || !csv.trim()}
382
+ >
383
+ {isPending ? <Spinner className="mr-2 h-4 w-4" /> : <Upload className="mr-2 h-4 w-4" />}
384
+ Apply Import
385
+ </Button>
386
+ </DialogFooter>
387
+ </DialogContent>
388
+ </Dialog>
389
+ </div>
390
+ );
391
+ }
@@ -0,0 +1,226 @@
1
+ "use server";
2
+
3
+ import {
4
+ applyBundleImport,
5
+ applyCsvImport,
6
+ buildCsvTemplate,
7
+ dryRunBundleImport,
8
+ dryRunCsvImport,
9
+ exportBackupBundle,
10
+ exportCsv,
11
+ } from "../../../lib/cms-transfer/server";
12
+ import type {
13
+ CmsContentType,
14
+ CmsImportApplyMode,
15
+ CmsImportConflictMode,
16
+ CmsTransferActionResult,
17
+ } from "../../../lib/cms-transfer/types";
18
+
19
+ function dateStamp() {
20
+ return new Date().toISOString().slice(0, 10);
21
+ }
22
+
23
+ function contentLabel(contentType: CmsContentType) {
24
+ return contentType === "pages" ? "pages" : contentType === "posts" ? "posts" : "products";
25
+ }
26
+
27
+ export async function getCmsCsvTemplateAction(
28
+ contentType: CmsContentType
29
+ ): Promise<CmsTransferActionResult> {
30
+ try {
31
+ return {
32
+ success: true,
33
+ fileName: `nextblock-${contentLabel(contentType)}-import-template.csv`,
34
+ mimeType: "text/csv",
35
+ content: buildCsvTemplate(contentType),
36
+ };
37
+ } catch (error) {
38
+ return {
39
+ success: false,
40
+ error: error instanceof Error ? error.message : "Failed to build template.",
41
+ };
42
+ }
43
+ }
44
+
45
+ export async function exportCmsCsvAction(params: {
46
+ contentType: CmsContentType;
47
+ languageId?: number;
48
+ }): Promise<CmsTransferActionResult> {
49
+ try {
50
+ return {
51
+ success: true,
52
+ fileName: `nextblock-${contentLabel(params.contentType)}-${dateStamp()}.csv`,
53
+ mimeType: "text/csv",
54
+ content: await exportCsv(params),
55
+ };
56
+ } catch (error) {
57
+ return {
58
+ success: false,
59
+ error: error instanceof Error ? error.message : "Failed to export CSV.",
60
+ };
61
+ }
62
+ }
63
+
64
+ export async function dryRunCmsCsvImportAction(params: {
65
+ contentType: CmsContentType;
66
+ csv: string;
67
+ conflictMode: CmsImportConflictMode;
68
+ applyMode: CmsImportApplyMode;
69
+ ignoreBlankFields?: boolean;
70
+ }) {
71
+ try {
72
+ return await dryRunCsvImport({
73
+ contentType: params.contentType,
74
+ csv: params.csv,
75
+ options: {
76
+ conflictMode: params.conflictMode,
77
+ applyMode: params.applyMode,
78
+ ignoreBlankFields: params.ignoreBlankFields,
79
+ },
80
+ });
81
+ } catch (error) {
82
+ return {
83
+ success: false,
84
+ totalRows: 0,
85
+ created: 0,
86
+ updated: 0,
87
+ skipped: 0,
88
+ preview: [],
89
+ warnings: [],
90
+ errors: [
91
+ {
92
+ row: 0,
93
+ type: "error" as const,
94
+ message: error instanceof Error ? error.message : "Failed to dry-run import.",
95
+ },
96
+ ],
97
+ };
98
+ }
99
+ }
100
+
101
+ export async function applyCmsCsvImportAction(params: {
102
+ contentType: CmsContentType;
103
+ csv: string;
104
+ conflictMode: CmsImportConflictMode;
105
+ applyMode: CmsImportApplyMode;
106
+ ignoreBlankFields?: boolean;
107
+ }) {
108
+ try {
109
+ return await applyCsvImport({
110
+ contentType: params.contentType,
111
+ csv: params.csv,
112
+ options: {
113
+ conflictMode: params.conflictMode,
114
+ applyMode: params.applyMode,
115
+ ignoreBlankFields: params.ignoreBlankFields,
116
+ },
117
+ });
118
+ } catch (error) {
119
+ return {
120
+ success: false,
121
+ totalRows: 0,
122
+ created: 0,
123
+ updated: 0,
124
+ skipped: 0,
125
+ preview: [],
126
+ warnings: [],
127
+ errors: [
128
+ {
129
+ row: 0,
130
+ type: "error" as const,
131
+ message: error instanceof Error ? error.message : "Failed to apply import.",
132
+ },
133
+ ],
134
+ };
135
+ }
136
+ }
137
+
138
+ export async function exportCmsBackupBundleAction(): Promise<CmsTransferActionResult> {
139
+ try {
140
+ return {
141
+ success: true,
142
+ fileName: `nextblock-content-backup-${dateStamp()}.json`,
143
+ mimeType: "application/json",
144
+ content: await exportBackupBundle(),
145
+ };
146
+ } catch (error) {
147
+ return {
148
+ success: false,
149
+ error: error instanceof Error ? error.message : "Failed to export backup bundle.",
150
+ };
151
+ }
152
+ }
153
+
154
+ export async function dryRunCmsBackupBundleImportAction(params: {
155
+ bundleJson: string;
156
+ contentTypes: CmsContentType[];
157
+ conflictMode: CmsImportConflictMode;
158
+ applyMode: CmsImportApplyMode;
159
+ includeBlocks?: boolean;
160
+ }) {
161
+ try {
162
+ return await dryRunBundleImport({
163
+ bundleJson: params.bundleJson,
164
+ contentTypes: params.contentTypes,
165
+ includeBlocks: params.includeBlocks,
166
+ options: {
167
+ conflictMode: params.conflictMode,
168
+ applyMode: params.applyMode,
169
+ },
170
+ });
171
+ } catch (error) {
172
+ return {
173
+ success: false,
174
+ totalRows: 0,
175
+ created: 0,
176
+ updated: 0,
177
+ skipped: 0,
178
+ preview: [],
179
+ warnings: [],
180
+ errors: [
181
+ {
182
+ row: 0,
183
+ type: "error" as const,
184
+ message: error instanceof Error ? error.message : "Failed to dry-run backup import.",
185
+ },
186
+ ],
187
+ };
188
+ }
189
+ }
190
+
191
+ export async function applyCmsBackupBundleImportAction(params: {
192
+ bundleJson: string;
193
+ contentTypes: CmsContentType[];
194
+ conflictMode: CmsImportConflictMode;
195
+ applyMode: CmsImportApplyMode;
196
+ includeBlocks?: boolean;
197
+ }) {
198
+ try {
199
+ return await applyBundleImport({
200
+ bundleJson: params.bundleJson,
201
+ contentTypes: params.contentTypes,
202
+ includeBlocks: params.includeBlocks,
203
+ options: {
204
+ conflictMode: params.conflictMode,
205
+ applyMode: params.applyMode,
206
+ },
207
+ });
208
+ } catch (error) {
209
+ return {
210
+ success: false,
211
+ totalRows: 0,
212
+ created: 0,
213
+ updated: 0,
214
+ skipped: 0,
215
+ preview: [],
216
+ warnings: [],
217
+ errors: [
218
+ {
219
+ row: 0,
220
+ type: "error" as const,
221
+ message: error instanceof Error ? error.message : "Failed to apply backup import.",
222
+ },
223
+ ],
224
+ };
225
+ }
226
+ }
@@ -1,10 +1,29 @@
1
- import 'katex/dist/katex.min.css';
2
- import CmsClientLayout from "./CmsClientLayout";
3
-
4
- export default function CmsLayout({
5
- children,
6
- }: {
7
- children: React.ReactNode;
8
- }) {
9
- return <CmsClientLayout>{children}</CmsClientLayout>;
10
- }
1
+ import 'katex/dist/katex.min.css';
2
+ import { redirect } from 'next/navigation';
3
+ import CmsClientLayout from "./CmsClientLayout";
4
+ import { verifyPackageOnline } from '@nextblock-cms/db/server';
5
+ import { evaluateTwoFactor } from '../../lib/auth/twoFactor';
6
+
7
+ export default async function CmsLayout({
8
+ children,
9
+ }: {
10
+ children: React.ReactNode;
11
+ }) {
12
+ // Enforce any outstanding second factor before rendering the CMS. This guards
13
+ // direct navigation to /cms/* with an aal1 (password-only) session.
14
+ const twoFactor = await evaluateTwoFactor();
15
+ if (twoFactor.status === 'totp_required' || twoFactor.status === 'email_required') {
16
+ redirect('/two-factor?redirect_to=/cms/dashboard');
17
+ }
18
+
19
+ const [isEcommerceActive, isCortexAiActive] = await Promise.all([
20
+ verifyPackageOnline('ecommerce'),
21
+ verifyPackageOnline('cortex-ai'),
22
+ ]);
23
+
24
+ return (
25
+ <CmsClientLayout isCortexAiActive={isCortexAiActive} isEcommerceActive={isEcommerceActive}>
26
+ {children}
27
+ </CmsClientLayout>
28
+ );
29
+ }
@@ -1,22 +1,22 @@
1
- "use client";
2
-
3
- import React, { createContext, useContext } from "react";
4
-
5
- type UploadFolderContextValue = {
6
- defaultFolder?: string | null;
7
- };
8
-
9
- const UploadFolderContext = createContext<UploadFolderContextValue>({ defaultFolder: undefined });
10
-
11
- export function UploadFolderProvider({ defaultFolder, children }: { defaultFolder?: string; children: React.ReactNode }) {
12
- return (
13
- <UploadFolderContext.Provider value={{ defaultFolder }}>
14
- {children}
15
- </UploadFolderContext.Provider>
16
- );
17
- }
18
-
19
- export function useUploadFolder() {
20
- return useContext(UploadFolderContext);
21
- }
22
-
1
+ "use client";
2
+
3
+ import React, { createContext, useContext } from "react";
4
+
5
+ type UploadFolderContextValue = {
6
+ defaultFolder?: string | null;
7
+ };
8
+
9
+ const UploadFolderContext = createContext<UploadFolderContextValue>({ defaultFolder: undefined });
10
+
11
+ export function UploadFolderProvider({ defaultFolder, children }: { defaultFolder?: string; children: React.ReactNode }) {
12
+ return (
13
+ <UploadFolderContext.Provider value={{ defaultFolder }}>
14
+ {children}
15
+ </UploadFolderContext.Provider>
16
+ );
17
+ }
18
+
19
+ export function useUploadFolder() {
20
+ return useContext(UploadFolderContext);
21
+ }
22
+