create-nextblock 0.13.10 → 0.13.12

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 (259) hide show
  1. package/docker-template/.dockerignore +23 -23
  2. package/docker-template/.env.docker.example +69 -69
  3. package/docker-template/docker/db/init/99-jwt.sql +6 -6
  4. package/docker-template/docker/db/init/99-roles.sql +25 -25
  5. package/docker-template/docker/kong/kong.yml +112 -112
  6. package/docker-template/scripts/docker-setup.mjs +310 -310
  7. package/libs/db/tsconfig.lib.json +3 -3
  8. package/libs/editor/tsconfig.lib.json +3 -3
  9. package/libs/ui/tsconfig.lib.json +3 -3
  10. package/libs/utils/tsconfig.json +3 -3
  11. package/package.json +1 -1
  12. package/project.json +19 -19
  13. package/templates/nextblock-template/.browserslistrc +11 -11
  14. package/templates/nextblock-template/.dockerignore +23 -23
  15. package/templates/nextblock-template/README.md +34 -34
  16. package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
  17. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
  18. package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +46 -46
  19. package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -51
  20. package/templates/nextblock-template/app/ToasterProvider.tsx +17 -17
  21. package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
  22. package/templates/nextblock-template/app/actions/consent.ts +57 -57
  23. package/templates/nextblock-template/app/actions/postActions.ts +146 -146
  24. package/templates/nextblock-template/app/actions/productGridActions.ts +40 -40
  25. package/templates/nextblock-template/app/actions/twoFactorEmail.ts +22 -22
  26. package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -153
  27. package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -96
  28. package/templates/nextblock-template/app/api/brand/email-logo/route.ts +48 -48
  29. package/templates/nextblock-template/app/api/cms/check-updates/route.ts +44 -44
  30. package/templates/nextblock-template/app/api/cms/ecommerce/product-picker/route.ts +151 -151
  31. package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -33
  32. package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -63
  33. package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3490 -3490
  34. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +5749 -5609
  35. package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +39 -39
  36. package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -92
  37. package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -43
  38. package/templates/nextblock-template/app/api/media/library/route.ts +69 -69
  39. package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -53
  40. package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
  41. package/templates/nextblock-template/app/api/search/route.ts +43 -43
  42. package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +441 -441
  43. package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
  44. package/templates/nextblock-template/app/cart/page.tsx +7 -7
  45. package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -160
  46. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
  47. package/templates/nextblock-template/app/cms/blocks/components/MultiEntityPicker.tsx +263 -263
  48. package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -167
  49. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +90 -90
  50. package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -122
  51. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
  52. package/templates/nextblock-template/app/cms/components/EcommerceActiveContext.tsx +27 -27
  53. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
  54. package/templates/nextblock-template/app/cms/components/SystemAlertsBanner.tsx +112 -112
  55. package/templates/nextblock-template/app/cms/components/TwoFactorReminderBanner.tsx +45 -45
  56. package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -102
  57. package/templates/nextblock-template/app/cms/components/system-alerts-actions.ts +31 -31
  58. package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -66
  59. package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -519
  60. package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -256
  61. package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -384
  62. package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -221
  63. package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -12
  64. package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -438
  65. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
  66. package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +130 -130
  67. package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
  68. package/templates/nextblock-template/app/cms/interactions/EmailRecipientsInput.tsx +189 -189
  69. package/templates/nextblock-template/app/cms/layout.tsx +73 -73
  70. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
  71. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
  72. package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
  73. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
  74. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
  75. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
  76. package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -12
  77. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
  78. package/templates/nextblock-template/app/cms/products/new/page.tsx +135 -135
  79. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
  80. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
  81. package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -456
  82. package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -115
  83. package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -31
  84. package/templates/nextblock-template/app/cms/revisions/service.ts +19 -19
  85. package/templates/nextblock-template/app/cms/revisions/utils.ts +132 -132
  86. package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -1004
  87. package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -29
  88. package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
  89. package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +497 -497
  90. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
  91. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
  92. package/templates/nextblock-template/app/cms/settings/email/page.tsx +28 -28
  93. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
  94. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
  95. package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
  96. package/templates/nextblock-template/app/cms/settings/languages/components/LanguageDetectionPanel.tsx +188 -188
  97. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  98. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
  99. package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -21
  100. package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +20 -20
  101. package/templates/nextblock-template/app/cms/settings/logos/components/SetActiveLogoButton.tsx +42 -42
  102. package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -133
  103. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  104. package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -122
  105. package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
  106. package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
  107. package/templates/nextblock-template/app/cms/settings/security/page.tsx +33 -33
  108. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
  109. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
  110. package/templates/nextblock-template/app/cms/users/components/CreateUserForm.tsx +217 -217
  111. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
  112. package/templates/nextblock-template/app/cms/users/new/page.tsx +44 -44
  113. package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
  114. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
  115. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
  116. package/templates/nextblock-template/app/profile/account-links.ts +22 -22
  117. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
  118. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
  119. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
  120. package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
  121. package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
  122. package/templates/nextblock-template/app/setup/SetupWizard.tsx +678 -678
  123. package/templates/nextblock-template/app/setup/layout.tsx +13 -13
  124. package/templates/nextblock-template/app/setup/page.tsx +111 -111
  125. package/templates/nextblock-template/app/sitemap.ts +130 -130
  126. package/templates/nextblock-template/components/BlockRenderer.tsx +4 -0
  127. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
  128. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
  129. package/templates/nextblock-template/components/DeferredGoogleAnalytics.tsx +70 -70
  130. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
  131. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
  132. package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -47
  133. package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
  134. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
  135. package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
  136. package/templates/nextblock-template/components/PublicEnvBootstrap.tsx +30 -30
  137. package/templates/nextblock-template/components/ResponsiveNav.tsx +14 -14
  138. package/templates/nextblock-template/components/auth/AuthBotProtection.tsx +182 -182
  139. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
  140. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
  141. package/templates/nextblock-template/components/blocks/ProductGridClient.tsx +114 -114
  142. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
  143. package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +8 -1
  144. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +249 -249
  145. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +3 -0
  146. package/templates/nextblock-template/components/blocks/renderers/VideoEmbedBlockRenderer.tsx +33 -23
  147. package/templates/nextblock-template/components/blocks/types.ts +7 -7
  148. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  149. package/templates/nextblock-template/components/form-message.tsx +32 -32
  150. package/templates/nextblock-template/components/media/YouTubeFacade.tsx +105 -0
  151. package/templates/nextblock-template/components/media/youtube-embed-replace.tsx +32 -0
  152. package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +170 -170
  153. package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +70 -70
  154. package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -28
  155. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -166
  156. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +471 -464
  157. package/templates/nextblock-template/components/submit-button.tsx +23 -23
  158. package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
  159. package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -21
  160. package/templates/nextblock-template/context/AuthContext.tsx +23 -23
  161. package/templates/nextblock-template/context/language-rest-client.ts +32 -32
  162. package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -6
  163. package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -25
  164. package/templates/nextblock-template/docker/kong/kong.yml +112 -112
  165. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
  166. package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -364
  167. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -202
  168. package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +246 -246
  169. package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +176 -176
  170. package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -146
  171. package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -222
  172. package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +173 -173
  173. package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +170 -170
  174. package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +151 -151
  175. package/templates/nextblock-template/docs/README.md +39 -39
  176. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
  177. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
  178. package/templates/nextblock-template/index.d.ts +7 -7
  179. package/templates/nextblock-template/lib/app-secrets.ts +39 -39
  180. package/templates/nextblock-template/lib/auth/cookies.ts +47 -47
  181. package/templates/nextblock-template/lib/auth/crypto.ts +45 -45
  182. package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -92
  183. package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
  184. package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +78 -78
  185. package/templates/nextblock-template/lib/blocks/README.md +13 -13
  186. package/templates/nextblock-template/lib/blocks/product-grid-data.ts +210 -210
  187. package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
  188. package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -2243
  189. package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -145
  190. package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -87
  191. package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -178
  192. package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -140
  193. package/templates/nextblock-template/lib/custom-block-r2-upload.ts +88 -88
  194. package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -227
  195. package/templates/nextblock-template/lib/custom-block-relations.ts +279 -279
  196. package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -14
  197. package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -172
  198. package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -213
  199. package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -22
  200. package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -193
  201. package/templates/nextblock-template/lib/email/branding-format.test.ts +133 -133
  202. package/templates/nextblock-template/lib/email/branding-format.ts +123 -123
  203. package/templates/nextblock-template/lib/email/branding.ts +76 -76
  204. package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -121
  205. package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -206
  206. package/templates/nextblock-template/lib/full-backup/server.ts +743 -743
  207. package/templates/nextblock-template/lib/i18n/country-languages.ts +247 -247
  208. package/templates/nextblock-template/lib/i18n/detection.test.ts +197 -197
  209. package/templates/nextblock-template/lib/i18n/detection.ts +192 -192
  210. package/templates/nextblock-template/lib/logos/active-logo.ts +53 -53
  211. package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +54 -54
  212. package/templates/nextblock-template/lib/media/youtube.ts +99 -0
  213. package/templates/nextblock-template/lib/onboarding/actions.ts +31 -31
  214. package/templates/nextblock-template/lib/onboarding/status.ts +222 -222
  215. package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
  216. package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -57
  217. package/templates/nextblock-template/lib/privacy/contact-emails.ts +64 -64
  218. package/templates/nextblock-template/lib/privacy/settings.ts +115 -115
  219. package/templates/nextblock-template/lib/privacy/types.ts +69 -69
  220. package/templates/nextblock-template/lib/promotions/server.test.ts +74 -74
  221. package/templates/nextblock-template/lib/promotions/server.ts +741 -741
  222. package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -142
  223. package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -255
  224. package/templates/nextblock-template/lib/search/types.ts +27 -27
  225. package/templates/nextblock-template/lib/setup/actions.ts +460 -460
  226. package/templates/nextblock-template/lib/setup/env-status.ts +125 -125
  227. package/templates/nextblock-template/lib/setup/env-write.ts +111 -111
  228. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +87 -82
  229. package/templates/nextblock-template/lib/setup/provisioning.ts +59 -59
  230. package/templates/nextblock-template/lib/setup/schema-apply.ts +408 -408
  231. package/templates/nextblock-template/lib/setup/system-config.ts +105 -105
  232. package/templates/nextblock-template/lib/setup/types.ts +18 -18
  233. package/templates/nextblock-template/lib/site-url.ts +48 -48
  234. package/templates/nextblock-template/lib/storage/provider.ts +66 -66
  235. package/templates/nextblock-template/lib/storage/supabase-storage.ts +103 -103
  236. package/templates/nextblock-template/lib/updates/check-upstream.ts +441 -441
  237. package/templates/nextblock-template/lib/updates/github-device.ts +206 -206
  238. package/templates/nextblock-template/lib/updates/repo-identity.ts +56 -56
  239. package/templates/nextblock-template/next-env.d.ts +1 -1
  240. package/templates/nextblock-template/next.config.js +6 -0
  241. package/templates/nextblock-template/package.json +1 -1
  242. package/templates/nextblock-template/postcss.config.js +6 -6
  243. package/templates/nextblock-template/scripts/backup.js +115 -115
  244. package/templates/nextblock-template/scripts/docker-setup.mjs +310 -310
  245. package/templates/nextblock-template/scripts/restore.js +385 -385
  246. package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +98 -98
  247. package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -62
  248. package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -537
  249. package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -58
  250. package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -188
  251. package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -123
  252. package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -133
  253. package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -65
  254. package/templates/nextblock-template/tailwind.config.js +25 -25
  255. package/templates/nextblock-template/tools/build-migrate.mjs +209 -209
  256. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
  257. package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
  258. package/templates/nextblock-template/types/jsdom.d.ts +6 -6
  259. package/tsconfig.base.json +3 -3
