create-nextblock 0.2.78 → 0.8.0
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/bin/create-nextblock.js +740 -459
- package/package.json +1 -2
- package/scripts/sync-template.js +18 -1
- package/templates/nextblock-template/.browserslistrc +11 -0
- package/templates/nextblock-template/.swcrc +30 -30
- package/templates/nextblock-template/README.md +23 -114
- package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -28
- package/templates/nextblock-template/app/(auth-pages)/sign-in/page.tsx +50 -25
- package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +111 -56
- package/templates/nextblock-template/app/(auth-pages)/two-factor/actions.ts +91 -0
- package/templates/nextblock-template/app/(auth-pages)/two-factor/components/TwoFactorForm.tsx +118 -0
- package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -0
- package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -0
- package/templates/nextblock-template/app/[slug]/PageClientContent.tsx +48 -28
- package/templates/nextblock-template/app/[slug]/page.tsx +63 -6
- package/templates/nextblock-template/app/[slug]/page.utils.ts +374 -157
- package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -0
- package/templates/nextblock-template/app/actions/consent.ts +57 -0
- package/templates/nextblock-template/app/actions/formActions.ts +130 -11
- package/templates/nextblock-template/app/actions/languageActions.ts +31 -30
- package/templates/nextblock-template/app/actions/package-actions.ts +183 -0
- package/templates/nextblock-template/app/actions/postActions.ts +146 -48
- package/templates/nextblock-template/app/actions/twoFactorEmail.ts +21 -0
- package/templates/nextblock-template/app/actions/visualEditingActions.test.ts +179 -0
- package/templates/nextblock-template/app/actions/visualEditingActions.ts +345 -0
- package/templates/nextblock-template/app/actions.ts +67 -12
- package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -0
- package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -0
- package/templates/nextblock-template/app/api/ai/global-agent/route.ts +965 -0
- package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -0
- package/templates/nextblock-template/app/api/checkout/route.ts +146 -0
- package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -0
- package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -0
- package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3413 -17
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +7830 -0
- package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +35 -0
- package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -0
- package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -0
- package/templates/nextblock-template/app/api/draft/disable/route.ts +25 -0
- package/templates/nextblock-template/app/api/draft/route.ts +93 -0
- package/templates/nextblock-template/app/api/draft/start/route.ts +77 -0
- package/templates/nextblock-template/app/api/media/library/route.ts +65 -0
- package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -0
- package/templates/nextblock-template/app/api/media/record/route.ts +160 -0
- package/templates/nextblock-template/app/api/search/route.ts +43 -0
- package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -0
- package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -0
- package/templates/nextblock-template/app/api/webhooks/freemius/route.ts +34 -0
- package/templates/nextblock-template/app/api/webhooks/stripe/route.ts +27 -0
- package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +392 -128
- package/templates/nextblock-template/app/article/[slug]/page.tsx +179 -127
- package/templates/nextblock-template/app/article/[slug]/page.utils.ts +262 -77
- package/templates/nextblock-template/app/auth/callback/route.ts +31 -58
- package/templates/nextblock-template/app/cart/page.tsx +7 -0
- package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -0
- package/templates/nextblock-template/app/checkout/page.tsx +52 -0
- package/templates/nextblock-template/app/checkout/success/actions.ts +136 -0
- package/templates/nextblock-template/app/checkout/success/page.tsx +186 -0
- package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +163 -33
- package/templates/nextblock-template/app/cms/blocks/actions.ts +424 -235
- package/templates/nextblock-template/app/cms/blocks/components/BackgroundSelector.tsx +212 -151
- package/templates/nextblock-template/app/cms/blocks/components/BlockEditorArea.tsx +41 -20
- package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +152 -19
- package/templates/nextblock-template/app/cms/blocks/components/BlockTypeCard.tsx +25 -17
- package/templates/nextblock-template/app/cms/blocks/components/BlockTypeSelector.tsx +200 -18
- package/templates/nextblock-template/app/cms/blocks/components/ColumnEditor.tsx +33 -16
- package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -0
- package/templates/nextblock-template/app/cms/blocks/components/EditableBlock.tsx +37 -18
- package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -67
- package/templates/nextblock-template/app/cms/blocks/components/SectionConfigPanel.tsx +108 -31
- package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -0
- package/templates/nextblock-template/app/cms/blocks/editors/FeaturedProductBlockEditor.tsx +31 -0
- package/templates/nextblock-template/app/cms/blocks/editors/FormBlockEditor.tsx +2 -2
- package/templates/nextblock-template/app/cms/blocks/editors/HeadingBlockEditor.tsx +1 -1
- package/templates/nextblock-template/app/cms/blocks/editors/ImageBlockEditor.tsx +29 -29
- package/templates/nextblock-template/app/cms/blocks/editors/PostsGridBlockEditor.tsx +14 -18
- package/templates/nextblock-template/app/cms/blocks/editors/ProductGridBlockEditor.tsx +41 -0
- package/templates/nextblock-template/app/cms/blocks/editors/SectionBlockEditor.tsx +318 -118
- package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +98 -21
- package/templates/nextblock-template/app/cms/blocks/editors/VideoEmbedBlockEditor.tsx +1 -1
- package/templates/nextblock-template/app/cms/components/ContentLanguageSwitcher.tsx +27 -9
- package/templates/nextblock-template/app/cms/components/CopyContentFromLanguage.tsx +1 -1
- package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -0
- package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -0
- package/templates/nextblock-template/app/cms/components/CortexGlobalAgentChat.tsx +1507 -0
- package/templates/nextblock-template/app/cms/components/DraftStatusActions.tsx +145 -0
- package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +244 -0
- package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +38 -24
- package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -0
- package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -0
- package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -0
- package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -0
- package/templates/nextblock-template/app/cms/custom-blocks/components/BlockComposer.tsx +1522 -0
- package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -0
- package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -0
- package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -0
- package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -0
- package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -0
- package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -98
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -0
- package/templates/nextblock-template/app/cms/dashboard/page.tsx +182 -154
- package/templates/nextblock-template/app/cms/import-export/ContentTransferControls.tsx +391 -0
- package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -0
- package/templates/nextblock-template/app/cms/layout.tsx +29 -10
- package/templates/nextblock-template/app/cms/media/UploadFolderContext.tsx +22 -22
- package/templates/nextblock-template/app/cms/media/actions.ts +45 -124
- package/templates/nextblock-template/app/cms/media/components/DeleteMediaButtonClient.tsx +1 -1
- package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
- package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -64
- package/templates/nextblock-template/app/cms/media/components/MediaPickerDialog.tsx +227 -158
- package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +101 -89
- package/templates/nextblock-template/app/cms/media/page.tsx +1 -1
- package/templates/nextblock-template/app/cms/navigation/components/NavigationItemForm.tsx +2 -2
- package/templates/nextblock-template/app/cms/orders/[id]/MarkPaidButton.tsx +44 -0
- package/templates/nextblock-template/app/cms/orders/[id]/page.tsx +16 -0
- package/templates/nextblock-template/app/cms/orders/actions.ts +201 -0
- package/templates/nextblock-template/app/cms/orders/page.tsx +20 -0
- package/templates/nextblock-template/app/cms/orders/types.ts +20 -0
- package/templates/nextblock-template/app/cms/pages/[id]/edit/EditPageClient.tsx +156 -121
- package/templates/nextblock-template/app/cms/pages/[id]/edit/page.tsx +79 -26
- package/templates/nextblock-template/app/cms/pages/actions.ts +54 -38
- package/templates/nextblock-template/app/cms/pages/components/DeletePageButtonClient.tsx +1 -1
- package/templates/nextblock-template/app/cms/pages/components/PageForm.tsx +267 -116
- package/templates/nextblock-template/app/cms/pages/page.tsx +25 -18
- package/templates/nextblock-template/app/cms/payments/page.tsx +16 -0
- package/templates/nextblock-template/app/cms/posts/[id]/edit/page.tsx +132 -90
- package/templates/nextblock-template/app/cms/posts/actions.ts +71 -72
- package/templates/nextblock-template/app/cms/posts/components/DeletePostButtonClient.tsx +1 -1
- package/templates/nextblock-template/app/cms/posts/components/PostForm.tsx +256 -245
- package/templates/nextblock-template/app/cms/posts/new/page.tsx +1 -1
- package/templates/nextblock-template/app/cms/posts/page.tsx +20 -13
- package/templates/nextblock-template/app/cms/products/ClientNotionEditor.tsx +16 -0
- package/templates/nextblock-template/app/cms/products/ProductFormClientShell.tsx +56 -0
- package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +292 -0
- package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -0
- package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -0
- package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -0
- package/templates/nextblock-template/app/cms/products/new/page.tsx +143 -0
- package/templates/nextblock-template/app/cms/products/page.tsx +42 -0
- package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -0
- package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -0
- package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -0
- package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -0
- package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -0
- package/templates/nextblock-template/app/cms/revisions/RevisionHistoryButton.tsx +2 -2
- package/templates/nextblock-template/app/cms/revisions/actions.ts +285 -285
- package/templates/nextblock-template/app/cms/revisions/service.ts +19 -16
- package/templates/nextblock-template/app/cms/revisions/utils.ts +8 -3
- package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -0
- package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -0
- package/templates/nextblock-template/app/cms/settings/bot-protection/actions.ts +93 -0
- package/templates/nextblock-template/app/cms/settings/bot-protection/components/BotProtectionForm.tsx +129 -0
- package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -0
- package/templates/nextblock-template/app/cms/settings/copyright/actions.ts +1 -1
- package/templates/nextblock-template/app/cms/settings/copyright/components/CopyrightForm.tsx +2 -2
- package/templates/nextblock-template/app/cms/settings/copyright/page.tsx +1 -1
- package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +496 -0
- package/templates/nextblock-template/app/cms/settings/cortex-ai/StoredCortexAiSettingsClient.tsx +410 -0
- package/templates/nextblock-template/app/cms/settings/cortex-ai/actions.ts +248 -0
- package/templates/nextblock-template/app/cms/settings/cortex-ai/page.tsx +80 -0
- package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -0
- package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -0
- package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -0
- package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +203 -44
- package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -242
- package/templates/nextblock-template/app/cms/settings/global-css/actions.ts +65 -0
- package/templates/nextblock-template/app/cms/settings/global-css/components/GlobalCssForm.tsx +46 -0
- package/templates/nextblock-template/app/cms/settings/global-css/page.tsx +24 -0
- package/templates/nextblock-template/app/cms/settings/languages/components/DeleteLanguageButton.tsx +1 -1
- package/templates/nextblock-template/app/cms/settings/languages/components/LanguageForm.tsx +2 -2
- package/templates/nextblock-template/app/cms/settings/languages/page.tsx +1 -1
- package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
- package/templates/nextblock-template/app/cms/settings/logos/actions.ts +82 -6
- package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -0
- package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -18
- package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +20 -16
- package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -0
- package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
- package/templates/nextblock-template/app/cms/settings/logos/page.tsx +120 -82
- package/templates/nextblock-template/app/cms/settings/logos/types.ts +8 -8
- package/templates/nextblock-template/app/cms/settings/packages/activation-form.tsx +84 -0
- package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -0
- package/templates/nextblock-template/app/cms/settings/packages/page.tsx +49 -0
- package/templates/nextblock-template/app/cms/settings/privacy/actions.ts +53 -0
- package/templates/nextblock-template/app/cms/settings/privacy/components/PrivacyForm.tsx +196 -0
- package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +26 -0
- package/templates/nextblock-template/app/cms/settings/security/actions.ts +251 -0
- package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +453 -0
- package/templates/nextblock-template/app/cms/settings/security/page.tsx +13 -0
- package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -0
- package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -0
- package/templates/nextblock-template/app/cms/users/[id]/edit/page.tsx +28 -23
- package/templates/nextblock-template/app/cms/users/actions.ts +105 -40
- package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +1 -1
- package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +65 -152
- package/templates/nextblock-template/app/cms/users/page.tsx +15 -10
- package/templates/nextblock-template/app/globals.css +9 -0
- package/templates/nextblock-template/app/layout.tsx +372 -120
- package/templates/nextblock-template/app/lib/seo.test.ts +52 -0
- package/templates/nextblock-template/app/lib/seo.ts +279 -0
- package/templates/nextblock-template/app/lib/site-settings.ts +87 -0
- package/templates/nextblock-template/app/lib/sitemap-utils.ts +224 -39
- package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -0
- package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -0
- package/templates/nextblock-template/app/lib/ucp/server.ts +1914 -0
- package/templates/nextblock-template/app/page.tsx +165 -73
- package/templates/nextblock-template/app/product/[slug]/page.tsx +433 -0
- package/templates/nextblock-template/app/profile/ProfileAccountSidebar.tsx +73 -0
- package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -0
- package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -0
- package/templates/nextblock-template/app/profile/account-data.ts +37 -0
- package/templates/nextblock-template/app/profile/account-links.ts +22 -0
- package/templates/nextblock-template/app/profile/account-types.ts +11 -0
- package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -0
- package/templates/nextblock-template/app/profile/orders/[id]/CustomerOrderDetailPageClient.tsx +79 -0
- package/templates/nextblock-template/app/profile/orders/[id]/page.tsx +32 -0
- package/templates/nextblock-template/app/profile/orders/page.tsx +19 -0
- package/templates/nextblock-template/app/profile/page.tsx +51 -0
- package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -0
- package/templates/nextblock-template/app/profile/password/actions.ts +59 -0
- package/templates/nextblock-template/app/profile/password/page.tsx +27 -0
- package/templates/nextblock-template/app/providers.tsx +55 -17
- package/templates/nextblock-template/app/robots.txt/route.ts +11 -1
- package/templates/nextblock-template/app/sitemap.ts +128 -0
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -0
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -0
- package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -0
- package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -0
- package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -0
- package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -0
- package/templates/nextblock-template/components/AppShell.tsx +154 -0
- package/templates/nextblock-template/components/BlockRenderer.tsx +210 -64
- package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -0
- package/templates/nextblock-template/components/CartTranslator.tsx +210 -0
- package/templates/nextblock-template/components/CurrentContentSetter.tsx +25 -0
- package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -0
- package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -0
- package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -0
- package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -0
- package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -0
- package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -0
- package/templates/nextblock-template/components/GitHubLoginButton.tsx +36 -0
- package/templates/nextblock-template/components/GlobalSearch.tsx +557 -0
- package/templates/nextblock-template/components/Header.tsx +49 -41
- package/templates/nextblock-template/components/LanguageSwitcher.tsx +55 -32
- package/templates/nextblock-template/components/ResponsiveNav.tsx +138 -43
- package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -8
- package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -55
- package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +42 -37
- package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +6 -2
- package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -0
- package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -0
- package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -84
- package/templates/nextblock-template/components/blocks/renderers/CartBlockRenderer.tsx +17 -0
- package/templates/nextblock-template/components/blocks/renderers/CheckoutBlockRenderer.tsx +19 -0
- package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +262 -8
- package/templates/nextblock-template/components/blocks/renderers/FeaturedProductBlockRenderer.tsx +22 -0
- package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +320 -37
- package/templates/nextblock-template/components/blocks/renderers/HeadingBlockRenderer.tsx +11 -8
- package/templates/nextblock-template/components/blocks/renderers/ImageBlockRenderer.tsx +12 -3
- package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +18 -13
- package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +90 -0
- package/templates/nextblock-template/components/blocks/renderers/ProductGridBlockRenderer.tsx +31 -0
- package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +424 -55
- package/templates/nextblock-template/components/blocks/renderers/SectionSlider.tsx +137 -0
- package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -0
- package/templates/nextblock-template/components/blocks/renderers/TextBlockRenderer.tsx +37 -22
- package/templates/nextblock-template/components/blocks/renderers/VideoEmbedBlockRenderer.tsx +23 -15
- package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +1 -3
- package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +1 -3
- package/templates/nextblock-template/components/blocks/types.ts +7 -6
- package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
- package/templates/nextblock-template/components/form-message.tsx +32 -26
- package/templates/nextblock-template/components/header-auth.tsx +69 -17
- package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +127 -0
- package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +59 -0
- package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -0
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -0
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +464 -0
- package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
- package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -0
- package/templates/nextblock-template/components/visual-editing/NextblockVisualEditing.tsx +1172 -0
- package/templates/nextblock-template/context/AuthContext.tsx +23 -90
- package/templates/nextblock-template/context/CurrentContentContext.tsx +10 -4
- package/templates/nextblock-template/context/LanguageContext.tsx +16 -16
- package/templates/nextblock-template/context/language-rest-client.ts +31 -0
- package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -0
- package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -0
- package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -0
- package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +252 -0
- package/templates/nextblock-template/docs/05-DEVELOPER-GUIDE.md +238 -0
- package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +125 -0
- package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -0
- package/templates/nextblock-template/docs/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +1319 -0
- package/templates/nextblock-template/docs/09-LIVE-DRAFT-MODE.md +104 -0
- package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -0
- package/templates/nextblock-template/docs/README.md +34 -0
- package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +12507 -0
- package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -14
- package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -0
- package/templates/nextblock-template/index.d.ts +2 -0
- package/templates/nextblock-template/lib/ai-block-generation.ts +339 -0
- package/templates/nextblock-template/lib/ai-client.ts +247 -0
- package/templates/nextblock-template/lib/ai-config.ts +81 -0
- package/templates/nextblock-template/lib/ai-cortex-widget-builder.ts +125 -0
- package/templates/nextblock-template/lib/ai-global-agent-custom-block-tools.ts +363 -0
- package/templates/nextblock-template/lib/ai-global-agent-db-tools.test.ts +405 -0
- package/templates/nextblock-template/lib/ai-global-agent-db-tools.ts +1228 -0
- package/templates/nextblock-template/lib/ai-global-agent-ecommerce.ts +5 -0
- package/templates/nextblock-template/lib/ai-global-agent-tools-stats.test.ts +223 -0
- package/templates/nextblock-template/lib/ai-global-agent-tools.test.ts +2183 -0
- package/templates/nextblock-template/lib/ai-global-agent-tools.ts +4807 -0
- package/templates/nextblock-template/lib/ai-key-crypto.test.ts +70 -0
- package/templates/nextblock-template/lib/ai-key-crypto.ts +132 -0
- package/templates/nextblock-template/lib/ai-model-catalog.test.ts +49 -0
- package/templates/nextblock-template/lib/ai-model-catalog.ts +41 -0
- package/templates/nextblock-template/lib/ai-model-registry.test.ts +231 -0
- package/templates/nextblock-template/lib/ai-model-registry.ts +522 -0
- package/templates/nextblock-template/lib/auth/cookies.ts +47 -0
- package/templates/nextblock-template/lib/auth/crypto.ts +42 -0
- package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -0
- package/templates/nextblock-template/lib/auth/twoFactor.ts +167 -0
- package/templates/nextblock-template/lib/auth-redirects.ts +46 -0
- package/templates/nextblock-template/lib/blocks/FeaturedProductBlock.tsx +94 -0
- package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +137 -0
- package/templates/nextblock-template/lib/blocks/README.md +13 -670
- package/templates/nextblock-template/lib/blocks/blockRegistry.ts +138 -56
- package/templates/nextblock-template/lib/blocks/blockTypes.ts +18 -0
- package/templates/nextblock-template/lib/blocks/ecommerce-block-schemas.ts +31 -0
- package/templates/nextblock-template/lib/cms-transfer/csv.test.ts +77 -0
- package/templates/nextblock-template/lib/cms-transfer/csv.ts +399 -0
- package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -0
- package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -0
- package/templates/nextblock-template/lib/cortex-widget-registry.test.ts +199 -0
- package/templates/nextblock-template/lib/cortex-widget-registry.ts +88 -0
- package/templates/nextblock-template/lib/cortex-widget-schema.test.tsx +237 -0
- package/templates/nextblock-template/lib/cortex-widget-schema.ts +393 -0
- package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -0
- package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -0
- package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -0
- package/templates/nextblock-template/lib/custom-block-r2-upload.ts +68 -0
- package/templates/nextblock-template/lib/custom-block-relation-registry.ts +256 -0
- package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -0
- package/templates/nextblock-template/lib/custom-block-relations.ts +279 -0
- package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -0
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -0
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -0
- package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -0
- package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -0
- package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -0
- package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -0
- package/templates/nextblock-template/lib/full-backup/server.ts +743 -0
- package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +45 -0
- package/templates/nextblock-template/lib/posts/readTime.ts +60 -0
- package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -0
- package/templates/nextblock-template/lib/privacy/settings.ts +103 -0
- package/templates/nextblock-template/lib/privacy/types.ts +67 -0
- package/templates/nextblock-template/lib/promotions/server.test.ts +74 -0
- package/templates/nextblock-template/lib/promotions/server.ts +741 -0
- package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -0
- package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -0
- package/templates/nextblock-template/lib/search/server.ts +585 -0
- package/templates/nextblock-template/lib/search/types.ts +27 -0
- package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -0
- package/templates/nextblock-template/lib/visual-editing/draft-content.ts +380 -0
- package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -0
- package/templates/nextblock-template/lib/visual-editing/draft-route.ts +82 -0
- package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -0
- package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -0
- package/templates/nextblock-template/lib/visual-editing/mutations.ts +190 -0
- package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -0
- package/templates/nextblock-template/lib/visual-editing/product-drafts.ts +511 -0
- package/templates/nextblock-template/lib/visual-editing/types.ts +122 -0
- package/templates/nextblock-template/lib/zod-config.ts +5 -0
- package/templates/nextblock-template/next.config.js +190 -66
- package/templates/nextblock-template/package.json +34 -30
- package/templates/nextblock-template/proxy.ts +435 -253
- package/templates/nextblock-template/public/images/NBcover.webp +0 -0
- package/templates/nextblock-template/public/images/cap.webp +0 -0
- package/templates/nextblock-template/public/images/commerce-plan.webp +0 -0
- package/templates/nextblock-template/public/images/commerce-square.webp +0 -0
- package/templates/nextblock-template/public/images/commerce-wide.webp +0 -0
- package/templates/nextblock-template/public/images/cortex-ai-square.webp +0 -0
- package/templates/nextblock-template/public/images/cortex-ai.webp +0 -0
- package/templates/nextblock-template/public/images/extensibility.webp +0 -0
- package/templates/nextblock-template/public/images/goals.webp +0 -0
- package/templates/nextblock-template/public/images/included.webp +0 -0
- package/templates/nextblock-template/public/images/nx-graph.webp +0 -0
- package/templates/nextblock-template/public/images/pants.webp +0 -0
- package/templates/nextblock-template/public/images/t-shirt.webp +0 -0
- package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -0
- package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +100 -0
- package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -0
- package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -0
- package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -0
- package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -0
- package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -0
- package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -0
- package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -0
- package/templates/nextblock-template/tailwind.config.js +1 -0
- package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -0
- package/templates/nextblock-template/tools/deploy-supabase.js +69 -71
- package/templates/nextblock-template/tsconfig.json +52 -66
- package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
- package/templates/nextblock-template/types/jsdom.d.ts +6 -0
- package/templates/nextblock-template/app/force-styles.tsx +0 -31
- package/templates/nextblock-template/app/sitemap.xml/route.ts +0 -63
- package/templates/nextblock-template/components/blocks/renderers/HeroBlockRenderer.tsx +0 -273
- package/templates/nextblock-template/docs/How to Create a Custom Block.md +0 -149
- package/templates/nextblock-template/docs/cms-application-overview.md +0 -56
- package/templates/nextblock-template/docs/cms-architecture-overview.md +0 -73
- package/templates/nextblock-template/docs/files-structure.md +0 -426
- package/templates/nextblock-template/docs/tiptap-bundle-optimization-summary.md +0 -174
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// components/LanguageSwitcher.tsx
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
-
import { useLanguage } from '
|
|
5
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@nextblock-cms/ui';
|
|
4
|
+
import { useLanguage } from '../context/LanguageContext';
|
|
5
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@nextblock-cms/ui';
|
|
6
6
|
import { useRouter, usePathname } from 'next/navigation';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
7
|
+
import { getContentTranslations, getContentMetadataBySlugAndLocale } from '../app/actions/languageActions';
|
|
8
|
+
import { Language } from '../app/actions/languageActions';
|
|
9
|
+
import { useCurrentContent } from '../context/CurrentContentContext';
|
|
9
10
|
|
|
10
11
|
interface CurrentPageInfo {
|
|
11
12
|
slug: string;
|
|
@@ -17,6 +18,7 @@ interface LanguageSwitcherProps {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export default function LanguageSwitcher({ currentPageData }: LanguageSwitcherProps) {
|
|
21
|
+
const { currentContent } = useCurrentContent();
|
|
20
22
|
const { currentLocale, setCurrentLocale, availableLanguages, isLoadingLanguages } = useLanguage();
|
|
21
23
|
const router = useRouter();
|
|
22
24
|
const pathname = usePathname();
|
|
@@ -36,47 +38,68 @@ export default function LanguageSwitcher({ currentPageData }: LanguageSwitcherPr
|
|
|
36
38
|
if (isHomePage) {
|
|
37
39
|
targetPath = '/'; // For any homepage, new language path is root
|
|
38
40
|
} else {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
// Extract slug from pathname (remove leading slash)
|
|
42
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
43
|
+
const currentSlug = segments[segments.length - 1] || '';
|
|
44
|
+
|
|
45
|
+
let contentMetadata = currentContent.id ? {
|
|
46
|
+
slug: currentContent.slug || '',
|
|
47
|
+
translation_group_id: currentContent.translation_group_id || null,
|
|
48
|
+
type: currentContent.type
|
|
49
|
+
} : null;
|
|
50
|
+
|
|
51
|
+
// Fallback to fetching if context is missing but we have a slug
|
|
52
|
+
if (!contentMetadata && currentSlug && !isHomePage) {
|
|
53
|
+
try {
|
|
54
|
+
// Try pages first, then products, then posts?
|
|
55
|
+
// Or determine from pathname prefix
|
|
56
|
+
let type: 'pages' | 'posts' | 'products' = 'pages';
|
|
57
|
+
if (pathname.includes('/product/')) type = 'products';
|
|
58
|
+
if (pathname.includes('/article/')) type = 'posts';
|
|
59
|
+
|
|
60
|
+
const fetchedMetadata = await getContentMetadataBySlugAndLocale(currentSlug, currentLocale, type);
|
|
61
|
+
if (fetchedMetadata) {
|
|
62
|
+
contentMetadata = fetchedMetadata as any;
|
|
53
63
|
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Error fetching content metadata:', error);
|
|
54
66
|
}
|
|
67
|
+
}
|
|
55
68
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
if (contentMetadata?.translation_group_id) {
|
|
70
|
+
try {
|
|
71
|
+
const typePlural = contentMetadata.type === 'product' ? 'products' : (contentMetadata.type === 'post' ? 'posts' : 'pages');
|
|
72
|
+
const translations = await getContentTranslations(contentMetadata.translation_group_id, typePlural);
|
|
73
|
+
const foundTranslation = translations.find(t => t.language_code === newLocaleCode);
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
if (foundTranslation) {
|
|
76
|
+
// Construct target path based on type
|
|
77
|
+
if (contentMetadata.type === 'product') {
|
|
78
|
+
targetPath = `/product/${foundTranslation.slug}`;
|
|
79
|
+
} else if (contentMetadata.type === 'post') {
|
|
80
|
+
targetPath = `/article/${foundTranslation.slug}`;
|
|
63
81
|
} else {
|
|
64
|
-
|
|
65
|
-
console.warn(`No translation found for ${pageMetadata.slug} to ${newLocaleCode}. Falling back to current path.`);
|
|
82
|
+
targetPath = `/${foundTranslation.slug}`;
|
|
66
83
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
console.error("Error fetching page translations:", error);
|
|
84
|
+
} else {
|
|
85
|
+
console.warn(`No translation found for ${contentMetadata.slug} to ${newLocaleCode}. Falling back to current path.`);
|
|
70
86
|
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
console.warn(`No translation_group_id for page: ${pageMetadata?.slug || currentSlug}. Current path will be used.`);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error("Error fetching translations:", error);
|
|
74
89
|
}
|
|
90
|
+
} else if (!isHomePage) {
|
|
91
|
+
console.warn(`No translation_group_id for content: ${contentMetadata?.slug || currentSlug}. Current path will be used.`);
|
|
92
|
+
}
|
|
75
93
|
}
|
|
76
94
|
|
|
77
95
|
setTimeout(() => {
|
|
78
96
|
if (pathname !== targetPath) {
|
|
79
97
|
router.push(targetPath);
|
|
98
|
+
// Force Next.js to re-fetch the root layout since the language cookie changed.
|
|
99
|
+
// This ensures the Header/Footer navigation links update accurately.
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
router.refresh();
|
|
102
|
+
}, 50);
|
|
80
103
|
} else {
|
|
81
104
|
// If path is the same, refresh to ensure content updates for the new locale
|
|
82
105
|
router.refresh();
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import Link from 'next/link';
|
|
4
|
+
import { usePathname } from 'next/navigation';
|
|
4
5
|
import React, { useState, useEffect, useMemo, useRef } from 'react'
|
|
5
6
|
import type { Database } from '@nextblock-cms/db' // Relative path from components/
|
|
6
|
-
import { useCurrentContent } from '../context/CurrentContentContext';
|
|
7
|
-
import { useTranslations } from '@nextblock-cms/utils';
|
|
7
|
+
import { useCurrentContent } from '../context/CurrentContentContext';
|
|
8
|
+
import { useTranslations } from '@nextblock-cms/utils';
|
|
9
|
+
import { DeferredGlobalSearch } from './DeferredGlobalSearch';
|
|
10
|
+
import { resolveMediaUrl } from '../lib/media/resolveMediaUrl';
|
|
8
11
|
|
|
9
12
|
type Logo = Database['public']['Tables']['logos']['Row'] & { media: (Database['public']['Tables']['media']['Row'] & { alt_text: string | null }) | null };
|
|
10
13
|
type NavigationItem = Database['public']['Tables']['navigation_items']['Row'];
|
|
11
|
-
import Image from 'next/image'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const FALLBACK_LOGO_PATH = '/images/nextblock-logo-small.webp';
|
|
14
|
+
import Image from 'next/image'
|
|
15
|
+
import { EyeOff, FilePenLine, Pencil } from 'lucide-react';
|
|
16
|
+
|
|
17
|
+
const FALLBACK_LOGO_PATH = '/images/nextblock-logo-small.webp';
|
|
15
18
|
|
|
16
19
|
// Define a type for hierarchical navigation items
|
|
17
20
|
interface HierarchicalNavigationItem extends NavigationItem {
|
|
@@ -66,8 +69,22 @@ interface ResponsiveNavProps {
|
|
|
66
69
|
navItems: NavigationItem[]
|
|
67
70
|
canAccessCms: boolean;
|
|
68
71
|
cmsDashboardLinkHref: string;
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
isDraftModeEnabled: boolean;
|
|
73
|
+
renderHeaderAuth: () => React.ReactNode;
|
|
74
|
+
renderLanguageSwitcher: () => React.ReactNode;
|
|
75
|
+
renderCurrencySwitcher?: () => React.ReactNode;
|
|
76
|
+
renderCartIcon?: () => React.ReactNode;
|
|
77
|
+
isEcommerceActive?: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function ClientOnly({ children }: { children: React.ReactNode }) {
|
|
81
|
+
const [mounted, setMounted] = useState(false);
|
|
82
|
+
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
setMounted(true);
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
return mounted ? <>{children}</> : null;
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
export default function ResponsiveNav({
|
|
@@ -75,22 +92,39 @@ export default function ResponsiveNav({
|
|
|
75
92
|
navItems,
|
|
76
93
|
canAccessCms,
|
|
77
94
|
cmsDashboardLinkHref,
|
|
78
|
-
|
|
79
|
-
|
|
95
|
+
isDraftModeEnabled,
|
|
96
|
+
renderHeaderAuth,
|
|
97
|
+
renderLanguageSwitcher,
|
|
98
|
+
renderCurrencySwitcher,
|
|
80
99
|
logo,
|
|
81
100
|
siteTitle,
|
|
101
|
+
renderCartIcon,
|
|
102
|
+
isEcommerceActive = false,
|
|
82
103
|
}: ResponsiveNavProps) {
|
|
83
104
|
const { t } = useTranslations();
|
|
105
|
+
const pathname = usePathname() || '/';
|
|
84
106
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
85
107
|
const [expandedMobileItems, setExpandedMobileItems] = useState<Record<string, boolean>>({});
|
|
86
108
|
|
|
87
109
|
const menuButtonRef = useRef<HTMLButtonElement>(null);
|
|
88
110
|
const menuContainerRef = useRef<HTMLDivElement>(null);
|
|
89
111
|
|
|
90
|
-
const
|
|
112
|
+
const activeNavItems = useMemo(() => {
|
|
113
|
+
if (isEcommerceActive) return navItems;
|
|
114
|
+
return navItems.filter(item => {
|
|
115
|
+
const href = item.url.toLowerCase();
|
|
116
|
+
if (href.startsWith('/product') || href.startsWith('/checkout') || href.startsWith('/shop') || href.startsWith('/cart')) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
});
|
|
121
|
+
}, [navItems, isEcommerceActive]);
|
|
122
|
+
|
|
123
|
+
const hierarchicalNavItems = useMemo(() => buildHierarchy(activeNavItems), [activeNavItems]);
|
|
91
124
|
const { currentContent } = useCurrentContent();
|
|
92
125
|
|
|
93
126
|
let editPathDetails: { href: string; label: string } | null = null;
|
|
127
|
+
let draftModeDetails: { href: string; label: string; ariaLabel: string } | null = null;
|
|
94
128
|
|
|
95
129
|
if (canAccessCms && currentContent.id && currentContent.type) {
|
|
96
130
|
if (currentContent.type === 'page') {
|
|
@@ -103,8 +137,32 @@ export default function ResponsiveNav({
|
|
|
103
137
|
href: `/cms/posts/${currentContent.id}/edit`,
|
|
104
138
|
label: t('edit_post'),
|
|
105
139
|
};
|
|
140
|
+
} else if (currentContent.type === 'product') {
|
|
141
|
+
editPathDetails = {
|
|
142
|
+
href: `/cms/products/${currentContent.id}/edit`,
|
|
143
|
+
label: t('edit_product'),
|
|
144
|
+
};
|
|
106
145
|
}
|
|
107
146
|
}
|
|
147
|
+
|
|
148
|
+
const canToggleDraftForCurrentContent =
|
|
149
|
+
canAccessCms &&
|
|
150
|
+
Boolean(currentContent.id) &&
|
|
151
|
+
(currentContent.type === 'page' ||
|
|
152
|
+
currentContent.type === 'post' ||
|
|
153
|
+
currentContent.type === 'product') &&
|
|
154
|
+
!pathname.startsWith('/cms');
|
|
155
|
+
|
|
156
|
+
if (canToggleDraftForCurrentContent) {
|
|
157
|
+
const draftRoute = isDraftModeEnabled ? '/api/draft/disable' : '/api/draft/start';
|
|
158
|
+
draftModeDetails = {
|
|
159
|
+
href: `${draftRoute}?path=${encodeURIComponent(pathname)}`,
|
|
160
|
+
label: isDraftModeEnabled ? 'Exit Draft' : 'Draft',
|
|
161
|
+
ariaLabel: isDraftModeEnabled
|
|
162
|
+
? 'Exit draft mode'
|
|
163
|
+
: `Enable draft mode for this ${currentContent.type}`,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
108
166
|
// The old path-based logic for determining editPathDetails is removed
|
|
109
167
|
// as the context is now the source of truth for ID and type.
|
|
110
168
|
// The link will only show if canAccessCms is true and context provides valid id and type.
|
|
@@ -259,17 +317,13 @@ export default function ResponsiveNav({
|
|
|
259
317
|
href={homeLinkHref}
|
|
260
318
|
className="flex items-center space-x-2 rtl:space-x-reverse"
|
|
261
319
|
>
|
|
262
|
-
{logo && logo.media ? (
|
|
263
|
-
<Image
|
|
264
|
-
src={
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
alt={logo.media.alt_text || siteTitle || 'Nextblock'}
|
|
270
|
-
width={logo.media.width || 100}
|
|
271
|
-
height={logo.media.height || 32}
|
|
272
|
-
className="h-14 w-auto object-contain"
|
|
320
|
+
{logo && logo.media ? (
|
|
321
|
+
<Image
|
|
322
|
+
src={resolveMediaUrl(logo.media.object_key) || FALLBACK_LOGO_PATH}
|
|
323
|
+
alt={logo.media.alt_text || siteTitle || 'Nextblock'}
|
|
324
|
+
width={logo.media.width || 100}
|
|
325
|
+
height={logo.media.height || 32}
|
|
326
|
+
className="h-14 w-auto object-contain" style={{ width: 'auto', height: '56px' }}
|
|
273
327
|
priority
|
|
274
328
|
/>
|
|
275
329
|
) : (
|
|
@@ -278,7 +332,7 @@ export default function ResponsiveNav({
|
|
|
278
332
|
alt={siteTitle || 'Nextblock'}
|
|
279
333
|
width={120}
|
|
280
334
|
height={40}
|
|
281
|
-
className="h-14 w-auto object-contain"
|
|
335
|
+
className="h-14 w-auto object-contain" style={{ width: 'auto', height: '56px' }}
|
|
282
336
|
priority
|
|
283
337
|
/>
|
|
284
338
|
)}
|
|
@@ -290,22 +344,44 @@ export default function ResponsiveNav({
|
|
|
290
344
|
</div>
|
|
291
345
|
|
|
292
346
|
{/* Right side: Auth, LangSwitcher (desktop), Hamburger (mobile) */}
|
|
293
|
-
<div className="hidden
|
|
347
|
+
<div className="hidden min-h-10 items-center gap-4 md:flex">
|
|
348
|
+
{draftModeDetails && (
|
|
349
|
+
<a
|
|
350
|
+
href={draftModeDetails.href}
|
|
351
|
+
aria-label={draftModeDetails.ariaLabel}
|
|
352
|
+
className={`flex items-center rounded-md px-3 py-2 text-sm font-semibold transition-colors ${
|
|
353
|
+
isDraftModeEnabled
|
|
354
|
+
? 'bg-amber-500 text-white hover:bg-amber-600'
|
|
355
|
+
: 'text-foreground hover:bg-gray-100 dark:hover:bg-gray-800'
|
|
356
|
+
}`}
|
|
357
|
+
>
|
|
358
|
+
{isDraftModeEnabled ? (
|
|
359
|
+
<EyeOff className="mr-2 h-4 w-4" />
|
|
360
|
+
) : (
|
|
361
|
+
<FilePenLine className="mr-2 h-4 w-4" />
|
|
362
|
+
)}
|
|
363
|
+
{draftModeDetails.label}
|
|
364
|
+
</a>
|
|
365
|
+
)}
|
|
294
366
|
{canAccessCms && editPathDetails && (
|
|
295
|
-
<Link href={editPathDetails.href} className="hover:underline font-semibold text-sm text-foreground mr-3">
|
|
367
|
+
<Link href={editPathDetails.href} className="hover:underline font-semibold text-sm text-foreground mr-3 flex items-center">
|
|
368
|
+
<Pencil className="w-4 h-4 mr-2" />
|
|
296
369
|
{editPathDetails.label}
|
|
297
370
|
</Link>
|
|
298
371
|
)}
|
|
299
|
-
|
|
300
|
-
<
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
372
|
+
<ClientOnly>
|
|
373
|
+
<DeferredGlobalSearch isEcommerceActive={isEcommerceActive} variant="desktop" />
|
|
374
|
+
{renderHeaderAuth()}
|
|
375
|
+
{renderLanguageSwitcher()}
|
|
376
|
+
{renderCurrencySwitcher?.()}
|
|
377
|
+
{renderCartIcon?.()}
|
|
378
|
+
</ClientOnly>
|
|
306
379
|
</div>
|
|
307
380
|
|
|
308
|
-
<div className="md:hidden flex items-center z-[60]">
|
|
381
|
+
<div className="md:hidden flex items-center gap-2 z-[60]">
|
|
382
|
+
<ClientOnly>
|
|
383
|
+
<DeferredGlobalSearch isEcommerceActive={isEcommerceActive} variant="mobile" />
|
|
384
|
+
</ClientOnly>
|
|
309
385
|
<button
|
|
310
386
|
ref={menuButtonRef}
|
|
311
387
|
onClick={toggleMobileMenu}
|
|
@@ -354,31 +430,50 @@ export default function ResponsiveNav({
|
|
|
354
430
|
|
|
355
431
|
{canAccessCms && (
|
|
356
432
|
<div className="mt-auto space-y-1 border-t border-gray-200 dark:border-gray-800">
|
|
433
|
+
{draftModeDetails && (
|
|
434
|
+
<a
|
|
435
|
+
href={draftModeDetails.href}
|
|
436
|
+
aria-label={draftModeDetails.ariaLabel}
|
|
437
|
+
className={`flex items-center rounded-md px-3 py-2 text-base font-medium ${
|
|
438
|
+
isDraftModeEnabled
|
|
439
|
+
? 'bg-amber-500 text-white hover:bg-amber-600'
|
|
440
|
+
: 'text-foreground hover:bg-gray-100 dark:hover:bg-gray-700'
|
|
441
|
+
}`}
|
|
442
|
+
onClick={() => {
|
|
443
|
+
toggleMobileMenu();
|
|
444
|
+
}}
|
|
445
|
+
>
|
|
446
|
+
{isDraftModeEnabled ? (
|
|
447
|
+
<EyeOff className="mr-2 h-4 w-4" />
|
|
448
|
+
) : (
|
|
449
|
+
<FilePenLine className="mr-2 h-4 w-4" />
|
|
450
|
+
)}
|
|
451
|
+
{draftModeDetails.label}
|
|
452
|
+
</a>
|
|
453
|
+
)}
|
|
357
454
|
{editPathDetails && (
|
|
358
455
|
<Link
|
|
359
456
|
href={editPathDetails.href}
|
|
360
|
-
className="
|
|
457
|
+
className="flex items-center px-3 py-2 rounded-md text-base font-medium text-foreground hover:bg-gray-100 dark:hover:bg-gray-700"
|
|
361
458
|
onClick={() => {
|
|
362
459
|
toggleMobileMenu();
|
|
363
460
|
}}
|
|
364
461
|
>
|
|
462
|
+
<Pencil className="w-4 h-4 mr-2" />
|
|
365
463
|
{editPathDetails.label}
|
|
366
464
|
</Link>
|
|
367
465
|
)}
|
|
368
|
-
<Link
|
|
369
|
-
href={cmsDashboardLinkHref}
|
|
370
|
-
className="block px-3 py-2 rounded-md text-base font-medium text-foreground hover:bg-gray-100 dark:hover:bg-gray-700"
|
|
371
|
-
onClick={toggleMobileMenu}
|
|
372
|
-
>
|
|
373
|
-
{t('cms_dashboard')}
|
|
374
|
-
</Link>
|
|
375
466
|
</div>
|
|
376
467
|
)}
|
|
377
468
|
</nav>
|
|
378
469
|
|
|
379
470
|
<div className="mt-auto pt-6 border-t border-foreground/20 space-y-4">
|
|
380
|
-
<
|
|
381
|
-
|
|
471
|
+
<ClientOnly>
|
|
472
|
+
<div>{renderHeaderAuth()}</div>
|
|
473
|
+
<div>{renderCartIcon?.()}</div>
|
|
474
|
+
<div>{renderCurrencySwitcher?.()}</div>
|
|
475
|
+
<div>{renderLanguageSwitcher()}</div>
|
|
476
|
+
</ClientOnly>
|
|
382
477
|
</div>
|
|
383
478
|
</div>
|
|
384
479
|
</div>
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import { Skeleton } from
|
|
1
|
+
import { Skeleton } from '@nextblock-cms/ui';
|
|
2
2
|
|
|
3
3
|
const PostCardSkeleton = () => {
|
|
4
4
|
return (
|
|
5
|
-
<div className="border rounded-lg overflow-hidden shadow-sm bg-card text-card-foreground">
|
|
6
|
-
<Skeleton className="h-48 w-full" />
|
|
7
|
-
<div className="p-4 space-y-3">
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
<div className="border rounded-lg overflow-hidden shadow-sm bg-card text-card-foreground">
|
|
6
|
+
<Skeleton className="h-48 w-full" />
|
|
7
|
+
<div className="p-4 space-y-3">
|
|
8
|
+
<div className="flex items-center gap-2">
|
|
9
|
+
<Skeleton className="h-6 w-20 rounded-full" />
|
|
10
|
+
<Skeleton className="h-4 w-16" />
|
|
11
|
+
</div>
|
|
12
|
+
<Skeleton className="h-5 w-3/4" />
|
|
13
|
+
<Skeleton className="h-4 w-full" />
|
|
14
|
+
<Skeleton className="h-4 w-5/6" />
|
|
11
15
|
<Skeleton className="h-4 w-1/4 mt-2" />
|
|
12
16
|
</div>
|
|
13
17
|
</div>
|
|
14
18
|
);
|
|
15
19
|
};
|
|
16
20
|
|
|
17
|
-
export default PostCardSkeleton;
|
|
21
|
+
export default PostCardSkeleton;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
// components/blocks/PostsGridBlock.tsx
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import type { Database } from '@nextblock-cms/db';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import PostsGridClient from './PostsGridClient';
|
|
10
|
-
import { fetchPaginatedPublishedPosts } from '../../app/actions/postActions'; // fetchInitialPublishedPosts removed
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { Database } from '@nextblock-cms/db';
|
|
4
|
+
|
|
5
|
+
type Block = Database['public']['Tables']['blocks']['Row'];
|
|
6
|
+
// import Link from 'next/link'; // Unused, PostsGridClient handles links
|
|
7
|
+
import PostsGridClient from './PostsGridClient';
|
|
8
|
+
import { fetchInitialPublishedPosts, fetchPaginatedPublishedPosts } from '../../app/actions/postActions';
|
|
11
9
|
|
|
12
10
|
interface PostsGridBlockProps {
|
|
13
11
|
block: Block;
|
|
@@ -22,52 +20,11 @@ const PostsGridBlock: React.FC<PostsGridBlockProps> = async ({ block, languageId
|
|
|
22
20
|
showPagination = true,
|
|
23
21
|
} = block.content as { title?: string, postsPerPage?: number, columns?: number, showPagination?: boolean };
|
|
24
22
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
.eq('status', 'published')
|
|
31
|
-
.eq('language_id', languageId)
|
|
32
|
-
.order('published_at', { ascending: false })
|
|
33
|
-
.limit(postsPerPage);
|
|
34
|
-
|
|
35
|
-
let initialPosts: PostWithMediaDimensions[] = [];
|
|
36
|
-
let totalCount = 0;
|
|
37
|
-
let postsError: string | null = null;
|
|
38
|
-
|
|
39
|
-
if (queryError) {
|
|
40
|
-
console.error("Error fetching initial posts directly in PostsGridBlock:", queryError);
|
|
41
|
-
postsError = queryError.message;
|
|
42
|
-
} else {
|
|
43
|
-
const buildMediaUrl = (objectKey?: string | null) => {
|
|
44
|
-
if (!objectKey) return null;
|
|
45
|
-
if (objectKey.startsWith('/')) return objectKey;
|
|
46
|
-
const base = process.env.NEXT_PUBLIC_R2_BASE_URL || '';
|
|
47
|
-
return base ? `${base}/${objectKey}` : objectKey;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
initialPosts = (postsData as any)?.map((p: any) => {
|
|
51
|
-
// feature_media_object is an object here, not an array, due to the query structure media!feature_image_id(object_key, width, height)
|
|
52
|
-
// Cast to 'unknown' then to the expected single object type to satisfy TypeScript, reflecting runtime reality.
|
|
53
|
-
const mediaObject = p.feature_media_object as unknown as { object_key: string; width?: number | null; height?: number | null; blur_data_url?: string | null } | null;
|
|
54
|
-
const imageUrl = buildMediaUrl(mediaObject?.object_key);
|
|
55
|
-
return {
|
|
56
|
-
...p,
|
|
57
|
-
// Convert feature_media_object to array format to match the type
|
|
58
|
-
feature_media_object: mediaObject ? [{ object_key: mediaObject.object_key }] : null,
|
|
59
|
-
feature_image_url: imageUrl,
|
|
60
|
-
feature_image_width: mediaObject?.width || null,
|
|
61
|
-
feature_image_height: mediaObject?.height || null,
|
|
62
|
-
blur_data_url: mediaObject?.blur_data_url || null,
|
|
63
|
-
};
|
|
64
|
-
}) as PostWithMediaDimensions[] || [];
|
|
65
|
-
totalCount = count || 0;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (postsError) {
|
|
69
|
-
return <div className="text-red-500">Error loading posts: {postsError}</div>;
|
|
70
|
-
}
|
|
23
|
+
const { posts: initialPosts, totalCount, error: postsError } = await fetchInitialPublishedPosts(languageId, postsPerPage);
|
|
24
|
+
|
|
25
|
+
if (postsError) {
|
|
26
|
+
return <div className="text-red-500">Error loading posts: {postsError}</div>;
|
|
27
|
+
}
|
|
71
28
|
|
|
72
29
|
if (!initialPosts || initialPosts.length === 0) {
|
|
73
30
|
return (
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
// components/blocks/PostsGridClient.tsx
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
-
import React, { useState, useEffect } from 'react';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
type PostWithMediaDimensions = Database['public']['Tables']['posts']['Row'] & {
|
|
9
|
-
feature_image_url: string | null;
|
|
10
|
-
feature_image_width: number | null;
|
|
11
|
-
feature_image_height: number | null;
|
|
12
|
-
blur_data_url: string | null;
|
|
13
|
-
};
|
|
4
|
+
import React, { useState, useEffect } from 'react';
|
|
5
|
+
import Link from 'next/link';
|
|
6
|
+
import { useLanguage } from '../../context/LanguageContext';
|
|
7
|
+
import type { PostWithMediaDimensions } from './types';
|
|
14
8
|
import Image from 'next/image';
|
|
15
|
-
import { Button } from '@nextblock-cms/ui';
|
|
9
|
+
import { Button } from '@nextblock-cms/ui';
|
|
16
10
|
import PostCardSkeleton from './PostCardSkeleton'; // Added import
|
|
17
11
|
|
|
18
12
|
interface PostsGridClientProps {
|
|
@@ -26,10 +20,10 @@ interface PostsGridClientProps {
|
|
|
26
20
|
fetchAction: (languageId: number, page: number, limit: number) => Promise<{ posts: PostWithMediaDimensions[], totalCount: number, error?: string }>;
|
|
27
21
|
}
|
|
28
22
|
|
|
29
|
-
const DEFAULT_FEATURE_IMAGE_WIDTH = 1600;
|
|
30
|
-
const DEFAULT_FEATURE_IMAGE_HEIGHT = 900;
|
|
31
|
-
|
|
32
|
-
const PostsGridClient: React.FC<PostsGridClientProps> = ({
|
|
23
|
+
const DEFAULT_FEATURE_IMAGE_WIDTH = 1600;
|
|
24
|
+
const DEFAULT_FEATURE_IMAGE_HEIGHT = 900;
|
|
25
|
+
|
|
26
|
+
const PostsGridClient: React.FC<PostsGridClientProps> = ({
|
|
33
27
|
initialPosts,
|
|
34
28
|
initialPage,
|
|
35
29
|
postsPerPage,
|
|
@@ -39,6 +33,7 @@ const PostsGridClient: React.FC<PostsGridClientProps> = ({
|
|
|
39
33
|
showPagination,
|
|
40
34
|
fetchAction,
|
|
41
35
|
}) => {
|
|
36
|
+
const { currentLocale } = useLanguage();
|
|
42
37
|
const [currentPage, setCurrentPage] = useState(initialPage);
|
|
43
38
|
const [posts, setPosts] = useState<PostWithMediaDimensions[]>(initialPosts);
|
|
44
39
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -117,30 +112,40 @@ const PostsGridClient: React.FC<PostsGridClientProps> = ({
|
|
|
117
112
|
<PostCardSkeleton key={`skeleton-${index}`} />
|
|
118
113
|
))
|
|
119
114
|
) : posts.length > 0 ? (
|
|
120
|
-
posts.map((post
|
|
121
|
-
<Link href={`/article/${post.slug}`} key={post.id} className="block group">
|
|
122
|
-
<div className="border rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow bg-card text-card-foreground">
|
|
115
|
+
posts.map((post) => (
|
|
116
|
+
<Link href={`/article/${post.slug}`} key={post.id} className="block group h-full">
|
|
117
|
+
<div className="border rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow bg-card text-card-foreground h-full flex flex-col">
|
|
123
118
|
{/* Basic Post Card Structure - Enhanced with Feature Image */}
|
|
124
|
-
{post.feature_image_url ? (
|
|
125
|
-
<div className="aspect-video overflow-hidden">
|
|
126
|
-
<Image
|
|
127
|
-
src={post.feature_image_url}
|
|
128
|
-
alt={`Feature image for ${post.title}`}
|
|
129
|
-
width={post.feature_image_width && post.feature_image_width > 0 ? post.feature_image_width : DEFAULT_FEATURE_IMAGE_WIDTH}
|
|
130
|
-
height={post.feature_image_height && post.feature_image_height > 0 ? post.feature_image_height : DEFAULT_FEATURE_IMAGE_HEIGHT}
|
|
131
|
-
sizes={imageSizes}
|
|
132
|
-
|
|
133
|
-
placeholder={post.blur_data_url ? 'blur' : 'empty'}
|
|
134
|
-
blurDataURL={post.blur_data_url ?? undefined}
|
|
135
|
-
quality={60}
|
|
136
|
-
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
137
|
-
/>
|
|
138
|
-
</div>
|
|
139
|
-
) : null}
|
|
140
|
-
<div className="p-4">
|
|
119
|
+
{post.feature_image_url ? (
|
|
120
|
+
<div className="aspect-video overflow-hidden">
|
|
121
|
+
<Image
|
|
122
|
+
src={post.feature_image_url}
|
|
123
|
+
alt={`Feature image for ${post.title}`}
|
|
124
|
+
width={post.feature_image_width && post.feature_image_width > 0 ? post.feature_image_width : DEFAULT_FEATURE_IMAGE_WIDTH}
|
|
125
|
+
height={post.feature_image_height && post.feature_image_height > 0 ? post.feature_image_height : DEFAULT_FEATURE_IMAGE_HEIGHT}
|
|
126
|
+
sizes={imageSizes}
|
|
127
|
+
loading="lazy"
|
|
128
|
+
placeholder={post.blur_data_url ? 'blur' : 'empty'}
|
|
129
|
+
blurDataURL={post.blur_data_url ?? undefined}
|
|
130
|
+
quality={60}
|
|
131
|
+
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
) : null}
|
|
135
|
+
<div className="p-4 flex flex-1 flex-col">
|
|
136
|
+
<div className="mb-3 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
|
137
|
+
<span className="inline-flex items-center rounded-full border border-slate-200 bg-slate-50 px-2.5 py-1 font-semibold text-slate-700">
|
|
138
|
+
{post.label?.trim() || 'Article'}
|
|
139
|
+
</span>
|
|
140
|
+
<span>
|
|
141
|
+
{post.estimated_read_time_minutes} {currentLocale === 'fr' ? 'min de lecture' : 'min read'}
|
|
142
|
+
</span>
|
|
143
|
+
</div>
|
|
141
144
|
<h3 className="text-lg font-semibold mb-2 group-hover:text-primary">{post.title}</h3>
|
|
142
145
|
{post.excerpt && <p className="text-sm text-muted-foreground mb-3 line-clamp-3">{post.excerpt}</p>}
|
|
143
|
-
<
|
|
146
|
+
<div className="mt-auto pt-2">
|
|
147
|
+
<span className="text-xs text-primary group-hover:underline">Read more</span>
|
|
148
|
+
</div>
|
|
144
149
|
</div>
|
|
145
150
|
</div>
|
|
146
151
|
</Link>
|
|
@@ -176,4 +181,4 @@ const PostsGridClient: React.FC<PostsGridClientProps> = ({
|
|
|
176
181
|
);
|
|
177
182
|
};
|
|
178
183
|
|
|
179
|
-
export default PostsGridClient;
|
|
184
|
+
export default PostsGridClient;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { z } from 'zod';
|
|
2
|
+
import { z } from '../../lib/zod-config';
|
|
3
3
|
import { BlockConfig, BlockProps, BlockEditorProps } from '@nextblock-cms/sdk';
|
|
4
|
-
import {
|
|
4
|
+
import { Avatar, AvatarFallback, AvatarImage } from '@nextblock-cms/ui';
|
|
5
|
+
import { Card, CardContent } from '@nextblock-cms/ui';
|
|
6
|
+
import { Input } from '@nextblock-cms/ui';
|
|
7
|
+
import { Label } from '@nextblock-cms/ui';
|
|
8
|
+
import { Textarea } from '@nextblock-cms/ui';
|
|
5
9
|
import { MessageSquareQuote } from 'lucide-react';
|
|
6
10
|
|
|
7
11
|
// 1. Define the Schema
|