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.
- package/docker-template/.dockerignore +23 -23
- package/docker-template/.env.docker.example +69 -69
- package/docker-template/docker/db/init/99-jwt.sql +6 -6
- package/docker-template/docker/db/init/99-roles.sql +25 -25
- package/docker-template/docker/kong/kong.yml +112 -112
- package/docker-template/scripts/docker-setup.mjs +310 -310
- package/libs/db/tsconfig.lib.json +3 -3
- package/libs/editor/tsconfig.lib.json +3 -3
- package/libs/ui/tsconfig.lib.json +3 -3
- package/libs/utils/tsconfig.json +3 -3
- package/package.json +1 -1
- package/project.json +19 -19
- package/templates/nextblock-template/.browserslistrc +11 -11
- package/templates/nextblock-template/.dockerignore +23 -23
- package/templates/nextblock-template/README.md +34 -34
- package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
- package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
- package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +46 -46
- package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -51
- package/templates/nextblock-template/app/ToasterProvider.tsx +17 -17
- package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
- package/templates/nextblock-template/app/actions/consent.ts +57 -57
- package/templates/nextblock-template/app/actions/postActions.ts +146 -146
- package/templates/nextblock-template/app/actions/twoFactorEmail.ts +22 -22
- package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -153
- package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -96
- package/templates/nextblock-template/app/api/brand/email-logo/route.ts +48 -48
- package/templates/nextblock-template/app/api/cms/check-updates/route.ts +44 -44
- package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -33
- package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -63
- package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3490 -3490
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +5749 -5749
- package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +39 -39
- package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -92
- package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -43
- package/templates/nextblock-template/app/api/media/library/route.ts +69 -69
- package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -53
- package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
- package/templates/nextblock-template/app/api/search/route.ts +43 -43
- package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +441 -441
- package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
- package/templates/nextblock-template/app/cart/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -160
- package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
- package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -167
- package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +4 -2
- package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +90 -90
- package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -122
- package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
- package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
- package/templates/nextblock-template/app/cms/components/SystemAlertsBanner.tsx +112 -112
- package/templates/nextblock-template/app/cms/components/TwoFactorReminderBanner.tsx +45 -45
- package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -102
- package/templates/nextblock-template/app/cms/components/system-alerts-actions.ts +31 -31
- package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -66
- package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -519
- package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -256
- package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -384
- package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -221
- package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -438
- package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +130 -130
- package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
- package/templates/nextblock-template/app/cms/layout.tsx +73 -73
- package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
- package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
- package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
- package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
- package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
- package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
- package/templates/nextblock-template/app/cms/products/new/page.tsx +135 -135
- package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
- package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
- package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -456
- package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -115
- package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -31
- package/templates/nextblock-template/app/cms/revisions/service.ts +19 -19
- package/templates/nextblock-template/app/cms/revisions/utils.ts +132 -132
- package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -1004
- package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -29
- package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
- package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +688 -688
- package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
- package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
- package/templates/nextblock-template/app/cms/settings/email/page.tsx +28 -28
- package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
- package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
- package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
- package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
- package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -21
- package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +23 -21
- package/templates/nextblock-template/app/cms/settings/logos/components/SetActiveLogoButton.tsx +42 -42
- package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -133
- package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
- package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -122
- package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +9 -7
- package/templates/nextblock-template/app/cms/settings/security/page.tsx +33 -33
- package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
- package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
- package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
- package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
- package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
- package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
- package/templates/nextblock-template/app/profile/account-links.ts +22 -22
- package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
- package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
- package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
- package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
- package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
- package/templates/nextblock-template/app/setup/SetupWizard.tsx +678 -678
- package/templates/nextblock-template/app/setup/layout.tsx +13 -13
- package/templates/nextblock-template/app/setup/page.tsx +111 -111
- package/templates/nextblock-template/app/sitemap.ts +130 -130
- package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
- package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
- package/templates/nextblock-template/components/DeferredGoogleAnalytics.tsx +70 -70
- package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
- package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
- package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -47
- package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
- package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
- package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
- package/templates/nextblock-template/components/PublicEnvBootstrap.tsx +30 -30
- package/templates/nextblock-template/components/ResponsiveNav.tsx +14 -14
- package/templates/nextblock-template/components/auth/AuthBotProtection.tsx +182 -182
- package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
- package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
- package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
- package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +249 -249
- package/templates/nextblock-template/components/blocks/types.ts +7 -7
- package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
- package/templates/nextblock-template/components/form-message.tsx +32 -32
- package/templates/nextblock-template/components/media/YouTubeFacade.tsx +105 -105
- package/templates/nextblock-template/components/media/youtube-embed-replace.tsx +32 -32
- package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +170 -170
- package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +70 -70
- package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -28
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -166
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +471 -471
- package/templates/nextblock-template/components/submit-button.tsx +23 -23
- package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
- package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -21
- package/templates/nextblock-template/context/AuthContext.tsx +23 -23
- package/templates/nextblock-template/context/language-rest-client.ts +32 -32
- package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -6
- package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -25
- package/templates/nextblock-template/docker/kong/kong.yml +112 -112
- package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
- package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -364
- package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -202
- package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +246 -246
- package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +176 -176
- package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -146
- package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -222
- package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +173 -173
- package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +170 -170
- package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +151 -151
- package/templates/nextblock-template/docs/README.md +39 -39
- package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
- package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
- package/templates/nextblock-template/index.d.ts +7 -7
- package/templates/nextblock-template/lib/app-secrets.ts +39 -39
- package/templates/nextblock-template/lib/auth/cookies.ts +47 -47
- package/templates/nextblock-template/lib/auth/crypto.ts +45 -45
- package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -92
- package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
- package/templates/nextblock-template/lib/blocks/README.md +13 -13
- package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
- package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -2243
- package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -145
- package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -87
- package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -178
- package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -140
- package/templates/nextblock-template/lib/custom-block-r2-upload.ts +88 -88
- package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -227
- package/templates/nextblock-template/lib/custom-block-relations.ts +279 -279
- package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -14
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -172
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -213
- package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -22
- package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -193
- package/templates/nextblock-template/lib/email/branding-format.test.ts +133 -133
- package/templates/nextblock-template/lib/email/branding-format.ts +123 -123
- package/templates/nextblock-template/lib/email/branding.ts +76 -76
- package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -121
- package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -206
- package/templates/nextblock-template/lib/full-backup/server.ts +743 -743
- package/templates/nextblock-template/lib/logos/active-logo.ts +53 -53
- package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +54 -54
- package/templates/nextblock-template/lib/media/youtube.ts +99 -99
- package/templates/nextblock-template/lib/onboarding/actions.ts +31 -31
- package/templates/nextblock-template/lib/onboarding/status.ts +222 -222
- package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
- package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -57
- package/templates/nextblock-template/lib/privacy/contact-emails.ts +64 -64
- package/templates/nextblock-template/lib/privacy/settings.ts +115 -115
- package/templates/nextblock-template/lib/privacy/types.ts +69 -69
- package/templates/nextblock-template/lib/promotions/server.test.ts +74 -74
- package/templates/nextblock-template/lib/promotions/server.ts +741 -741
- package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -142
- package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -255
- package/templates/nextblock-template/lib/search/types.ts +27 -27
- package/templates/nextblock-template/lib/setup/actions.ts +460 -460
- package/templates/nextblock-template/lib/setup/env-status.ts +125 -125
- package/templates/nextblock-template/lib/setup/env-write.ts +111 -111
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +87 -87
- package/templates/nextblock-template/lib/setup/provisioning.ts +59 -59
- package/templates/nextblock-template/lib/setup/schema-apply.ts +408 -408
- package/templates/nextblock-template/lib/setup/system-config.ts +105 -105
- package/templates/nextblock-template/lib/setup/types.ts +18 -18
- package/templates/nextblock-template/lib/site-url.ts +48 -48
- package/templates/nextblock-template/lib/storage/provider.ts +66 -66
- package/templates/nextblock-template/lib/storage/supabase-storage.ts +103 -103
- package/templates/nextblock-template/lib/updates/check-upstream.ts +441 -441
- package/templates/nextblock-template/lib/updates/github-device.ts +206 -206
- package/templates/nextblock-template/lib/updates/repo-identity.ts +56 -56
- package/templates/nextblock-template/package.json +1 -1
- package/templates/nextblock-template/postcss.config.js +6 -6
- package/templates/nextblock-template/scripts/backup.js +115 -115
- package/templates/nextblock-template/scripts/docker-setup.mjs +310 -310
- package/templates/nextblock-template/scripts/restore.js +385 -385
- package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +98 -98
- package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -62
- package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -537
- package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -58
- package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -188
- package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -123
- package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -133
- package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -65
- package/templates/nextblock-template/tailwind.config.js +25 -25
- package/templates/nextblock-template/tools/build-migrate.mjs +209 -209
- package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
- package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
- package/templates/nextblock-template/types/jsdom.d.ts +6 -6
- 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';
|