create-nextblock 0.14.1 → 0.14.2

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 (241) 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/twoFactorEmail.ts +22 -22
  25. package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -153
  26. package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -96
  27. package/templates/nextblock-template/app/api/brand/email-logo/route.ts +48 -48
  28. package/templates/nextblock-template/app/api/cms/check-updates/route.ts +44 -44
  29. package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -33
  30. package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -63
  31. package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3490 -3490
  32. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +5749 -5749
  33. package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +39 -39
  34. package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -92
  35. package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -43
  36. package/templates/nextblock-template/app/api/media/library/route.ts +69 -69
  37. package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -53
  38. package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
  39. package/templates/nextblock-template/app/api/search/route.ts +43 -43
  40. package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +441 -441
  41. package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
  42. package/templates/nextblock-template/app/cart/page.tsx +7 -7
  43. package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -160
  44. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
  45. package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -167
  46. package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +4 -2
  47. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +90 -90
  48. package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -122
  49. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
  50. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
  51. package/templates/nextblock-template/app/cms/components/SystemAlertsBanner.tsx +112 -112
  52. package/templates/nextblock-template/app/cms/components/TwoFactorReminderBanner.tsx +45 -45
  53. package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -102
  54. package/templates/nextblock-template/app/cms/components/system-alerts-actions.ts +31 -31
  55. package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -66
  56. package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -519
  57. package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -256
  58. package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -384
  59. package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -221
  60. package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -12
  61. package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -438
  62. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
  63. package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +130 -130
  64. package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
  65. package/templates/nextblock-template/app/cms/layout.tsx +73 -73
  66. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
  67. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
  68. package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
  69. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
  70. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
  71. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
  72. package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -12
  73. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
  74. package/templates/nextblock-template/app/cms/products/new/page.tsx +135 -135
  75. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
  76. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
  77. package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -456
  78. package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -115
  79. package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -31
  80. package/templates/nextblock-template/app/cms/revisions/service.ts +19 -19
  81. package/templates/nextblock-template/app/cms/revisions/utils.ts +132 -132
  82. package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -1004
  83. package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -29
  84. package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
  85. package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +688 -688
  86. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
  87. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
  88. package/templates/nextblock-template/app/cms/settings/email/page.tsx +28 -28
  89. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
  90. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
  91. package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
  92. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  93. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
  94. package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -21
  95. package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +23 -21
  96. package/templates/nextblock-template/app/cms/settings/logos/components/SetActiveLogoButton.tsx +42 -42
  97. package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -133
  98. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  99. package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -122
  100. package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
  101. package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
  102. package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +9 -7
  103. package/templates/nextblock-template/app/cms/settings/security/page.tsx +33 -33
  104. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
  105. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
  106. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
  107. package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
  108. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
  109. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
  110. package/templates/nextblock-template/app/profile/account-links.ts +22 -22
  111. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
  112. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
  113. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
  114. package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
  115. package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
  116. package/templates/nextblock-template/app/setup/SetupWizard.tsx +678 -678
  117. package/templates/nextblock-template/app/setup/layout.tsx +13 -13
  118. package/templates/nextblock-template/app/setup/page.tsx +111 -111
  119. package/templates/nextblock-template/app/sitemap.ts +130 -130
  120. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
  121. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
  122. package/templates/nextblock-template/components/DeferredGoogleAnalytics.tsx +70 -70
  123. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
  124. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
  125. package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -47
  126. package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
  127. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
  128. package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
  129. package/templates/nextblock-template/components/PublicEnvBootstrap.tsx +30 -30
  130. package/templates/nextblock-template/components/ResponsiveNav.tsx +14 -14
  131. package/templates/nextblock-template/components/auth/AuthBotProtection.tsx +182 -182
  132. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
  133. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
  134. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
  135. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +249 -249
  136. package/templates/nextblock-template/components/blocks/types.ts +7 -7
  137. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  138. package/templates/nextblock-template/components/form-message.tsx +32 -32
  139. package/templates/nextblock-template/components/media/YouTubeFacade.tsx +105 -105
  140. package/templates/nextblock-template/components/media/youtube-embed-replace.tsx +32 -32
  141. package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +170 -170
  142. package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +70 -70
  143. package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -28
  144. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -166
  145. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +471 -471
  146. package/templates/nextblock-template/components/submit-button.tsx +23 -23
  147. package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
  148. package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -21
  149. package/templates/nextblock-template/context/AuthContext.tsx +23 -23
  150. package/templates/nextblock-template/context/language-rest-client.ts +32 -32
  151. package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -6
  152. package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -25
  153. package/templates/nextblock-template/docker/kong/kong.yml +112 -112
  154. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
  155. package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -364
  156. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -202
  157. package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +246 -246
  158. package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +176 -176
  159. package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -146
  160. package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -222
  161. package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +173 -173
  162. package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +170 -170
  163. package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +151 -151
  164. package/templates/nextblock-template/docs/README.md +39 -39
  165. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
  166. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
  167. package/templates/nextblock-template/index.d.ts +7 -7
  168. package/templates/nextblock-template/lib/app-secrets.ts +39 -39
  169. package/templates/nextblock-template/lib/auth/cookies.ts +47 -47
  170. package/templates/nextblock-template/lib/auth/crypto.ts +45 -45
  171. package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -92
  172. package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
  173. package/templates/nextblock-template/lib/blocks/README.md +13 -13
  174. package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
  175. package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -2243
  176. package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -145
  177. package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -87
  178. package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -178
  179. package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -140
  180. package/templates/nextblock-template/lib/custom-block-r2-upload.ts +88 -88
  181. package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -227
  182. package/templates/nextblock-template/lib/custom-block-relations.ts +279 -279
  183. package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -14
  184. package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -172
  185. package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -213
  186. package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -22
  187. package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -193
  188. package/templates/nextblock-template/lib/email/branding-format.test.ts +133 -133
  189. package/templates/nextblock-template/lib/email/branding-format.ts +123 -123
  190. package/templates/nextblock-template/lib/email/branding.ts +76 -76
  191. package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -121
  192. package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -206
  193. package/templates/nextblock-template/lib/full-backup/server.ts +743 -743
  194. package/templates/nextblock-template/lib/logos/active-logo.ts +53 -53
  195. package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +54 -54
  196. package/templates/nextblock-template/lib/media/youtube.ts +99 -99
  197. package/templates/nextblock-template/lib/onboarding/actions.ts +31 -31
  198. package/templates/nextblock-template/lib/onboarding/status.ts +222 -222
  199. package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
  200. package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -57
  201. package/templates/nextblock-template/lib/privacy/contact-emails.ts +64 -64
  202. package/templates/nextblock-template/lib/privacy/settings.ts +115 -115
  203. package/templates/nextblock-template/lib/privacy/types.ts +69 -69
  204. package/templates/nextblock-template/lib/promotions/server.test.ts +74 -74
  205. package/templates/nextblock-template/lib/promotions/server.ts +741 -741
  206. package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -142
  207. package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -255
  208. package/templates/nextblock-template/lib/search/types.ts +27 -27
  209. package/templates/nextblock-template/lib/setup/actions.ts +460 -460
  210. package/templates/nextblock-template/lib/setup/env-status.ts +125 -125
  211. package/templates/nextblock-template/lib/setup/env-write.ts +111 -111
  212. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +87 -87
  213. package/templates/nextblock-template/lib/setup/provisioning.ts +59 -59
  214. package/templates/nextblock-template/lib/setup/schema-apply.ts +408 -408
  215. package/templates/nextblock-template/lib/setup/system-config.ts +105 -105
  216. package/templates/nextblock-template/lib/setup/types.ts +18 -18
  217. package/templates/nextblock-template/lib/site-url.ts +48 -48
  218. package/templates/nextblock-template/lib/storage/provider.ts +66 -66
  219. package/templates/nextblock-template/lib/storage/supabase-storage.ts +103 -103
  220. package/templates/nextblock-template/lib/updates/check-upstream.ts +441 -441
  221. package/templates/nextblock-template/lib/updates/github-device.ts +206 -206
  222. package/templates/nextblock-template/lib/updates/repo-identity.ts +56 -56
  223. package/templates/nextblock-template/package.json +1 -1
  224. package/templates/nextblock-template/postcss.config.js +6 -6
  225. package/templates/nextblock-template/scripts/backup.js +115 -115
  226. package/templates/nextblock-template/scripts/docker-setup.mjs +310 -310
  227. package/templates/nextblock-template/scripts/restore.js +385 -385
  228. package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +98 -98
  229. package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -62
  230. package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -537
  231. package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -58
  232. package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -188
  233. package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -123
  234. package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -133
  235. package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -65
  236. package/templates/nextblock-template/tailwind.config.js +25 -25
  237. package/templates/nextblock-template/tools/build-migrate.mjs +209 -209
  238. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
  239. package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
  240. package/templates/nextblock-template/types/jsdom.d.ts +6 -6
  241. 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';