create-nextblock 0.12.9 → 0.12.11
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/SignUpForm.tsx +141 -141
- package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +46 -46
- package/templates/nextblock-template/app/(auth-pages)/two-factor/actions.ts +91 -91
- package/templates/nextblock-template/app/(auth-pages)/two-factor/components/TwoFactorForm.tsx +123 -123
- 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/interactions.test.ts +301 -301
- package/templates/nextblock-template/app/actions/interactions.ts +373 -373
- 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/ai/global-agent/route.ts +964 -964
- 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 +5597 -5597
- 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 +29 -29
- 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/FeatureImageField.tsx +244 -244
- 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/BlockComposer.tsx +1522 -1522
- 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/ContentTransferControls.tsx +431 -391
- package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
- package/templates/nextblock-template/app/cms/interactions/InteractionsModerationClient.tsx +408 -408
- package/templates/nextblock-template/app/cms/interactions/page.tsx +51 -51
- 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/media/components/MediaPickerDialog.tsx +149 -149
- package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +101 -101
- package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
- package/templates/nextblock-template/app/cms/pages/page.tsx +18 -17
- package/templates/nextblock-template/app/cms/posts/page.tsx +19 -18
- 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/page.tsx +39 -27
- 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/actions.ts +93 -93
- package/templates/nextblock-template/app/cms/settings/bot-protection/components/BotProtectionForm.tsx +129 -129
- package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
- package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +497 -497
- package/templates/nextblock-template/app/cms/settings/cortex-ai/StoredCortexAiSettingsClient.tsx +410 -410
- package/templates/nextblock-template/app/cms/settings/cortex-ai/actions.ts +246 -246
- package/templates/nextblock-template/app/cms/settings/cortex-ai/page.tsx +80 -80
- 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/actions.ts +64 -64
- package/templates/nextblock-template/app/cms/settings/email/components/EmailForm.tsx +181 -181
- 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/actions.ts +60 -60
- package/templates/nextblock-template/app/cms/settings/google-analytics/components/GoogleAnalyticsForm.tsx +129 -129
- package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
- package/templates/nextblock-template/app/cms/settings/languages/actions.ts +16 -16
- 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 +20 -20
- 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/actions.ts +52 -52
- package/templates/nextblock-template/app/cms/settings/privacy/components/PrivacyForm.tsx +148 -148
- package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/registration/actions.ts +44 -44
- package/templates/nextblock-template/app/cms/settings/registration/components/RegistrationForm.tsx +65 -65
- package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/security/actions.ts +284 -284
- package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +522 -522
- 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/actions.ts +222 -222
- package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
- package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +44 -44
- package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
- package/templates/nextblock-template/app/lib/sitemap-utils.ts +253 -253
- package/templates/nextblock-template/app/profile/ProfileAccountSidebar.tsx +73 -73
- 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-data.ts +37 -37
- package/templates/nextblock-template/app/profile/account-links.ts +22 -22
- package/templates/nextblock-template/app/profile/account-types.ts +11 -11
- package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
- package/templates/nextblock-template/app/profile/orders/[id]/CustomerOrderDetailPageClient.tsx +79 -79
- package/templates/nextblock-template/app/profile/orders/[id]/page.tsx +32 -32
- package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
- package/templates/nextblock-template/app/profile/page.tsx +42 -42
- 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/GitHubLoginButton.tsx +15 -15
- package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
- package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
- package/templates/nextblock-template/components/PostCommentsSection.tsx +369 -369
- package/templates/nextblock-template/components/ProductReviewsSection.tsx +419 -419
- 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/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 +464 -464
- 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/LanguageContext.tsx +16 -16
- 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/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +1319 -1319
- 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/docs/TECHNICAL_SPECIFICATION.md +12505 -12505
- 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/twoFactor.ts +208 -208
- 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/blocks/blockRegistry.ts +6 -6
- package/templates/nextblock-template/lib/blocks/blockTypes.ts +18 -18
- package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
- package/templates/nextblock-template/lib/cms-transfer/csv.test.ts +100 -77
- package/templates/nextblock-template/lib/cms-transfer/csv.ts +403 -399
- 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/config/email-settings.ts +217 -217
- 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-relation-registry.ts +256 -256
- 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/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/server.ts +585 -585
- 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 +37 -37
- 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/types/jsdom.d.ts +6 -6
- package/tsconfig.base.json +3 -3
- package/templates/nextblock-template/tsconfig.tsbuildinfo +0 -1
|
@@ -1,253 +1,253 @@
|
|
|
1
|
-
import { getSsgSupabaseClient } from '@nextblock-cms/db/server';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A single, language-aware entry destined for the XML sitemap.
|
|
5
|
-
*
|
|
6
|
-
* Paths are kept site-root-relative (e.g. "/about", "/article/my-post"); the
|
|
7
|
-
* `app/sitemap.ts` route prefixes the absolute origin so the base URL lives in
|
|
8
|
-
* exactly one place.
|
|
9
|
-
*/
|
|
10
|
-
export interface SitemapEntry {
|
|
11
|
-
/** Root-relative path for this row, e.g. "/about" or "/article/hello". */
|
|
12
|
-
path: string;
|
|
13
|
-
/** ISO-8601 timestamp mapped from the row's `updated_at`. */
|
|
14
|
-
lastModified: string;
|
|
15
|
-
/**
|
|
16
|
-
* hreflang map of `languageCode -> root-relative path` covering every
|
|
17
|
-
* published translation that shares this row's `translation_group_id`, plus
|
|
18
|
-
* an `x-default` key pointing at the default-language version. Left
|
|
19
|
-
* `undefined` for content that only exists in a single language.
|
|
20
|
-
*
|
|
21
|
-
* NextBlock's i18n model gives each language its own slug (there is no
|
|
22
|
-
* `/en` `/fr` path prefix), so alternates are resolved by grouping sibling
|
|
23
|
-
* rows on `translation_group_id` rather than by swapping a locale segment.
|
|
24
|
-
*/
|
|
25
|
-
alternates?: Record<string, string>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Page slugs that are rendered by their own canonical route and must not be
|
|
30
|
-
* re-advertised under the generic `/{slug}` catch-all:
|
|
31
|
-
* - `product-template` backs the product layout (app/product/[slug]); it is
|
|
32
|
-
* not a public page.
|
|
33
|
-
* - `home` / `accueil` back the locale-resolved homepage served at "/"
|
|
34
|
-
* (see getHomepageSlugForLocale in app/page.tsx); listing them as `/home`
|
|
35
|
-
* and `/accueil` would duplicate the canonical "/" entry.
|
|
36
|
-
*/
|
|
37
|
-
const EXCLUDED_PAGE_SLUGS = new Set(['product-template', 'home', 'accueil']);
|
|
38
|
-
|
|
39
|
-
type SupabaseLikeClient = ReturnType<typeof getSsgSupabaseClient>;
|
|
40
|
-
|
|
41
|
-
/** Minimal shape every content table exposes for sitemap purposes. */
|
|
42
|
-
interface ContentRow {
|
|
43
|
-
slug: string | null;
|
|
44
|
-
updated_at: string | null;
|
|
45
|
-
created_at?: string | null;
|
|
46
|
-
language_id: number | null;
|
|
47
|
-
translation_group_id: string | null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function toIsoDate(value: string | null | undefined): string {
|
|
51
|
-
if (value) {
|
|
52
|
-
const parsed = new Date(value);
|
|
53
|
-
if (!Number.isNaN(parsed.getTime())) {
|
|
54
|
-
return parsed.toISOString();
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return new Date().toISOString();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Loads the language set once so callers can resolve a row's `language_id` to
|
|
62
|
-
* its BCP-47 `code` and identify the default language used for `x-default`.
|
|
63
|
-
*/
|
|
64
|
-
async function fetchLanguageMap(
|
|
65
|
-
supabase: SupabaseLikeClient,
|
|
66
|
-
): Promise<{ codeById: Map<number, string>; defaultCode: string | null }> {
|
|
67
|
-
const codeById = new Map<number, string>();
|
|
68
|
-
let defaultCode: string | null = null;
|
|
69
|
-
|
|
70
|
-
const { data, error } = await supabase
|
|
71
|
-
.from('languages')
|
|
72
|
-
.select('id, code, is_default');
|
|
73
|
-
|
|
74
|
-
if (error) {
|
|
75
|
-
console.error('Error fetching languages for sitemap alternates:', error);
|
|
76
|
-
return { codeById, defaultCode };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
for (const lang of data ?? []) {
|
|
80
|
-
if (lang.code) {
|
|
81
|
-
codeById.set(lang.id, lang.code);
|
|
82
|
-
if (lang.is_default) {
|
|
83
|
-
defaultCode = lang.code;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return { codeById, defaultCode };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Turns raw content rows into language-aware sitemap entries. Rows that share a
|
|
93
|
-
* `translation_group_id` are emitted as sibling URLs that cross-link to one
|
|
94
|
-
* another via `alternates`.
|
|
95
|
-
*/
|
|
96
|
-
function rowsToEntries(
|
|
97
|
-
rows: ContentRow[],
|
|
98
|
-
pathForSlug: (slug: string) => string,
|
|
99
|
-
codeById: Map<number, string>,
|
|
100
|
-
defaultCode: string | null,
|
|
101
|
-
): SitemapEntry[] {
|
|
102
|
-
// Bucket rows by their translation group so each URL can advertise its siblings.
|
|
103
|
-
const groups = new Map<string, ContentRow[]>();
|
|
104
|
-
for (const row of rows) {
|
|
105
|
-
if (!row.slug) continue;
|
|
106
|
-
const key = row.translation_group_id ?? `solo:${row.slug}`;
|
|
107
|
-
const bucket = groups.get(key);
|
|
108
|
-
if (bucket) {
|
|
109
|
-
bucket.push(row);
|
|
110
|
-
} else {
|
|
111
|
-
groups.set(key, [row]);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const entries: SitemapEntry[] = [];
|
|
116
|
-
for (const group of groups.values()) {
|
|
117
|
-
// languageCode -> path for every translated sibling in this group.
|
|
118
|
-
const byCode: Record<string, string> = {};
|
|
119
|
-
let defaultPath: string | undefined;
|
|
120
|
-
for (const sibling of group) {
|
|
121
|
-
if (!sibling.slug || sibling.language_id == null) continue;
|
|
122
|
-
const code = codeById.get(sibling.language_id);
|
|
123
|
-
if (!code) continue;
|
|
124
|
-
const path = pathForSlug(sibling.slug);
|
|
125
|
-
byCode[code] = path;
|
|
126
|
-
if (code === defaultCode) {
|
|
127
|
-
defaultPath = path;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// hreflang is only meaningful when 2+ *distinct* URLs exist. NextBlock has no
|
|
132
|
-
// locale path prefix, so two translations can share a slug (e.g. `/articles`
|
|
133
|
-
// is the slug in both en and fr); that collapses to a single URL with nothing
|
|
134
|
-
// to cross-link, so no alternates are emitted.
|
|
135
|
-
const hasAlternates = new Set(Object.values(byCode)).size > 1;
|
|
136
|
-
const alternates = hasAlternates
|
|
137
|
-
? { ...byCode, 'x-default': defaultPath ?? Object.values(byCode)[0] }
|
|
138
|
-
: undefined;
|
|
139
|
-
|
|
140
|
-
for (const row of group) {
|
|
141
|
-
if (!row.slug) continue;
|
|
142
|
-
entries.push({
|
|
143
|
-
path: pathForSlug(row.slug),
|
|
144
|
-
lastModified: toIsoDate(row.updated_at ?? row.created_at),
|
|
145
|
-
alternates,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return entries;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Fetches all published pages from Supabase and formats them, with language
|
|
155
|
-
* alternates, for the sitemap.
|
|
156
|
-
*/
|
|
157
|
-
export async function fetchAllPublishedPages(): Promise<SitemapEntry[]> {
|
|
158
|
-
const supabase = getSsgSupabaseClient();
|
|
159
|
-
try {
|
|
160
|
-
const [{ data: pages, error }, languageMap] = await Promise.all([
|
|
161
|
-
supabase
|
|
162
|
-
.from('pages')
|
|
163
|
-
.select('slug, updated_at, language_id, translation_group_id')
|
|
164
|
-
.eq('status', 'published'),
|
|
165
|
-
fetchLanguageMap(supabase),
|
|
166
|
-
]);
|
|
167
|
-
|
|
168
|
-
if (error) {
|
|
169
|
-
console.error('Error fetching published pages:', error);
|
|
170
|
-
return [];
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const rows = (pages ?? []).filter(
|
|
174
|
-
(page) => page.slug && !EXCLUDED_PAGE_SLUGS.has(page.slug),
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
return rowsToEntries(
|
|
178
|
-
rows,
|
|
179
|
-
(slug) => `/${slug}`,
|
|
180
|
-
languageMap.codeById,
|
|
181
|
-
languageMap.defaultCode,
|
|
182
|
-
);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
console.error('An unexpected error occurred while fetching pages:', err);
|
|
185
|
-
return [];
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Fetches all published posts (respecting scheduled `published_at`) from
|
|
191
|
-
* Supabase and formats them, with language alternates, for the sitemap.
|
|
192
|
-
*/
|
|
193
|
-
export async function fetchAllPublishedPosts(): Promise<SitemapEntry[]> {
|
|
194
|
-
const supabase = getSsgSupabaseClient();
|
|
195
|
-
try {
|
|
196
|
-
const nowIso = new Date().toISOString();
|
|
197
|
-
const [{ data: posts, error }, languageMap] = await Promise.all([
|
|
198
|
-
supabase
|
|
199
|
-
.from('posts')
|
|
200
|
-
.select('slug, updated_at, language_id, translation_group_id')
|
|
201
|
-
.eq('status', 'published')
|
|
202
|
-
.or(`published_at.is.null,published_at.lte.${nowIso}`),
|
|
203
|
-
fetchLanguageMap(supabase),
|
|
204
|
-
]);
|
|
205
|
-
|
|
206
|
-
if (error) {
|
|
207
|
-
console.error('Error fetching published posts:', error);
|
|
208
|
-
return [];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return rowsToEntries(
|
|
212
|
-
posts ?? [],
|
|
213
|
-
(slug) => `/article/${slug}`,
|
|
214
|
-
languageMap.codeById,
|
|
215
|
-
languageMap.defaultCode,
|
|
216
|
-
);
|
|
217
|
-
} catch (err) {
|
|
218
|
-
console.error('An unexpected error occurred while fetching posts:', err);
|
|
219
|
-
return [];
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Fetches all active storefront products from Supabase and formats them, with
|
|
225
|
-
* language alternates, for the sitemap.
|
|
226
|
-
*/
|
|
227
|
-
export async function fetchAllActiveProducts(): Promise<SitemapEntry[]> {
|
|
228
|
-
const supabase = getSsgSupabaseClient();
|
|
229
|
-
try {
|
|
230
|
-
const [{ data: products, error }, languageMap] = await Promise.all([
|
|
231
|
-
supabase
|
|
232
|
-
.from('products')
|
|
233
|
-
.select('slug, updated_at, created_at, language_id, translation_group_id')
|
|
234
|
-
.eq('status', 'active'),
|
|
235
|
-
fetchLanguageMap(supabase),
|
|
236
|
-
]);
|
|
237
|
-
|
|
238
|
-
if (error) {
|
|
239
|
-
console.error('Error fetching active products:', error);
|
|
240
|
-
return [];
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return rowsToEntries(
|
|
244
|
-
products ?? [],
|
|
245
|
-
(slug) => `/product/${slug}`,
|
|
246
|
-
languageMap.codeById,
|
|
247
|
-
languageMap.defaultCode,
|
|
248
|
-
);
|
|
249
|
-
} catch (err) {
|
|
250
|
-
console.error('An unexpected error occurred while fetching products:', err);
|
|
251
|
-
return [];
|
|
252
|
-
}
|
|
253
|
-
}
|
|
1
|
+
import { getSsgSupabaseClient } from '@nextblock-cms/db/server';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A single, language-aware entry destined for the XML sitemap.
|
|
5
|
+
*
|
|
6
|
+
* Paths are kept site-root-relative (e.g. "/about", "/article/my-post"); the
|
|
7
|
+
* `app/sitemap.ts` route prefixes the absolute origin so the base URL lives in
|
|
8
|
+
* exactly one place.
|
|
9
|
+
*/
|
|
10
|
+
export interface SitemapEntry {
|
|
11
|
+
/** Root-relative path for this row, e.g. "/about" or "/article/hello". */
|
|
12
|
+
path: string;
|
|
13
|
+
/** ISO-8601 timestamp mapped from the row's `updated_at`. */
|
|
14
|
+
lastModified: string;
|
|
15
|
+
/**
|
|
16
|
+
* hreflang map of `languageCode -> root-relative path` covering every
|
|
17
|
+
* published translation that shares this row's `translation_group_id`, plus
|
|
18
|
+
* an `x-default` key pointing at the default-language version. Left
|
|
19
|
+
* `undefined` for content that only exists in a single language.
|
|
20
|
+
*
|
|
21
|
+
* NextBlock's i18n model gives each language its own slug (there is no
|
|
22
|
+
* `/en` `/fr` path prefix), so alternates are resolved by grouping sibling
|
|
23
|
+
* rows on `translation_group_id` rather than by swapping a locale segment.
|
|
24
|
+
*/
|
|
25
|
+
alternates?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Page slugs that are rendered by their own canonical route and must not be
|
|
30
|
+
* re-advertised under the generic `/{slug}` catch-all:
|
|
31
|
+
* - `product-template` backs the product layout (app/product/[slug]); it is
|
|
32
|
+
* not a public page.
|
|
33
|
+
* - `home` / `accueil` back the locale-resolved homepage served at "/"
|
|
34
|
+
* (see getHomepageSlugForLocale in app/page.tsx); listing them as `/home`
|
|
35
|
+
* and `/accueil` would duplicate the canonical "/" entry.
|
|
36
|
+
*/
|
|
37
|
+
const EXCLUDED_PAGE_SLUGS = new Set(['product-template', 'home', 'accueil']);
|
|
38
|
+
|
|
39
|
+
type SupabaseLikeClient = ReturnType<typeof getSsgSupabaseClient>;
|
|
40
|
+
|
|
41
|
+
/** Minimal shape every content table exposes for sitemap purposes. */
|
|
42
|
+
interface ContentRow {
|
|
43
|
+
slug: string | null;
|
|
44
|
+
updated_at: string | null;
|
|
45
|
+
created_at?: string | null;
|
|
46
|
+
language_id: number | null;
|
|
47
|
+
translation_group_id: string | null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function toIsoDate(value: string | null | undefined): string {
|
|
51
|
+
if (value) {
|
|
52
|
+
const parsed = new Date(value);
|
|
53
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
54
|
+
return parsed.toISOString();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return new Date().toISOString();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Loads the language set once so callers can resolve a row's `language_id` to
|
|
62
|
+
* its BCP-47 `code` and identify the default language used for `x-default`.
|
|
63
|
+
*/
|
|
64
|
+
async function fetchLanguageMap(
|
|
65
|
+
supabase: SupabaseLikeClient,
|
|
66
|
+
): Promise<{ codeById: Map<number, string>; defaultCode: string | null }> {
|
|
67
|
+
const codeById = new Map<number, string>();
|
|
68
|
+
let defaultCode: string | null = null;
|
|
69
|
+
|
|
70
|
+
const { data, error } = await supabase
|
|
71
|
+
.from('languages')
|
|
72
|
+
.select('id, code, is_default');
|
|
73
|
+
|
|
74
|
+
if (error) {
|
|
75
|
+
console.error('Error fetching languages for sitemap alternates:', error);
|
|
76
|
+
return { codeById, defaultCode };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (const lang of data ?? []) {
|
|
80
|
+
if (lang.code) {
|
|
81
|
+
codeById.set(lang.id, lang.code);
|
|
82
|
+
if (lang.is_default) {
|
|
83
|
+
defaultCode = lang.code;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { codeById, defaultCode };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Turns raw content rows into language-aware sitemap entries. Rows that share a
|
|
93
|
+
* `translation_group_id` are emitted as sibling URLs that cross-link to one
|
|
94
|
+
* another via `alternates`.
|
|
95
|
+
*/
|
|
96
|
+
function rowsToEntries(
|
|
97
|
+
rows: ContentRow[],
|
|
98
|
+
pathForSlug: (slug: string) => string,
|
|
99
|
+
codeById: Map<number, string>,
|
|
100
|
+
defaultCode: string | null,
|
|
101
|
+
): SitemapEntry[] {
|
|
102
|
+
// Bucket rows by their translation group so each URL can advertise its siblings.
|
|
103
|
+
const groups = new Map<string, ContentRow[]>();
|
|
104
|
+
for (const row of rows) {
|
|
105
|
+
if (!row.slug) continue;
|
|
106
|
+
const key = row.translation_group_id ?? `solo:${row.slug}`;
|
|
107
|
+
const bucket = groups.get(key);
|
|
108
|
+
if (bucket) {
|
|
109
|
+
bucket.push(row);
|
|
110
|
+
} else {
|
|
111
|
+
groups.set(key, [row]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const entries: SitemapEntry[] = [];
|
|
116
|
+
for (const group of groups.values()) {
|
|
117
|
+
// languageCode -> path for every translated sibling in this group.
|
|
118
|
+
const byCode: Record<string, string> = {};
|
|
119
|
+
let defaultPath: string | undefined;
|
|
120
|
+
for (const sibling of group) {
|
|
121
|
+
if (!sibling.slug || sibling.language_id == null) continue;
|
|
122
|
+
const code = codeById.get(sibling.language_id);
|
|
123
|
+
if (!code) continue;
|
|
124
|
+
const path = pathForSlug(sibling.slug);
|
|
125
|
+
byCode[code] = path;
|
|
126
|
+
if (code === defaultCode) {
|
|
127
|
+
defaultPath = path;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// hreflang is only meaningful when 2+ *distinct* URLs exist. NextBlock has no
|
|
132
|
+
// locale path prefix, so two translations can share a slug (e.g. `/articles`
|
|
133
|
+
// is the slug in both en and fr); that collapses to a single URL with nothing
|
|
134
|
+
// to cross-link, so no alternates are emitted.
|
|
135
|
+
const hasAlternates = new Set(Object.values(byCode)).size > 1;
|
|
136
|
+
const alternates = hasAlternates
|
|
137
|
+
? { ...byCode, 'x-default': defaultPath ?? Object.values(byCode)[0] }
|
|
138
|
+
: undefined;
|
|
139
|
+
|
|
140
|
+
for (const row of group) {
|
|
141
|
+
if (!row.slug) continue;
|
|
142
|
+
entries.push({
|
|
143
|
+
path: pathForSlug(row.slug),
|
|
144
|
+
lastModified: toIsoDate(row.updated_at ?? row.created_at),
|
|
145
|
+
alternates,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return entries;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Fetches all published pages from Supabase and formats them, with language
|
|
155
|
+
* alternates, for the sitemap.
|
|
156
|
+
*/
|
|
157
|
+
export async function fetchAllPublishedPages(): Promise<SitemapEntry[]> {
|
|
158
|
+
const supabase = getSsgSupabaseClient();
|
|
159
|
+
try {
|
|
160
|
+
const [{ data: pages, error }, languageMap] = await Promise.all([
|
|
161
|
+
supabase
|
|
162
|
+
.from('pages')
|
|
163
|
+
.select('slug, updated_at, language_id, translation_group_id')
|
|
164
|
+
.eq('status', 'published'),
|
|
165
|
+
fetchLanguageMap(supabase),
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
if (error) {
|
|
169
|
+
console.error('Error fetching published pages:', error);
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const rows = (pages ?? []).filter(
|
|
174
|
+
(page) => page.slug && !EXCLUDED_PAGE_SLUGS.has(page.slug),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
return rowsToEntries(
|
|
178
|
+
rows,
|
|
179
|
+
(slug) => `/${slug}`,
|
|
180
|
+
languageMap.codeById,
|
|
181
|
+
languageMap.defaultCode,
|
|
182
|
+
);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.error('An unexpected error occurred while fetching pages:', err);
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Fetches all published posts (respecting scheduled `published_at`) from
|
|
191
|
+
* Supabase and formats them, with language alternates, for the sitemap.
|
|
192
|
+
*/
|
|
193
|
+
export async function fetchAllPublishedPosts(): Promise<SitemapEntry[]> {
|
|
194
|
+
const supabase = getSsgSupabaseClient();
|
|
195
|
+
try {
|
|
196
|
+
const nowIso = new Date().toISOString();
|
|
197
|
+
const [{ data: posts, error }, languageMap] = await Promise.all([
|
|
198
|
+
supabase
|
|
199
|
+
.from('posts')
|
|
200
|
+
.select('slug, updated_at, language_id, translation_group_id')
|
|
201
|
+
.eq('status', 'published')
|
|
202
|
+
.or(`published_at.is.null,published_at.lte.${nowIso}`),
|
|
203
|
+
fetchLanguageMap(supabase),
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
if (error) {
|
|
207
|
+
console.error('Error fetching published posts:', error);
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return rowsToEntries(
|
|
212
|
+
posts ?? [],
|
|
213
|
+
(slug) => `/article/${slug}`,
|
|
214
|
+
languageMap.codeById,
|
|
215
|
+
languageMap.defaultCode,
|
|
216
|
+
);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
console.error('An unexpected error occurred while fetching posts:', err);
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Fetches all active storefront products from Supabase and formats them, with
|
|
225
|
+
* language alternates, for the sitemap.
|
|
226
|
+
*/
|
|
227
|
+
export async function fetchAllActiveProducts(): Promise<SitemapEntry[]> {
|
|
228
|
+
const supabase = getSsgSupabaseClient();
|
|
229
|
+
try {
|
|
230
|
+
const [{ data: products, error }, languageMap] = await Promise.all([
|
|
231
|
+
supabase
|
|
232
|
+
.from('products')
|
|
233
|
+
.select('slug, updated_at, created_at, language_id, translation_group_id')
|
|
234
|
+
.eq('status', 'active'),
|
|
235
|
+
fetchLanguageMap(supabase),
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
if (error) {
|
|
239
|
+
console.error('Error fetching active products:', error);
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return rowsToEntries(
|
|
244
|
+
products ?? [],
|
|
245
|
+
(slug) => `/product/${slug}`,
|
|
246
|
+
languageMap.codeById,
|
|
247
|
+
languageMap.defaultCode,
|
|
248
|
+
);
|
|
249
|
+
} catch (err) {
|
|
250
|
+
console.error('An unexpected error occurred while fetching products:', err);
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { AccountNavigationMenu } from '@nextblock-cms/ecommerce';
|
|
4
|
-
import { useTranslations } from '@nextblock-cms/utils';
|
|
5
|
-
import {
|
|
6
|
-
Avatar,
|
|
7
|
-
AvatarFallback,
|
|
8
|
-
AvatarImage,
|
|
9
|
-
Card,
|
|
10
|
-
CardContent,
|
|
11
|
-
CardHeader,
|
|
12
|
-
CardTitle,
|
|
13
|
-
} from '@nextblock-cms/ui';
|
|
14
|
-
import { User as UserIcon } from 'lucide-react';
|
|
15
|
-
|
|
16
|
-
import type { ProfileAccountSummary, ProfileAccountUser } from './account-types';
|
|
17
|
-
import { profileAccountLinks } from './account-links';
|
|
18
|
-
|
|
19
|
-
interface ProfileAccountSidebarProps {
|
|
20
|
-
profile: ProfileAccountSummary;
|
|
21
|
-
user: ProfileAccountUser;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function ProfileAccountSidebar({
|
|
25
|
-
profile,
|
|
26
|
-
user,
|
|
27
|
-
}: ProfileAccountSidebarProps) {
|
|
28
|
-
const { t } = useTranslations();
|
|
29
|
-
const displayName =
|
|
30
|
-
profile.full_name || profile.github_username || user.email || 'User';
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<Card className="h-fit">
|
|
34
|
-
<CardHeader>
|
|
35
|
-
<CardTitle className="text-xl">
|
|
36
|
-
{t('public_profile') === 'public_profile'
|
|
37
|
-
? 'Public Profile'
|
|
38
|
-
: t('public_profile')}
|
|
39
|
-
</CardTitle>
|
|
40
|
-
</CardHeader>
|
|
41
|
-
|
|
42
|
-
<CardContent className="flex flex-col items-center space-y-4 text-center">
|
|
43
|
-
<Avatar className="h-28 w-28 border-4 border-muted">
|
|
44
|
-
<AvatarImage
|
|
45
|
-
src={profile.avatar_url || undefined}
|
|
46
|
-
alt={displayName}
|
|
47
|
-
className="object-cover"
|
|
48
|
-
/>
|
|
49
|
-
<AvatarFallback className="bg-secondary text-3xl">
|
|
50
|
-
{displayName?.charAt(0)?.toUpperCase() || (
|
|
51
|
-
<UserIcon className="h-10 w-10" />
|
|
52
|
-
)}
|
|
53
|
-
</AvatarFallback>
|
|
54
|
-
</Avatar>
|
|
55
|
-
|
|
56
|
-
<div className="w-full space-y-1 text-left">
|
|
57
|
-
<div className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
|
|
58
|
-
{t('identity') === 'identity' ? 'Identity' : t('identity')}
|
|
59
|
-
</div>
|
|
60
|
-
<p className="text-lg font-semibold">{displayName}</p>
|
|
61
|
-
{user.email ? (
|
|
62
|
-
<p className="text-sm text-muted-foreground">{user.email}</p>
|
|
63
|
-
) : null}
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
<AccountNavigationMenu
|
|
67
|
-
links={profileAccountLinks}
|
|
68
|
-
className="text-left"
|
|
69
|
-
/>
|
|
70
|
-
</CardContent>
|
|
71
|
-
</Card>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { AccountNavigationMenu } from '@nextblock-cms/ecommerce';
|
|
4
|
+
import { useTranslations } from '@nextblock-cms/utils';
|
|
5
|
+
import {
|
|
6
|
+
Avatar,
|
|
7
|
+
AvatarFallback,
|
|
8
|
+
AvatarImage,
|
|
9
|
+
Card,
|
|
10
|
+
CardContent,
|
|
11
|
+
CardHeader,
|
|
12
|
+
CardTitle,
|
|
13
|
+
} from '@nextblock-cms/ui';
|
|
14
|
+
import { User as UserIcon } from 'lucide-react';
|
|
15
|
+
|
|
16
|
+
import type { ProfileAccountSummary, ProfileAccountUser } from './account-types';
|
|
17
|
+
import { profileAccountLinks } from './account-links';
|
|
18
|
+
|
|
19
|
+
interface ProfileAccountSidebarProps {
|
|
20
|
+
profile: ProfileAccountSummary;
|
|
21
|
+
user: ProfileAccountUser;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function ProfileAccountSidebar({
|
|
25
|
+
profile,
|
|
26
|
+
user,
|
|
27
|
+
}: ProfileAccountSidebarProps) {
|
|
28
|
+
const { t } = useTranslations();
|
|
29
|
+
const displayName =
|
|
30
|
+
profile.full_name || profile.github_username || user.email || 'User';
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Card className="h-fit">
|
|
34
|
+
<CardHeader>
|
|
35
|
+
<CardTitle className="text-xl">
|
|
36
|
+
{t('public_profile') === 'public_profile'
|
|
37
|
+
? 'Public Profile'
|
|
38
|
+
: t('public_profile')}
|
|
39
|
+
</CardTitle>
|
|
40
|
+
</CardHeader>
|
|
41
|
+
|
|
42
|
+
<CardContent className="flex flex-col items-center space-y-4 text-center">
|
|
43
|
+
<Avatar className="h-28 w-28 border-4 border-muted">
|
|
44
|
+
<AvatarImage
|
|
45
|
+
src={profile.avatar_url || undefined}
|
|
46
|
+
alt={displayName}
|
|
47
|
+
className="object-cover"
|
|
48
|
+
/>
|
|
49
|
+
<AvatarFallback className="bg-secondary text-3xl">
|
|
50
|
+
{displayName?.charAt(0)?.toUpperCase() || (
|
|
51
|
+
<UserIcon className="h-10 w-10" />
|
|
52
|
+
)}
|
|
53
|
+
</AvatarFallback>
|
|
54
|
+
</Avatar>
|
|
55
|
+
|
|
56
|
+
<div className="w-full space-y-1 text-left">
|
|
57
|
+
<div className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
|
|
58
|
+
{t('identity') === 'identity' ? 'Identity' : t('identity')}
|
|
59
|
+
</div>
|
|
60
|
+
<p className="text-lg font-semibold">{displayName}</p>
|
|
61
|
+
{user.email ? (
|
|
62
|
+
<p className="text-sm text-muted-foreground">{user.email}</p>
|
|
63
|
+
) : null}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<AccountNavigationMenu
|
|
67
|
+
links={profileAccountLinks}
|
|
68
|
+
className="text-left"
|
|
69
|
+
/>
|
|
70
|
+
</CardContent>
|
|
71
|
+
</Card>
|
|
72
|
+
);
|
|
73
|
+
}
|