@@ -1,151 +1,151 @@
1
- # 13 · Staying Up to Date (Automated Upstream Updates)
2
-
3
- NextBlock keeps your instance in sync with the upstream project
4
- (`nextblock-cms/nextblock`) with **as little manual work as possible**. How updates
5
- arrive depends on how you deployed, and the system auto-detects which path applies:
6
-
7
- | Install type | Track | How updates arrive |
8
- | :--- | :--- | :--- |
9
- | Vercel 1-click / GitHub fork (git-backed) | **A** | A daily GitHub Action merges upstream and pushes to your deploy branch (→ Vercel CD). |
10
- | `npm create nextblock` / local clone / Docker image (standalone) | **B** | The CMS checks GitHub Releases and shows a "download the new version" banner. |
11
-
12
- Both tracks surface their status in the CMS through a dashboard banner backed by the
13
- `system_alerts` table (migration `00000000000036`). Reads are ADMIN-only (RLS).
14
-
15
- ---
16
-
17
- ## Track A — Git-backed installs (Vercel 1-click, GitHub forks)
18
-
19
- The workflow lives at [`.github/workflows/nextblock-sync.yml`](../.github/workflows/nextblock-sync.yml).
20
- It runs **daily at 00:00 UTC** and on demand (**Actions → NextBlock Upstream Sync → Run
21
- workflow**). Each run:
22
-
23
- 1. Merges the upstream release branch into your deploy branch.
24
- 2. **Clean merge** → commits and pushes to your branch, which triggers a normal Vercel
25
- deployment. Any open conflict issue is auto-closed.
26
- 3. **Conflict** → aborts the merge and opens (or updates) a GitHub Issue labeled
27
- `nextblock-sync-conflict`. The CMS mirrors that issue into an **amber banner** on the
28
- dashboard with a link to resolve it. Once you resolve and **close the issue**, the
29
- banner clears automatically.
30
-
31
- ### One-click install (Connect GitHub)
32
-
33
- Vercel's 1-click deploy creates your repo through an integration whose token lacks the
34
- GitHub **`workflow`** scope, so GitHub **strips `.github/workflows/`** from the copy — your
35
- new repo won't have the sync workflow even though the template ships it. To fix that with no
36
- token to create, the dashboard onboarding step shows a **Connect GitHub** button:
37
-
38
- 1. Click **Connect GitHub** — a short code appears.
39
- 2. Click **Authorize on GitHub**, enter the code, approve.
40
- 3. NextBlock installs `.github/workflows/nextblock-sync.yml` into your repo for you, and the
41
- step turns green.
42
-
43
- This uses GitHub's **device flow** — no token to create, no per-site callback, nothing to
44
- configure (the public client id ships with NextBlock). The authorization requests the
45
- `repo` + `workflow` scopes because GitHub requires them to write a workflow file; NextBlock
46
- uses the grant once to install the file and does **not** store it. Revoke it anytime at
47
- GitHub → **Settings → Applications**.
48
-
49
- ### Do you need to enable GitHub Actions?
50
-
51
- It depends on how the repository was created:
52
-
53
- - **Vercel 1-click deploy** creates a **new repository you own** (a copy, *not* a GitHub
54
- fork). GitHub **enables Actions by default** on repos you own — **there's nothing to turn
55
- on**. The sync workflow runs automatically once it lands on your repo's **default branch**.
56
- - **A manual GitHub _fork_** (the "Fork" button) has Actions **disabled** by default. Enable
57
- them once: your repo → **Actions** tab → **"I understand my workflows, go ahead and enable
58
- them."**
59
-
60
- > **Seeing GitHub's "Get started with Actions / choose a workflow" page?** That only means
61
- > your Actions tab is **empty** — `.github/workflows/nextblock-sync.yml` isn't on your
62
- > **default branch** yet (scheduled workflows only run from the default branch). Once it is,
63
- > the tab shows **NextBlock Upstream Sync** with a **Run workflow** button. There is no
64
- > separate "enable" button on an owned repo because Actions are already on.
65
-
66
- The dashboard onboarding step ("Automatic updates (GitHub Actions)") links to **Settings →
67
- Actions** — where you can confirm Actions are allowed — and completes itself once GitHub
68
- reports the sync workflow as active.
69
-
70
- ### No GitHub secrets required (public forks)
71
-
72
- The conflict signal uses the **`GITHUB_TOKEN` that GitHub provides to every workflow
73
- automatically** — you do **not** add any Supabase secret to GitHub. The app writes the
74
- dashboard alert itself using the Supabase key it already has, and reads your repo's
75
- conflict issues over the public GitHub API.
76
-
77
- > **We recommend forking to a _public_ repository** — it's fully zero-config.
78
-
79
- ### Private forks
80
-
81
- If your fork is **private**, the public GitHub API can't read its issues, so add **one**
82
- environment variable to your deployment (Vercel project → Settings → Environment
83
- Variables, or your `.env`):
84
-
85
- | Variable | Value |
86
- | :--- | :--- |
87
- | `NEXTBLOCK_GITHUB_TOKEN` | A GitHub token with **read access to issues** on your fork (a fine-grained PAT scoped to the repo, or a classic token with `repo`). |
88
-
89
- With that set, the dashboard conflict banner works on private forks too. (The workflow
90
- itself still needs no extra secret — `GITHUB_TOKEN` covers it either way.)
91
-
92
- > **⚠️ Vercel Hobby (free) plan + a private repo blocks auto-deploys.** On Hobby, Vercel only
93
- > deploys **private**-repo commits authored by the project owner and rejects automated
94
- > (bot/collaborator) commits — so the auto-merge push won't deploy (*"Hobby Plan does not
95
- > support collaboration for private repositories"*). Either **make the repo public**
96
- > (recommended — it also makes the conflict banner tokenless) or upgrade to **Vercel Pro**.
97
- > Public repos have no such restriction on Hobby.
98
-
99
- ### How the dashboard stays current (no cron)
100
-
101
- The CMS refreshes update/conflict status **in the background after a dashboard page
102
- loads** (throttled to ~6 hours), so it works on Vercel's Hobby plan without consuming a
103
- cron slot. Admins can also force a check immediately:
104
-
105
- ```
106
- POST /api/cms/check-updates # admin-only; returns the version + conflict status
107
- ```
108
-
109
- ---
110
-
111
- ## Track B — Standalone installs (npm create / local / Docker)
112
-
113
- These installs aren't wired to a GitHub Action, so NextBlock checks the **GitHub Releases
114
- API** and, when a newer release exists, records a `runtime_update_available` alert — an
115
- **indigo banner** on the dashboard with a direct **download link** to the release tarball.
116
- Updating is manual by design: download the archive, replace your files, and update
117
- dependencies (`npm install`). The same admin check endpoint above triggers a check on
118
- demand.
119
-
120
- ---
121
-
122
- ## Schema stays in step with deploys (build-time migrations)
123
-
124
- So a new version's code never runs against an old schema, a build-time hook
125
- ([`apps/nextblock/tools/build-migrate.mjs`](../apps/nextblock/tools/build-migrate.mjs))
126
- applies pending, forward-only migrations **before** `next build`:
127
-
128
- - **Vercel:** runs automatically when `VERCEL_ENV=production`; **preview/development
129
- builds are skipped** so they never touch live data.
130
- - **Standalone / local / Docker:** gated on `NEXTBLOCK_BUILD_MIGRATE=1`, which the
131
- `/setup` wizard and the create/Docker setup scripts write into your env automatically.
132
-
133
- It is **non-destructive and never breaks the build** — if the database is unreachable it
134
- logs a warning and continues. Migrations are tracked in `supabase_migrations.schema_migrations`,
135
- identically to the Supabase CLI.
136
-
137
- > **Edge case:** if your project's migration history is empty/inconsistent, the hook skips
138
- > rather than risk misapplying. Run `npm run db:migrate:repair-history` then
139
- > `npm run db:migrate` once to reconcile (see [docs/04](./04-DATABASE-AND-AUTH.md)).
140
-
141
- ---
142
-
143
- ## Quick reference
144
-
145
- | You want… | Do this |
146
- | :--- | :--- |
147
- | Fully hands-off updates | Fork **public**, deploy on Vercel, **enable Actions** once. |
148
- | Conflict banners on a **private** fork | Also set `NEXTBLOCK_GITHUB_TOKEN`. |
149
- | To update a **standalone** install | Watch for the dashboard banner → download → replace → `npm install`. |
150
- | To force an update check now | Dashboard (admin) → it polls in the background; or `POST /api/cms/check-updates`. |
151
- | To resolve a sync conflict | Open the linked GitHub issue, merge upstream locally, fix, push, close the issue. |
1
+ # 13 · Staying Up to Date (Automated Upstream Updates)
2
+
3
+ NextBlock keeps your instance in sync with the upstream project
4
+ (`nextblock-cms/nextblock`) with **as little manual work as possible**. How updates
5
+ arrive depends on how you deployed, and the system auto-detects which path applies:
6
+
7
+ | Install type | Track | How updates arrive |
8
+ | :--- | :--- | :--- |
9
+ | Vercel 1-click / GitHub fork (git-backed) | **A** | A daily GitHub Action merges upstream and pushes to your deploy branch (→ Vercel CD). |
10
+ | `npm create nextblock` / local clone / Docker image (standalone) | **B** | The CMS checks GitHub Releases and shows a "download the new version" banner. |
11
+
12
+ Both tracks surface their status in the CMS through a dashboard banner backed by the
13
+ `system_alerts` table (migration `00000000000036`). Reads are ADMIN-only (RLS).
14
+
15
+ ---
16
+
17
+ ## Track A — Git-backed installs (Vercel 1-click, GitHub forks)
18
+
19
+ The workflow lives at [`.github/workflows/nextblock-sync.yml`](../.github/workflows/nextblock-sync.yml).
20
+ It runs **daily at 00:00 UTC** and on demand (**Actions → NextBlock Upstream Sync → Run
21
+ workflow**). Each run:
22
+
23
+ 1. Merges the upstream release branch into your deploy branch.
24
+ 2. **Clean merge** → commits and pushes to your branch, which triggers a normal Vercel
25
+ deployment. Any open conflict issue is auto-closed.
26
+ 3. **Conflict** → aborts the merge and opens (or updates) a GitHub Issue labeled
27
+ `nextblock-sync-conflict`. The CMS mirrors that issue into an **amber banner** on the
28
+ dashboard with a link to resolve it. Once you resolve and **close the issue**, the
29
+ banner clears automatically.
30
+
31
+ ### One-click install (Connect GitHub)
32
+
33
+ Vercel's 1-click deploy creates your repo through an integration whose token lacks the
34
+ GitHub **`workflow`** scope, so GitHub **strips `.github/workflows/`** from the copy — your
35
+ new repo won't have the sync workflow even though the template ships it. To fix that with no
36
+ token to create, the dashboard onboarding step shows a **Connect GitHub** button:
37
+
38
+ 1. Click **Connect GitHub** — a short code appears.
39
+ 2. Click **Authorize on GitHub**, enter the code, approve.
40
+ 3. NextBlock installs `.github/workflows/nextblock-sync.yml` into your repo for you, and the
41
+ step turns green.
42
+
43
+ This uses GitHub's **device flow** — no token to create, no per-site callback, nothing to
44
+ configure (the public client id ships with NextBlock). The authorization requests the
45
+ `repo` + `workflow` scopes because GitHub requires them to write a workflow file; NextBlock
46
+ uses the grant once to install the file and does **not** store it. Revoke it anytime at
47
+ GitHub → **Settings → Applications**.
48
+
49
+ ### Do you need to enable GitHub Actions?
50
+
51
+ It depends on how the repository was created:
52
+
53
+ - **Vercel 1-click deploy** creates a **new repository you own** (a copy, *not* a GitHub
54
+ fork). GitHub **enables Actions by default** on repos you own — **there's nothing to turn
55
+ on**. The sync workflow runs automatically once it lands on your repo's **default branch**.
56
+ - **A manual GitHub _fork_** (the "Fork" button) has Actions **disabled** by default. Enable
57
+ them once: your repo → **Actions** tab → **"I understand my workflows, go ahead and enable
58
+ them."**
59
+
60
+ > **Seeing GitHub's "Get started with Actions / choose a workflow" page?** That only means
61
+ > your Actions tab is **empty** — `.github/workflows/nextblock-sync.yml` isn't on your
62
+ > **default branch** yet (scheduled workflows only run from the default branch). Once it is,
63
+ > the tab shows **NextBlock Upstream Sync** with a **Run workflow** button. There is no
64
+ > separate "enable" button on an owned repo because Actions are already on.
65
+
66
+ The dashboard onboarding step ("Automatic updates (GitHub Actions)") links to **Settings →
67
+ Actions** — where you can confirm Actions are allowed — and completes itself once GitHub
68
+ reports the sync workflow as active.
69
+
70
+ ### No GitHub secrets required (public forks)
71
+
72
+ The conflict signal uses the **`GITHUB_TOKEN` that GitHub provides to every workflow
73
+ automatically** — you do **not** add any Supabase secret to GitHub. The app writes the
74
+ dashboard alert itself using the Supabase key it already has, and reads your repo's
75
+ conflict issues over the public GitHub API.
76
+
77
+ > **We recommend forking to a _public_ repository** — it's fully zero-config.
78
+
79
+ ### Private forks
80
+
81
+ If your fork is **private**, the public GitHub API can't read its issues, so add **one**
82
+ environment variable to your deployment (Vercel project → Settings → Environment
83
+ Variables, or your `.env`):
84
+
85
+ | Variable | Value |
86
+ | :--- | :--- |
87
+ | `NEXTBLOCK_GITHUB_TOKEN` | A GitHub token with **read access to issues** on your fork (a fine-grained PAT scoped to the repo, or a classic token with `repo`). |
88
+
89
+ With that set, the dashboard conflict banner works on private forks too. (The workflow
90
+ itself still needs no extra secret — `GITHUB_TOKEN` covers it either way.)
91
+
92
+ > **⚠️ Vercel Hobby (free) plan + a private repo blocks auto-deploys.** On Hobby, Vercel only
93
+ > deploys **private**-repo commits authored by the project owner and rejects automated
94
+ > (bot/collaborator) commits — so the auto-merge push won't deploy (*"Hobby Plan does not
95
+ > support collaboration for private repositories"*). Either **make the repo public**
96
+ > (recommended — it also makes the conflict banner tokenless) or upgrade to **Vercel Pro**.
97
+ > Public repos have no such restriction on Hobby.
98
+
99
+ ### How the dashboard stays current (no cron)
100
+
101
+ The CMS refreshes update/conflict status **in the background after a dashboard page
102
+ loads** (throttled to ~6 hours), so it works on Vercel's Hobby plan without consuming a
103
+ cron slot. Admins can also force a check immediately:
104
+
105
+ ```
106
+ POST /api/cms/check-updates # admin-only; returns the version + conflict status
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Track B — Standalone installs (npm create / local / Docker)
112
+
113
+ These installs aren't wired to a GitHub Action, so NextBlock checks the **GitHub Releases
114
+ API** and, when a newer release exists, records a `runtime_update_available` alert — an
115
+ **indigo banner** on the dashboard with a direct **download link** to the release tarball.
116
+ Updating is manual by design: download the archive, replace your files, and update
117
+ dependencies (`npm install`). The same admin check endpoint above triggers a check on
118
+ demand.
119
+
120
+ ---
121
+
122
+ ## Schema stays in step with deploys (build-time migrations)
123
+
124
+ So a new version's code never runs against an old schema, a build-time hook
125
+ ([`apps/nextblock/tools/build-migrate.mjs`](../apps/nextblock/tools/build-migrate.mjs))
126
+ applies pending, forward-only migrations **before** `next build`:
127
+
128
+ - **Vercel:** runs automatically when `VERCEL_ENV=production`; **preview/development
129
+ builds are skipped** so they never touch live data.
130
+ - **Standalone / local / Docker:** gated on `NEXTBLOCK_BUILD_MIGRATE=1`, which the
131
+ `/setup` wizard and the create/Docker setup scripts write into your env automatically.
132
+
133
+ It is **non-destructive and never breaks the build** — if the database is unreachable it
134
+ logs a warning and continues. Migrations are tracked in `supabase_migrations.schema_migrations`,
135
+ identically to the Supabase CLI.
136
+
137
+ > **Edge case:** if your project's migration history is empty/inconsistent, the hook skips
138
+ > rather than risk misapplying. Run `npm run db:migrate:repair-history` then
139
+ > `npm run db:migrate` once to reconcile (see [docs/04](./04-DATABASE-AND-AUTH.md)).
140
+
141
+ ---
142
+
143
+ ## Quick reference
144
+
145
+ | You want… | Do this |
146
+ | :--- | :--- |
147
+ | Fully hands-off updates | Fork **public**, deploy on Vercel, **enable Actions** once. |
148
+ | Conflict banners on a **private** fork | Also set `NEXTBLOCK_GITHUB_TOKEN`. |
149
+ | To update a **standalone** install | Watch for the dashboard banner → download → replace → `npm install`. |
150
+ | To force an update check now | Dashboard (admin) → it polls in the background; or `POST /api/cms/check-updates`. |
151
+ | To resolve a sync conflict | Open the linked GitHub issue, merge upstream locally, fix, push, close the issue. |
@@ -1,39 +1,39 @@
1
- # NextBlock CMS Documentation
2
-
3
- This folder is the source-of-truth reference set for the NextBlock monorepo.
4
- The numbered files are written from live code, routes, migrations, and shipped
5
- library surfaces rather than historical planning notes.
6
-
7
- ## Start Here
8
-
9
- - Product and architecture overview: [01-PROJECT-OVERVIEW.md](./01-PROJECT-OVERVIEW.md)
10
- - Commerce capabilities: [02-ECOMMERCE-CAPABILITIES.md](./02-ECOMMERCE-CAPABILITIES.md)
11
- - CMS editor and block system: [03-CMS-AND-EDITOR.md](./03-CMS-AND-EDITOR.md)
12
- - Database, auth, and migrations: [04-DATABASE-AND-AUTH.md](./04-DATABASE-AND-AUTH.md)
13
- - Contributor workflow and local operations: [05-DEVELOPER-GUIDE.md](./05-DEVELOPER-GUIDE.md)
14
- - CLI and scaffolded project flow: [06-CLI-AND-SCAFFOLDING.md](./06-CLI-AND-SCAFFOLDING.md)
15
- - Block SDK and extensibility surface: [07-BLOCK-SDK-AND-EXTENSIBILITY.md](./07-BLOCK-SDK-AND-EXTENSIBILITY.md)
16
- - Cortex AI architecture: [08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md](./08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md)
17
- - Live draft (visual editing) mode: [09-LIVE-DRAFT-MODE.md](./09-LIVE-DRAFT-MODE.md)
18
- - Custom blocks (data-driven CRUD): [10-CUSTOM-BLOCKS.md](./10-CUSTOM-BLOCKS.md)
19
- - Self-hosted local Docker stack: [11-SELF-HOSTED-DOCKER.md](./11-SELF-HOSTED-DOCKER.md)
20
- - One-click cloud deploy (Deploy to Vercel): [12-VERCEL-DEPLOYMENT.md](./12-VERCEL-DEPLOYMENT.md)
21
-
22
- ## Audience Guide
23
-
24
- - New contributors: read `01`, then `04`, then `05`.
25
- - Commerce work: read `02`, then `04`.
26
- - Editor or page-builder work: read `03`, then `07`.
27
- - Custom block work: read `03`, then `10`.
28
- - AI / Cortex work: read `08`.
29
- - CLI or template work: read `06`.
30
- - Publishing the libraries / scaffold CLI: read `06`.
31
- - Running everything locally without cloud accounts: read `11`.
32
- - One-click cloud deploy and the browser setup wizard: read `12`.
33
- - AI agents: start with this index, then move directly to the subsystem file that
34
- matches the task. Treat `apps/nextblock`, `libs/*`, and
35
- `libs/db/src/supabase/migrations` as the final authority if a doc and code ever
36
- disagree.
37
- - AI agents touching migrations must also read the root `AGENTS.md` note:
38
- production/shared database changes are append-only and non-destructive by
39
- default.
1
+ # NextBlock CMS Documentation
2
+
3
+ This folder is the source-of-truth reference set for the NextBlock monorepo.
4
+ The numbered files are written from live code, routes, migrations, and shipped
5
+ library surfaces rather than historical planning notes.
6
+
7
+ ## Start Here
8
+
9
+ - Product and architecture overview: [01-PROJECT-OVERVIEW.md](./01-PROJECT-OVERVIEW.md)
10
+ - Commerce capabilities: [02-ECOMMERCE-CAPABILITIES.md](./02-ECOMMERCE-CAPABILITIES.md)
11
+ - CMS editor and block system: [03-CMS-AND-EDITOR.md](./03-CMS-AND-EDITOR.md)
12
+ - Database, auth, and migrations: [04-DATABASE-AND-AUTH.md](./04-DATABASE-AND-AUTH.md)
13
+ - Contributor workflow and local operations: [05-DEVELOPER-GUIDE.md](./05-DEVELOPER-GUIDE.md)
14
+ - CLI and scaffolded project flow: [06-CLI-AND-SCAFFOLDING.md](./06-CLI-AND-SCAFFOLDING.md)
15
+ - Block SDK and extensibility surface: [07-BLOCK-SDK-AND-EXTENSIBILITY.md](./07-BLOCK-SDK-AND-EXTENSIBILITY.md)
16
+ - Cortex AI architecture: [08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md](./08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md)
17
+ - Live draft (visual editing) mode: [09-LIVE-DRAFT-MODE.md](./09-LIVE-DRAFT-MODE.md)
18
+ - Custom blocks (data-driven CRUD): [10-CUSTOM-BLOCKS.md](./10-CUSTOM-BLOCKS.md)
19
+ - Self-hosted local Docker stack: [11-SELF-HOSTED-DOCKER.md](./11-SELF-HOSTED-DOCKER.md)
20
+ - One-click cloud deploy (Deploy to Vercel): [12-VERCEL-DEPLOYMENT.md](./12-VERCEL-DEPLOYMENT.md)
21
+
22
+ ## Audience Guide
23
+
24
+ - New contributors: read `01`, then `04`, then `05`.
25
+ - Commerce work: read `02`, then `04`.
26
+ - Editor or page-builder work: read `03`, then `07`.
27
+ - Custom block work: read `03`, then `10`.
28
+ - AI / Cortex work: read `08`.
29
+ - CLI or template work: read `06`.
30
+ - Publishing the libraries / scaffold CLI: read `06`.
31
+ - Running everything locally without cloud accounts: read `11`.
32
+ - One-click cloud deploy and the browser setup wizard: read `12`.
33
+ - AI agents: start with this index, then move directly to the subsystem file that
34
+ matches the task. Treat `apps/nextblock`, `libs/*`, and
35
+ `libs/db/src/supabase/migrations` as the final authority if a doc and code ever
36
+ disagree.
37
+ - AI agents touching migrations must also read the root `AGENTS.md` note:
38
+ production/shared database changes are append-only and non-destructive by
39
+ default.
@@ -1,4 +1,4 @@
1
- import { useEffect, type DependencyList } from 'react';
1
+ import { useEffect, type DependencyList } from 'react';
2
2
 
3
3
  /**
4
4
  * Hook to handle keyboard shortcuts.
@@ -8,26 +8,26 @@ import { useEffect, type DependencyList } from 'react';
8
8
  * @param callback The function to call when the key combination is pressed
9
9
  * @param deps Dependencies array for the effect
10
10
  */
