create-nextblock 0.2.77 → 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 +191 -151
- 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 -116
- 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,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// app/cms/blocks/components/BackgroundSelector.tsx
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
4
|
import React, { useState, useEffect } from "react";
|
|
@@ -6,16 +6,14 @@ import Image from "next/image";
|
|
|
6
6
|
import { Label, Select, SelectTrigger, SelectContent, SelectItem, SelectValue, Button, Input, Checkbox } from "@nextblock-cms/ui";
|
|
7
7
|
import { CustomSelectWithInput, ColorPicker } from "@nextblock-cms/ui";
|
|
8
8
|
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
|
9
|
-
import {
|
|
10
|
-
import { cn } from "@nextblock-cms/utils";
|
|
9
|
+
import { Trash } from "lucide-react";
|
|
11
10
|
import type { Database } from "@nextblock-cms/db";
|
|
12
|
-
import
|
|
13
|
-
import MediaPickerDialog from "
|
|
11
|
+
import { SectionBlockContent } from '../../../../lib/blocks/blockRegistry';
|
|
12
|
+
import MediaPickerDialog from "../../media/components/MediaPickerDialog";
|
|
13
|
+
import { resolveMediaUrl } from "../../../../lib/media/resolveMediaUrl";
|
|
14
14
|
|
|
15
15
|
type Media = Database["public"]["Tables"]["media"]["Row"];
|
|
16
16
|
|
|
17
|
-
const R2_BASE_URL = process.env.NEXT_PUBLIC_R2_BASE_URL || "";
|
|
18
|
-
|
|
19
17
|
interface BackgroundSelectorProps {
|
|
20
18
|
background: SectionBlockContent["background"];
|
|
21
19
|
onChange: (newBackground: SectionBlockContent["background"]) => void;
|
|
@@ -127,11 +125,6 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
|
|
|
127
125
|
}
|
|
128
126
|
};
|
|
129
127
|
|
|
130
|
-
const handleBackgroundPropertyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
131
|
-
const { name, value } = e.target;
|
|
132
|
-
onChange({ ...background, [name]: value });
|
|
133
|
-
};
|
|
134
|
-
|
|
135
128
|
const handleOverlayGradientChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
136
129
|
const { name, value } = e.target;
|
|
137
130
|
if (background?.type === "image" && background.image) {
|
|
@@ -161,17 +154,36 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
|
|
|
161
154
|
}
|
|
162
155
|
};
|
|
163
156
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
157
|
+
const handleBackgroundGradientChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
158
|
+
const { name, value } = e.target as any;
|
|
159
|
+
if (backgroundType !== 'gradient') return;
|
|
160
|
+
const current = background.gradient || { type: 'linear' as const, direction: 'to right', stops: [ { color: '#3b82f6', position: 0 }, { color: '#8b5cf6', position: 100 } ] };
|
|
161
|
+
if (name === 'direction') {
|
|
162
|
+
onChange({ type: 'gradient', gradient: { ...current, direction: value } });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (name === 'startColor' || name === 'endColor') {
|
|
166
|
+
const updatedStops = (current.stops || [ { color: '#3b82f6', position: 0 }, { color: '#8b5cf6', position: 100 } ]).map((s) => {
|
|
167
|
+
if (name === 'startColor' && s.position === 0) return { ...s, color: value };
|
|
168
|
+
if (name === 'endColor' && s.position === 100) return { ...s, color: value };
|
|
169
|
+
return s;
|
|
170
|
+
});
|
|
171
|
+
onChange({ type: 'gradient', gradient: { ...current, stops: updatedStops } });
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const selectedImageUrl = resolveMediaUrl(selectedImage?.object_key);
|
|
167
176
|
|
|
168
177
|
return (
|
|
169
178
|
<TooltipProvider>
|
|
170
|
-
<div className="
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
<div className="flex flex-wrap items-end gap-3.5 pt-1">
|
|
180
|
+
{/* Background Type */}
|
|
181
|
+
<div className="space-y-1.5 w-[140px]">
|
|
182
|
+
<Label className="text-xs uppercase font-bold text-muted-foreground tracking-wider leading-none">Background Type</Label>
|
|
173
183
|
<Select value={backgroundType} onValueChange={(v) => handleTypeChange(v as any)}>
|
|
174
|
-
<SelectTrigger className="
|
|
184
|
+
<SelectTrigger className="h-9 text-sm">
|
|
185
|
+
<SelectValue placeholder="Select type" />
|
|
186
|
+
</SelectTrigger>
|
|
175
187
|
<SelectContent>
|
|
176
188
|
<SelectItem value="none">None</SelectItem>
|
|
177
189
|
<SelectItem value="gradient">Gradient</SelectItem>
|
|
@@ -180,169 +192,218 @@ export default function BackgroundSelector({ background, onChange }: BackgroundS
|
|
|
180
192
|
</Select>
|
|
181
193
|
</div>
|
|
182
194
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
195
|
+
{/* Minimum Height */}
|
|
196
|
+
<div className="space-y-1.5 w-[110px]">
|
|
197
|
+
<Label htmlFor="min_height" className="text-xs uppercase font-bold text-muted-foreground tracking-wider leading-none">Min Height</Label>
|
|
198
|
+
<Input
|
|
199
|
+
id="min_height"
|
|
200
|
+
name="min_height"
|
|
201
|
+
value={minHeight}
|
|
202
|
+
onChange={(e) => setMinHeight(e.target.value)}
|
|
203
|
+
onBlur={() => {
|
|
204
|
+
onChange({ ...background, min_height: minHeight });
|
|
205
|
+
}}
|
|
206
|
+
onKeyDown={(e) => {
|
|
207
|
+
if (e.key === "Enter") {
|
|
208
|
+
e.currentTarget.blur();
|
|
209
|
+
}
|
|
210
|
+
}}
|
|
211
|
+
placeholder="e.g., 250px"
|
|
212
|
+
className="h-9 text-sm"
|
|
213
|
+
/>
|
|
191
214
|
</div>
|
|
192
215
|
|
|
193
|
-
{
|
|
216
|
+
{/* Gradient Background configuration (rendered inline) */}
|
|
217
|
+
{backgroundType === "gradient" && (
|
|
194
218
|
<>
|
|
195
|
-
<div className="
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
219
|
+
<div className="w-[200px]">
|
|
220
|
+
<CustomSelectWithInput
|
|
221
|
+
label="Gradient Direction"
|
|
222
|
+
tooltipContent="Select a preset or enter a custom angle like '45deg' or 'to top left'."
|
|
223
|
+
value={background.gradient?.direction || "to right"}
|
|
224
|
+
onChange={(value: string) => handleBackgroundGradientChange({ target: { name: "direction", value } } as any)}
|
|
225
|
+
options={[
|
|
226
|
+
{ value: "to right", label: "To Right" },
|
|
227
|
+
{ value: "to left", label: "To Left" },
|
|
228
|
+
{ value: "to top", label: "To Top" },
|
|
229
|
+
{ value: "to bottom", label: "To Bottom" },
|
|
230
|
+
{ value: "to bottom right", label: "To Bottom Right" },
|
|
231
|
+
{ value: "to top left", label: "To Top Left" },
|
|
232
|
+
]}
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
235
|
+
<div className="w-[150px]">
|
|
236
|
+
<ColorPicker
|
|
237
|
+
label="Start Color"
|
|
238
|
+
color={background.gradient?.stops?.[0]?.color || "#3b82f6"}
|
|
239
|
+
onChange={(color) => handleBackgroundGradientChange({ target: { name: "startColor", value: color } } as any)}
|
|
240
|
+
/>
|
|
241
|
+
</div>
|
|
242
|
+
<div className="w-[150px]">
|
|
243
|
+
<ColorPicker
|
|
244
|
+
label="End Color"
|
|
245
|
+
color={background.gradient?.stops?.[1]?.color || "#8b5cf6"}
|
|
246
|
+
onChange={(color) => handleBackgroundGradientChange({ target: { name: "endColor", value: color } } as any)}
|
|
247
|
+
/>
|
|
248
|
+
</div>
|
|
249
|
+
</>
|
|
250
|
+
)}
|
|
251
|
+
|
|
252
|
+
{/* Image Size (only if image type) */}
|
|
253
|
+
{backgroundType === "image" && (
|
|
254
|
+
<div className="space-y-1.5 w-[110px]">
|
|
255
|
+
<Label className="text-xs uppercase font-bold text-muted-foreground tracking-wider leading-none">Image Size</Label>
|
|
256
|
+
<Select value={selectedImage?.size || "cover"} onValueChange={(v) => handleImagePropertyChange("size", v)}>
|
|
257
|
+
<SelectTrigger className="h-9 text-sm">
|
|
258
|
+
<SelectValue />
|
|
259
|
+
</SelectTrigger>
|
|
260
|
+
<SelectContent>
|
|
261
|
+
<SelectItem value="cover">Cover</SelectItem>
|
|
262
|
+
<SelectItem value="contain">Contain</SelectItem>
|
|
263
|
+
</SelectContent>
|
|
264
|
+
</Select>
|
|
265
|
+
</div>
|
|
266
|
+
)}
|
|
267
|
+
|
|
268
|
+
{/* Image Position (only if image type) */}
|
|
269
|
+
{backgroundType === "image" && (
|
|
270
|
+
<div className="space-y-1.5 w-[130px]">
|
|
271
|
+
<Label className="text-xs uppercase font-bold text-muted-foreground tracking-wider leading-none">Image Position</Label>
|
|
272
|
+
<Select value={imagePosition} onValueChange={(v) => { setImagePosition(v); handleImagePropertyChange("position", v); }}>
|
|
273
|
+
<SelectTrigger className="h-9 text-sm">
|
|
274
|
+
<SelectValue />
|
|
275
|
+
</SelectTrigger>
|
|
276
|
+
<SelectContent>
|
|
277
|
+
<SelectItem value="center">Center</SelectItem>
|
|
278
|
+
<SelectItem value="top">Top</SelectItem>
|
|
279
|
+
<SelectItem value="bottom">Bottom</SelectItem>
|
|
280
|
+
<SelectItem value="left">Left</SelectItem>
|
|
281
|
+
<SelectItem value="right">Right</SelectItem>
|
|
282
|
+
<SelectItem value="top left">Top Left</SelectItem>
|
|
283
|
+
<SelectItem value="top right">Top Right</SelectItem>
|
|
284
|
+
<SelectItem value="bottom left">Bottom Left</SelectItem>
|
|
285
|
+
<SelectItem value="bottom right">Bottom Right</SelectItem>
|
|
286
|
+
</SelectContent>
|
|
287
|
+
</Select>
|
|
288
|
+
</div>
|
|
289
|
+
)}
|
|
290
|
+
|
|
291
|
+
{/* Image Picker Trigger / Thumbnail */}
|
|
292
|
+
{backgroundType === "image" && (
|
|
293
|
+
<div className="space-y-1.5">
|
|
294
|
+
<Label className="text-xs uppercase font-bold text-muted-foreground tracking-wider leading-none">Image</Label>
|
|
295
|
+
{selectedImage?.object_key ? (
|
|
296
|
+
<div className="flex items-center gap-2 h-9">
|
|
297
|
+
<div className="relative w-9 h-9 rounded border bg-muted overflow-hidden flex-shrink-0 shadow-sm">
|
|
298
|
+
{selectedImageUrl ? (
|
|
299
|
+
<Image
|
|
300
|
+
src={selectedImageUrl}
|
|
301
|
+
alt="Thumbnail"
|
|
302
|
+
fill
|
|
303
|
+
sizes="36px"
|
|
304
|
+
className="object-cover"
|
|
305
|
+
style={{ objectPosition: selectedImage.position }}
|
|
306
|
+
/>
|
|
307
|
+
) : null}
|
|
199
308
|
{selectedImage.overlay && (
|
|
200
309
|
<div className="absolute inset-0" style={{ background: generateGradientCss(selectedImage.overlay.gradient) }} />
|
|
201
310
|
)}
|
|
202
|
-
|
|
203
|
-
|
|
311
|
+
</div>
|
|
312
|
+
<div className="flex items-center gap-1.5">
|
|
313
|
+
<MediaPickerDialog
|
|
314
|
+
triggerLabel="Change"
|
|
315
|
+
triggerVariant="outline"
|
|
316
|
+
onSelect={handleSelectMediaFromLibrary}
|
|
317
|
+
accept={(m) => !!m.file_type?.startsWith("image/")}
|
|
318
|
+
title="Select Background Image"
|
|
319
|
+
>
|
|
320
|
+
<Button type="button" variant="outline" size="sm" className="h-9 px-2.5 text-xs">
|
|
321
|
+
Change
|
|
322
|
+
</Button>
|
|
323
|
+
</MediaPickerDialog>
|
|
324
|
+
<Button
|
|
325
|
+
type="button"
|
|
326
|
+
variant="outline"
|
|
327
|
+
size="icon"
|
|
328
|
+
className="h-9 w-9 text-destructive border-destructive/20 hover:bg-destructive/10 hover:text-destructive flex-shrink-0"
|
|
329
|
+
onClick={handleRemoveImage}
|
|
330
|
+
title="Remove Image"
|
|
331
|
+
>
|
|
332
|
+
<Trash className="h-4 w-4" />
|
|
204
333
|
</Button>
|
|
205
334
|
</div>
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
<div className="mt-3">
|
|
335
|
+
</div>
|
|
336
|
+
) : (
|
|
337
|
+
<div className="h-9 flex items-center">
|
|
211
338
|
<MediaPickerDialog
|
|
212
|
-
triggerLabel=
|
|
339
|
+
triggerLabel="Select Image"
|
|
340
|
+
triggerVariant="outline"
|
|
213
341
|
onSelect={handleSelectMediaFromLibrary}
|
|
214
342
|
accept={(m) => !!m.file_type?.startsWith("image/")}
|
|
215
|
-
title="Select
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
<div className="grid gap-2">
|
|
221
|
-
<Label>Image Size</Label>
|
|
222
|
-
<Select value={selectedImage?.size || "cover"} onValueChange={(v) => handleImagePropertyChange("size", v)}>
|
|
223
|
-
<SelectTrigger className="w-full max-w-[250px]"><SelectValue /></SelectTrigger>
|
|
224
|
-
<SelectContent>
|
|
225
|
-
<SelectItem value="cover">Cover</SelectItem>
|
|
226
|
-
<SelectItem value="contain">Contain</SelectItem>
|
|
227
|
-
</SelectContent>
|
|
228
|
-
</Select>
|
|
229
|
-
</div>
|
|
230
|
-
|
|
231
|
-
<div className="grid gap-2">
|
|
232
|
-
<Label>Image Position</Label>
|
|
233
|
-
<Select value={imagePosition} onValueChange={(v) => { setImagePosition(v); handleImagePropertyChange("position", v); }}>
|
|
234
|
-
<SelectTrigger className="w-full max-w-[250px]"><SelectValue /></SelectTrigger>
|
|
235
|
-
<SelectContent>
|
|
236
|
-
<SelectItem value="center">Center</SelectItem>
|
|
237
|
-
<SelectItem value="top">Top</SelectItem>
|
|
238
|
-
<SelectItem value="bottom">Bottom</SelectItem>
|
|
239
|
-
<SelectItem value="left">Left</SelectItem>
|
|
240
|
-
<SelectItem value="right">Right</SelectItem>
|
|
241
|
-
<SelectItem value="top left">Top Left</SelectItem>
|
|
242
|
-
<SelectItem value="top right">Top Right</SelectItem>
|
|
243
|
-
<SelectItem value="bottom left">Bottom Left</SelectItem>
|
|
244
|
-
<SelectItem value="bottom right">Bottom Right</SelectItem>
|
|
245
|
-
</SelectContent>
|
|
246
|
-
</Select>
|
|
247
|
-
</div>
|
|
248
|
-
|
|
249
|
-
<div className="flex items-center space-x-2 mt-2">
|
|
250
|
-
<Checkbox id="gradientOverlay" checked={!!selectedImage?.overlay} onCheckedChange={(c) => handleOverlayToggle(!!c)} />
|
|
251
|
-
<div className="grid gap-1.5 leading-none">
|
|
252
|
-
<label htmlFor="gradientOverlay" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">Add Gradient Overlay</label>
|
|
253
|
-
</div>
|
|
254
|
-
</div>
|
|
255
|
-
|
|
256
|
-
{selectedImage?.overlay && (
|
|
257
|
-
<div className="mt-3 p-3 border rounded-md bg-muted/30 space-y-4">
|
|
258
|
-
<div className="flex items-center gap-2">
|
|
259
|
-
<div className="flex-grow">
|
|
260
|
-
<CustomSelectWithInput
|
|
261
|
-
label="Direction"
|
|
262
|
-
tooltipContent="Select a preset or enter a custom angle like '45deg' or 'to top left'. See MDN's linear-gradient docs for more options."
|
|
263
|
-
value={overlayDirection}
|
|
264
|
-
onChange={setOverlayDirection}
|
|
265
|
-
options={[
|
|
266
|
-
{ value: "to bottom", label: "To Bottom" },
|
|
267
|
-
{ value: "to top", label: "To Top" },
|
|
268
|
-
{ value: "to left", label: "To Left" },
|
|
269
|
-
{ value: "to right", label: "To Right" },
|
|
270
|
-
{ value: "to bottom right", label: "To Bottom Right" },
|
|
271
|
-
{ value: "to top left", label: "To Top Left" },
|
|
272
|
-
]}
|
|
273
|
-
/>
|
|
274
|
-
</div>
|
|
275
|
-
<Button size="icon" variant="ghost" onClick={() => handleOverlayGradientChange({ target: { name: "direction", value: overlayDirection } } as any)} disabled={!hasOverlayDirectionChanged} title="Save Overlay Direction">
|
|
276
|
-
<Save className={cn("h-5 w-5 mt-[1.3rem]", hasOverlayDirectionChanged && "text-green-600")} />
|
|
343
|
+
title="Select Background Image"
|
|
344
|
+
>
|
|
345
|
+
<Button type="button" variant="outline" size="sm" className="h-9 px-3 text-xs">
|
|
346
|
+
Select Image
|
|
277
347
|
</Button>
|
|
278
|
-
</
|
|
279
|
-
<div className="flex items-center gap-4">
|
|
280
|
-
<ColorPicker
|
|
281
|
-
label="Start Color"
|
|
282
|
-
color={selectedImage.overlay.gradient?.stops?.[0]?.color || "rgba(0,0,0,0.5)"}
|
|
283
|
-
onChange={(color) => handleOverlayGradientChange({ target: { name: "startColor", value: color } } as any)}
|
|
284
|
-
/>
|
|
285
|
-
<ColorPicker
|
|
286
|
-
label="End Color"
|
|
287
|
-
color={selectedImage.overlay.gradient?.stops?.[1]?.color || "rgba(0,0,0,0)"}
|
|
288
|
-
onChange={(color) => handleOverlayGradientChange({ target: { name: "endColor", value: color } } as any)}
|
|
289
|
-
/>
|
|
290
|
-
</div>
|
|
348
|
+
</MediaPickerDialog>
|
|
291
349
|
</div>
|
|
292
350
|
)}
|
|
293
|
-
|
|
351
|
+
</div>
|
|
294
352
|
)}
|
|
295
353
|
|
|
296
|
-
{
|
|
297
|
-
|
|
298
|
-
|
|
354
|
+
{/* Overlay Checkbox */}
|
|
355
|
+
{backgroundType === "image" && selectedImage?.object_key && (
|
|
356
|
+
<div className="space-y-1.5">
|
|
357
|
+
<Label className="text-xs uppercase font-bold text-muted-foreground tracking-wider leading-none">Overlay</Label>
|
|
358
|
+
<div className="flex items-center justify-center h-9 border border-input rounded-md px-3 bg-background">
|
|
359
|
+
<Checkbox
|
|
360
|
+
id="gradientOverlay"
|
|
361
|
+
checked={!!selectedImage?.overlay}
|
|
362
|
+
onCheckedChange={(c) => handleOverlayToggle(!!c)}
|
|
363
|
+
/>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
)}
|
|
367
|
+
|
|
368
|
+
{/* Gradient Overlay configuration fields (inline) */}
|
|
369
|
+
{backgroundType === "image" && selectedImage?.overlay && (
|
|
370
|
+
<>
|
|
371
|
+
<div className="w-[200px]">
|
|
299
372
|
<CustomSelectWithInput
|
|
300
|
-
label="Direction"
|
|
301
|
-
tooltipContent="Select a preset or enter a custom angle like '45deg' or 'to top left'.
|
|
302
|
-
value={
|
|
303
|
-
onChange={(
|
|
373
|
+
label="Overlay Direction"
|
|
374
|
+
tooltipContent="Select a preset or enter a custom angle like '45deg' or 'to top left'."
|
|
375
|
+
value={overlayDirection}
|
|
376
|
+
onChange={(val) => {
|
|
377
|
+
setOverlayDirection(val);
|
|
378
|
+
handleOverlayGradientChange({ target: { name: "direction", value: val } } as any);
|
|
379
|
+
}}
|
|
304
380
|
options={[
|
|
305
|
-
{ value: "to right", label: "To Right" },
|
|
306
|
-
{ value: "to left", label: "To Left" },
|
|
307
|
-
{ value: "to top", label: "To Top" },
|
|
308
381
|
{ value: "to bottom", label: "To Bottom" },
|
|
382
|
+
{ value: "to top", label: "To Top" },
|
|
383
|
+
{ value: "to left", label: "To Left" },
|
|
384
|
+
{ value: "to right", label: "To Right" },
|
|
309
385
|
{ value: "to bottom right", label: "To Bottom Right" },
|
|
310
386
|
{ value: "to top left", label: "To Top Left" },
|
|
311
387
|
]}
|
|
312
388
|
/>
|
|
313
389
|
</div>
|
|
314
|
-
<div className="
|
|
390
|
+
<div className="w-[150px]">
|
|
315
391
|
<ColorPicker
|
|
316
|
-
label="Start
|
|
317
|
-
color={
|
|
318
|
-
onChange={(color) =>
|
|
392
|
+
label="Overlay Start"
|
|
393
|
+
color={selectedImage.overlay.gradient?.stops?.[0]?.color || "rgba(0,0,0,0.5)"}
|
|
394
|
+
onChange={(color) => handleOverlayGradientChange({ target: { name: "startColor", value: color } } as any)}
|
|
319
395
|
/>
|
|
396
|
+
</div>
|
|
397
|
+
<div className="w-[150px]">
|
|
320
398
|
<ColorPicker
|
|
321
|
-
label="End
|
|
322
|
-
color={
|
|
323
|
-
onChange={(color) =>
|
|
399
|
+
label="Overlay End"
|
|
400
|
+
color={selectedImage.overlay.gradient?.stops?.[1]?.color || "rgba(0,0,0,0)"}
|
|
401
|
+
onChange={(color) => handleOverlayGradientChange({ target: { name: "endColor", value: color } } as any)}
|
|
324
402
|
/>
|
|
325
403
|
</div>
|
|
326
|
-
|
|
404
|
+
</>
|
|
327
405
|
)}
|
|
328
406
|
</div>
|
|
329
407
|
</TooltipProvider>
|
|
330
408
|
);
|
|
331
|
-
const handleBackgroundGradientChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
332
|
-
const { name, value } = e.target as any;
|
|
333
|
-
if (backgroundType !== 'gradient') return;
|
|
334
|
-
const current = background.gradient || { type: 'linear' as const, direction: 'to right', stops: [ { color: '#3b82f6', position: 0 }, { color: '#8b5cf6', position: 100 } ] };
|
|
335
|
-
if (name === 'direction') {
|
|
336
|
-
onChange({ type: 'gradient', gradient: { ...current, direction: value } });
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
if (name === 'startColor' || name === 'endColor') {
|
|
340
|
-
const updatedStops = (current.stops || [ { color: '#3b82f6', position: 0 }, { color: '#8b5cf6', position: 100 } ]).map((s) => {
|
|
341
|
-
if (name === 'startColor' && s.position === 0) return { ...s, color: value };
|
|
342
|
-
if (name === 'endColor' && s.position === 100) return { ...s, color: value };
|
|
343
|
-
return s;
|
|
344
|
-
});
|
|
345
|
-
onChange({ type: 'gradient', gradient: { ...current, stops: updatedStops } });
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
409
|
}
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
4
|
import React, { useState, useTransition, useEffect, ComponentType, useCallback, useRef } from "react";
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
5
6
|
import dynamic from 'next/dynamic';
|
|
6
7
|
import debounce from 'lodash.debounce';
|
|
7
8
|
import type { Database, Json } from "@nextblock-cms/db";
|
|
8
|
-
import {
|
|
9
|
+
import { BlockType } from '../../../../lib/blocks/blockRegistry';
|
|
9
10
|
|
|
10
11
|
type Block = Database["public"]["Tables"]["blocks"]["Row"];
|
|
11
|
-
import { getBlockDefinition,
|
|
12
|
+
import { getBlockDefinition, SectionBlockContent } from '../../../../lib/blocks/blockRegistry';
|
|
12
13
|
import { Button } from "@nextblock-cms/ui";
|
|
13
14
|
import { PlusCircle } from "lucide-react";
|
|
14
15
|
import {
|
|
@@ -23,6 +24,7 @@ import BlockTypeSelector from "./BlockTypeSelector";
|
|
|
23
24
|
import {
|
|
24
25
|
createBlockForPage,
|
|
25
26
|
createBlockForPost,
|
|
27
|
+
createBlockForProduct,
|
|
26
28
|
updateBlock,
|
|
27
29
|
updateMultipleBlockOrders,
|
|
28
30
|
} from "../actions";
|
|
@@ -47,8 +49,8 @@ import { SortableBlockItem } from "./SortableBlockItem";
|
|
|
47
49
|
import EditableBlock from "./EditableBlock";
|
|
48
50
|
|
|
49
51
|
interface BlockEditorAreaProps {
|
|
50
|
-
parentId: number;
|
|
51
|
-
parentType: "page" | "post";
|
|
52
|
+
parentId: number | string;
|
|
53
|
+
parentType: "page" | "post" | "product";
|
|
52
54
|
initialBlocks: Block[];
|
|
53
55
|
languageId: number;
|
|
54
56
|
}
|
|
@@ -76,6 +78,7 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
76
78
|
|
|
77
79
|
const [blocks, setBlocks] = useState<Block[]>(() => initialBlocks.sort((a, b) => a.order - b.order));
|
|
78
80
|
const lastSavedBlocks = useRef(blocks);
|
|
81
|
+
const router = useRouter();
|
|
79
82
|
const [isPending, startTransition] = useTransition();
|
|
80
83
|
const [isSavingNested, startSavingNestedTransition] = useTransition();
|
|
81
84
|
const [isBlockSelectorOpen, setIsBlockSelectorOpen] = useState(false);
|
|
@@ -95,13 +98,15 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
95
98
|
const result = await updateBlock(
|
|
96
99
|
blockToSave.id,
|
|
97
100
|
blockToSave.content,
|
|
98
|
-
parentType === "page" ? parentId : null,
|
|
99
|
-
parentType === "post" ? parentId : null
|
|
101
|
+
parentType === "page" ? parentId as number : null,
|
|
102
|
+
parentType === "post" ? parentId as number : null,
|
|
103
|
+
parentType === "product" ? parentId as string : null
|
|
100
104
|
);
|
|
101
105
|
|
|
102
106
|
if (result.success && result.updatedBlock) {
|
|
103
107
|
// On success, update the last saved state ref
|
|
104
108
|
lastSavedBlocks.current = blocks;
|
|
109
|
+
router.refresh();
|
|
105
110
|
} else {
|
|
106
111
|
// On failure, revert the UI to the last known good state
|
|
107
112
|
alert(`Failed to save changes: ${result.error}. Reverting.`);
|
|
@@ -250,13 +255,15 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
250
255
|
const result = await updateBlock(
|
|
251
256
|
parentSectionBlock.id,
|
|
252
257
|
parentSectionBlock.content,
|
|
253
|
-
parentType === "page" ? parentId : null,
|
|
254
|
-
parentType === "post" ? parentId : null
|
|
258
|
+
parentType === "page" ? parentId as number : null,
|
|
259
|
+
parentType === "post" ? parentId as number : null,
|
|
260
|
+
parentType === "product" ? parentId as string : null
|
|
255
261
|
);
|
|
256
262
|
|
|
257
263
|
if (result.success && result.updatedBlock) {
|
|
258
264
|
lastSavedBlocks.current = blocks;
|
|
259
265
|
setEditingNestedBlockInfo(null);
|
|
266
|
+
router.refresh();
|
|
260
267
|
} else {
|
|
261
268
|
alert(`Error saving nested block changes: ${result.error}`);
|
|
262
269
|
setBlocks(lastSavedBlocks.current);
|
|
@@ -293,8 +300,9 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
293
300
|
if (blocksToUpdate.length > 0) {
|
|
294
301
|
const updateResult = await updateMultipleBlockOrders(
|
|
295
302
|
blocksToUpdate,
|
|
296
|
-
parentType === "page" ? parentId : null,
|
|
297
|
-
parentType === "post" ? parentId : null
|
|
303
|
+
parentType === "page" ? parentId as number : null,
|
|
304
|
+
parentType === "post" ? parentId as number : null,
|
|
305
|
+
parentType === "product" ? parentId as string : null
|
|
298
306
|
);
|
|
299
307
|
|
|
300
308
|
if (updateResult?.error) {
|
|
@@ -306,14 +314,21 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
306
314
|
let createResult;
|
|
307
315
|
if (parentType === "page") {
|
|
308
316
|
createResult = await createBlockForPage(
|
|
309
|
-
parentId,
|
|
317
|
+
parentId as number,
|
|
310
318
|
languageId,
|
|
311
319
|
blockType,
|
|
312
320
|
newOrder
|
|
313
321
|
);
|
|
314
|
-
} else {
|
|
322
|
+
} else if (parentType === "post") {
|
|
315
323
|
createResult = await createBlockForPost(
|
|
316
|
-
parentId,
|
|
324
|
+
parentId as number,
|
|
325
|
+
languageId,
|
|
326
|
+
blockType,
|
|
327
|
+
newOrder
|
|
328
|
+
);
|
|
329
|
+
} else {
|
|
330
|
+
createResult = await createBlockForProduct(
|
|
331
|
+
parentId as string,
|
|
317
332
|
languageId,
|
|
318
333
|
blockType,
|
|
319
334
|
newOrder
|
|
@@ -333,6 +348,7 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
333
348
|
|
|
334
349
|
setBlocks(finalBlocks);
|
|
335
350
|
lastSavedBlocks.current = finalBlocks;
|
|
351
|
+
router.refresh();
|
|
336
352
|
} else {
|
|
337
353
|
alert(`Error adding block: ${createResult?.error}`);
|
|
338
354
|
// TODO: Revert order changes if creation fails
|
|
@@ -380,8 +396,9 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
380
396
|
startTransition(async () => {
|
|
381
397
|
const result = await updateMultipleBlockOrders(
|
|
382
398
|
itemsToUpdateDb,
|
|
383
|
-
parentType === "page" ? parentId : null,
|
|
384
|
-
parentType === "post" ? parentId : null
|
|
399
|
+
parentType === "page" ? parentId as number : null,
|
|
400
|
+
parentType === "post" ? parentId as number : null,
|
|
401
|
+
parentType === "product" ? parentId as string : null
|
|
385
402
|
);
|
|
386
403
|
|
|
387
404
|
if (result?.error) {
|
|
@@ -389,6 +406,7 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
389
406
|
setBlocks(originalBlocks);
|
|
390
407
|
} else {
|
|
391
408
|
lastSavedBlocks.current = finalItemsWithUpdatedOrder;
|
|
409
|
+
router.refresh();
|
|
392
410
|
}
|
|
393
411
|
});
|
|
394
412
|
}
|
|
@@ -407,7 +425,7 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
407
425
|
parentBlockId: parentBlockIdStr,
|
|
408
426
|
columnIndex,
|
|
409
427
|
blockIndexInColumn,
|
|
410
|
-
blockData: nestedBlockData,
|
|
428
|
+
blockData: nestedBlockData as unknown as NestedBlockData,
|
|
411
429
|
});
|
|
412
430
|
} else {
|
|
413
431
|
console.error("Nested block not found at specified indices:", { parentBlockIdStr, columnIndex, blockIndexInColumn });
|
|
@@ -465,14 +483,16 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
465
483
|
const result = await import("../actions").then(({ deleteBlock }) =>
|
|
466
484
|
deleteBlock(
|
|
467
485
|
blockIdToDelete,
|
|
468
|
-
parentType === "page" ? parentId : null,
|
|
469
|
-
parentType === "post" ? parentId : null
|
|
486
|
+
parentType === "page" ? parentId as number : null,
|
|
487
|
+
parentType === "post" ? parentId as number : null,
|
|
488
|
+
parentType === "product" ? parentId as string : null
|
|
470
489
|
)
|
|
471
490
|
);
|
|
472
491
|
if (result && result.success) {
|
|
473
492
|
const newBlocks = blocks.filter((b) => b.id !== blockIdToDelete);
|
|
474
493
|
setBlocks(newBlocks);
|
|
475
494
|
lastSavedBlocks.current = newBlocks;
|
|
495
|
+
router.refresh();
|
|
476
496
|
} else if (result?.error) {
|
|
477
497
|
alert(`Error deleting block: ${result.error}`);
|
|
478
498
|
}
|
|
@@ -546,8 +566,9 @@ export default function BlockEditorArea({ parentId, parentType, initialBlocks, l
|
|
|
546
566
|
order: 0, // Temporary order for nested blocks
|
|
547
567
|
created_at: new Date().toISOString(),
|
|
548
568
|
updated_at: new Date().toISOString(),
|
|
549
|
-
page_id: parentType === 'page' ? parentId : null,
|
|
550
|
-
post_id: parentType === 'post' ? parentId : null,
|
|
569
|
+
page_id: parentType === 'page' ? parentId as number : null,
|
|
570
|
+
post_id: parentType === 'post' ? parentId as number : null,
|
|
571
|
+
product_id: parentType === 'product' ? parentId as string : null,
|
|
551
572
|
};
|
|
552
573
|
return <NestedBlockEditorComponent
|
|
553
574
|
block={fullBlockForEditor}
|