11
- export function useHotkeys(
12
- key: string,
13
- callback: (event: KeyboardEvent) => void,
14
- deps: DependencyList = [],
15
- ) {
16
- useEffect(() => {
17
- const normalizedShortcut = key.toLowerCase();
18
-
19
- const handleKeyDown = (event: KeyboardEvent) => {
20
- const isCtrl = event.ctrlKey || event.metaKey; // cmd on mac, ctrl on windows
21
- const keyLower = typeof event.key === 'string' ? event.key.toLowerCase() : '';
22
- const isSaveKey = keyLower === 's' || event.code === 'KeyS';
23
-
24
- // Check for ctrl+s / cmd+s
25
- if ((normalizedShortcut === 'ctrl+s' || normalizedShortcut === 'meta+s') && isCtrl && isSaveKey) {
26
- event.preventDefault();
27
- callback(event);
28
- }
29
- };
30
-
11
+ export function useHotkeys(
12
+ key: string,
13
+ callback: (event: KeyboardEvent) => void,
14
+ deps: DependencyList = [],
15
+ ) {
16
+ useEffect(() => {
17
+ const normalizedShortcut = key.toLowerCase();
18
+
19
+ const handleKeyDown = (event: KeyboardEvent) => {
20
+ const isCtrl = event.ctrlKey || event.metaKey; // cmd on mac, ctrl on windows
21
+ const keyLower = typeof event.key === 'string' ? event.key.toLowerCase() : '';
22
+ const isSaveKey = keyLower === 's' || event.code === 'KeyS';
23
+
24
+ // Check for ctrl+s / cmd+s
25
+ if ((normalizedShortcut === 'ctrl+s' || normalizedShortcut === 'meta+s') && isCtrl && isSaveKey) {
26
+ event.preventDefault();
27
+ callback(event);
28
+ }
29
+ };
30
+
31
31
  window.addEventListener('keydown', handleKeyDown);
32
32
  return () => window.removeEventListener('keydown', handleKeyDown);
33
33
  }, [key, ...deps]); // callback should be stable or included in deps if handled by caller
@@ -1,101 +1,101 @@
1
- 'use client';
2
-
3
- import { useEffect, useMemo, useState } from 'react';
4
- import { useLanguage } from '../context/LanguageContext';
5
- import type {
6
- GlobalSearchFilter,
7
- GlobalSearchResponse,
8
- GlobalSearchResult,
9
- } from '../lib/search/types';
10
-
11
- type SearchStatus = 'idle' | 'loading' | 'success' | 'error';
12
-
13
- export function useGlobalSearch({
14
- initialQuery = '',
15
- initialFilter = 'all',
16
- limit = 12,
17
- }: {
18
- initialQuery?: string;
19
- initialFilter?: GlobalSearchFilter;
20
- limit?: number;
21
- } = {}) {
22
- const { currentLocale } = useLanguage();
23
- const [query, setQuery] = useState(initialQuery);
24
- const [filter, setFilter] = useState<GlobalSearchFilter>(initialFilter);
25
- const [results, setResults] = useState<GlobalSearchResult[]>([]);
26
- const [counts, setCounts] = useState<GlobalSearchResponse['counts']>({
27
- all: 0,
28
- page: 0,
29
- post: 0,
30
- product: 0,
31
- });
32
- const [status, setStatus] = useState<SearchStatus>('idle');
33
-
34
- const normalizedQuery = useMemo(() => query.replace(/\s+/g, ' ').trim(), [query]);
35
- const canSearch = normalizedQuery.length >= 2;
36
-
37
- useEffect(() => {
38
- if (!canSearch) {
39
- setResults([]);
40
- setCounts({ all: 0, page: 0, post: 0, product: 0 });
41
- setStatus('idle');
42
- return;
43
- }
44
-
45
- const controller = new AbortController();
46
- const timeoutId = window.setTimeout(async () => {
47
- setStatus('loading');
48
-
49
- const params = new URLSearchParams({
50
- q: normalizedQuery,
51
- type: filter,
52
- limit: String(limit),
53
- });
54
-
55
- if (currentLocale) {
56
- params.set('locale', currentLocale);
57
- }
58
-
59
- try {
60
- const response = await fetch(`/api/search?${params.toString()}`, {
61
- signal: controller.signal,
62
- });
63
-
64
- if (!response.ok) {
65
- throw new Error(`Search request failed with ${response.status}`);
66
- }
67
-
68
- const payload = (await response.json()) as GlobalSearchResponse;
69
- setResults(payload.results);
70
- setCounts(payload.counts);
71
- setStatus('success');
72
- } catch (error) {
73
- if ((error as Error).name === 'AbortError') {
74
- return;
75
- }
76
-
77
- console.error('Global search request failed:', error);
78
- setResults([]);
79
- setCounts({ all: 0, page: 0, post: 0, product: 0 });
80
- setStatus('error');
81
- }
82
- }, 180);
83
-
84
- return () => {
85
- controller.abort();
86
- window.clearTimeout(timeoutId);
87
- };
88
- }, [canSearch, currentLocale, filter, limit, normalizedQuery]);
89
-
90
- return {
91
- query,
92
- setQuery,
93
- filter,
94
- setFilter,
95
- results,
96
- counts,
97
- status,
98
- canSearch,
99
- isLoading: status === 'loading',
100
- };
101
- }
1
+ 'use client';
2
+
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { useLanguage } from '../context/LanguageContext';
5
+ import type {
6
+ GlobalSearchFilter,
7
+ GlobalSearchResponse,
8
+ GlobalSearchResult,
9
+ } from '../lib/search/types';
10
+
11
+ type SearchStatus = 'idle' | 'loading' | 'success' | 'error';
12
+
13
+ export function useGlobalSearch({
14
+ initialQuery = '',
15
+ initialFilter = 'all',
16
+ limit = 12,
17
+ }: {
18
+ initialQuery?: string;
19
+ initialFilter?: GlobalSearchFilter;
20
+ limit?: number;
21
+ } = {}) {
22
+ const { currentLocale } = useLanguage();
23
+ const [query, setQuery] = useState(initialQuery);
24
+ const [filter, setFilter] = useState<GlobalSearchFilter>(initialFilter);
25
+ const [results, setResults] = useState<GlobalSearchResult[]>([]);
26
+ const [counts, setCounts] = useState<GlobalSearchResponse['counts']>({
27
+ all: 0,
28
+ page: 0,
29
+ post: 0,
30
+ product: 0,
31
+ });
32
+ const [status, setStatus] = useState<SearchStatus>('idle');
33
+
34
+ const normalizedQuery = useMemo(() => query.replace(/\s+/g, ' ').trim(), [query]);
35
+ const canSearch = normalizedQuery.length >= 2;
36
+
37
+ useEffect(() => {
38
+ if (!canSearch) {
39
+ setResults([]);
40
+ setCounts({ all: 0, page: 0, post: 0, product: 0 });
41
+ setStatus('idle');
42
+ return;
43
+ }
44
+
45
+ const controller = new AbortController();
46
+ const timeoutId = window.setTimeout(async () => {
47
+ setStatus('loading');
48
+
49
+ const params = new URLSearchParams({
50
+ q: normalizedQuery,
51
+ type: filter,
52
+ limit: String(limit),
53
+ });
54
+
55
+ if (currentLocale) {
56
+ params.set('locale', currentLocale);
57
+ }
58
+
59
+ try {
60
+ const response = await fetch(`/api/search?${params.toString()}`, {
61
+ signal: controller.signal,
62
+ });
63
+
64
+ if (!response.ok) {
65
+ throw new Error(`Search request failed with ${response.status}`);
66
+ }
67
+
68
+ const payload = (await response.json()) as GlobalSearchResponse;
69
+ setResults(payload.results);
70
+ setCounts(payload.counts);
71
+ setStatus('success');
72
+ } catch (error) {
73
+ if ((error as Error).name === 'AbortError') {
74
+ return;
75
+ }
76
+
77
+ console.error('Global search request failed:', error);
78
+ setResults([]);
79
+ setCounts({ all: 0, page: 0, post: 0, product: 0 });
80
+ setStatus('error');
81
+ }
82
+ }, 180);
83
+
84
+ return () => {
85
+ controller.abort();
86
+ window.clearTimeout(timeoutId);
87
+ };
88
+ }, [canSearch, currentLocale, filter, limit, normalizedQuery]);
89
+
90
+ return {
91
+ query,
92
+ setQuery,
93
+ filter,
94
+ setFilter,
95
+ results,
96
+ counts,
97
+ status,
98
+ canSearch,
99
+ isLoading: status === 'loading',
100
+ };
101
+ }
@@ -1,7 +1,7 @@
1
- declare module '*.svg' {
2
- const content: any;
3
- export const ReactComponent: any;
4
- export default content;
5
- }
6
-
7
- declare module 'uuid';
1
+ declare module '*.svg' {
2
+ const content: any;
3
+ export const ReactComponent: any;
4
+ export default content;
5
+ }
6
+
7
+ declare module 'uuid';