@fayz-ai/storefront 0.9.0 → 0.10.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/README.md +3 -3
- package/dist/app/createStorefrontApp.d.ts +65 -0
- package/dist/app/createStorefrontApp.d.ts.map +1 -0
- package/dist/{define.d.ts → app/define.d.ts} +3 -3
- package/dist/app/define.d.ts.map +1 -0
- package/dist/app/hydration.d.ts +37 -0
- package/dist/app/hydration.d.ts.map +1 -0
- package/dist/app/prerender.d.ts +57 -0
- package/dist/app/prerender.d.ts.map +1 -0
- package/dist/{router.d.ts → app/router.d.ts} +140 -113
- package/dist/app/router.d.ts.map +1 -0
- package/dist/{scaffold.d.ts → app/scaffold.d.ts} +2 -2
- package/dist/app/scaffold.d.ts.map +1 -0
- package/dist/app/serving.d.ts +70 -0
- package/dist/app/serving.d.ts.map +1 -0
- package/dist/app/static-render.d.ts +46 -0
- package/dist/app/static-render.d.ts.map +1 -0
- package/dist/blocks/block-at-path.d.ts +59 -0
- package/dist/blocks/block-at-path.d.ts.map +1 -0
- package/dist/blocks/page-blocks.d.ts +163 -0
- package/dist/blocks/page-blocks.d.ts.map +1 -0
- package/dist/blocks/page-data.d.ts +105 -0
- package/dist/blocks/page-data.d.ts.map +1 -0
- package/dist/{blocks.d.ts → blocks/registry.d.ts} +1 -1
- package/dist/blocks/registry.d.ts.map +1 -0
- package/dist/{sections.d.ts → blocks/sections.d.ts} +40 -2
- package/dist/blocks/sections.d.ts.map +1 -0
- package/dist/chunk-6B7H3IZ6.js +10232 -0
- package/dist/chunk-6B7H3IZ6.js.map +1 -0
- package/dist/chunk-6IXTENLB.js +162 -0
- package/dist/chunk-6IXTENLB.js.map +1 -0
- package/dist/components/PaymentTerms.d.ts +34 -0
- package/dist/components/PaymentTerms.d.ts.map +1 -0
- package/dist/components/SmoothImage.d.ts +1 -1
- package/dist/components/SmoothImage.d.ts.map +1 -1
- package/dist/components/account/CustomerAccountShell.d.ts.map +1 -0
- package/dist/components/account/SignInModal.d.ts.map +1 -0
- package/dist/components/cart/CartDrawer.d.ts.map +1 -0
- package/dist/components/cart/QuantityInput.d.ts.map +1 -0
- package/dist/components/checkout/DeliveryEstimator.d.ts.map +1 -0
- package/dist/components/collection/CategoryShowcase.d.ts +44 -0
- package/dist/components/collection/CategoryShowcase.d.ts.map +1 -0
- package/dist/components/collection/FiltersPanel.d.ts +19 -0
- package/dist/components/collection/FiltersPanel.d.ts.map +1 -0
- package/dist/components/collection/ProductGrid.d.ts.map +1 -0
- package/dist/components/layout/PageBodyBlocks.d.ts +40 -0
- package/dist/components/layout/PageBodyBlocks.d.ts.map +1 -0
- package/dist/components/layout/SearchOverlay.d.ts +13 -0
- package/dist/components/layout/SearchOverlay.d.ts.map +1 -0
- package/dist/components/layout/StorefrontErrorBoundary.d.ts.map +1 -0
- package/dist/components/layout/StorefrontFooter.d.ts.map +1 -0
- package/dist/components/layout/StorefrontHeader.d.ts.map +1 -0
- package/dist/components/layout/Toaster.d.ts.map +1 -0
- package/dist/components/marketing/CollectionSections.d.ts +463 -0
- package/dist/components/marketing/CollectionSections.d.ts.map +1 -0
- package/dist/components/marketing/ConversionSections.d.ts +221 -0
- package/dist/components/marketing/ConversionSections.d.ts.map +1 -0
- package/dist/components/marketing/EditorialSections.d.ts +248 -0
- package/dist/components/marketing/EditorialSections.d.ts.map +1 -0
- package/dist/components/marketing/EntityList.d.ts +97 -0
- package/dist/components/marketing/EntityList.d.ts.map +1 -0
- package/dist/components/marketing/FormBlock.d.ts +167 -0
- package/dist/components/marketing/FormBlock.d.ts.map +1 -0
- package/dist/components/marketing/HeroSection.d.ts +125 -0
- package/dist/components/marketing/HeroSection.d.ts.map +1 -0
- package/dist/components/marketing/MediaCarousel.d.ts +135 -0
- package/dist/components/marketing/MediaCarousel.d.ts.map +1 -0
- package/dist/components/marketing/MiscSections.d.ts +252 -0
- package/dist/components/marketing/MiscSections.d.ts.map +1 -0
- package/dist/components/order/OrderTrackingTimeline.d.ts.map +1 -0
- package/dist/components/product/ProductCard.d.ts +14 -0
- package/dist/components/product/ProductCard.d.ts.map +1 -0
- package/dist/components/product/ProductEnquiryForm.d.ts.map +1 -0
- package/dist/components/product/ProductGallery.d.ts +36 -0
- package/dist/components/product/ProductGallery.d.ts.map +1 -0
- package/dist/components/{ProductOptionSelector.d.ts → product/ProductOptionSelector.d.ts} +1 -1
- package/dist/components/product/ProductOptionSelector.d.ts.map +1 -0
- package/dist/components/product/ProductRail.d.ts +93 -0
- package/dist/components/product/ProductRail.d.ts.map +1 -0
- package/dist/components/product/ProductReviews.d.ts +39 -0
- package/dist/components/product/ProductReviews.d.ts.map +1 -0
- package/dist/components/product/ProductSlider.d.ts +169 -0
- package/dist/components/product/ProductSlider.d.ts.map +1 -0
- package/dist/components/product/ProductSpecs.d.ts.map +1 -0
- package/dist/components/product/RelatedProducts.d.ts.map +1 -0
- package/dist/{auth.d.ts → data/auth.d.ts} +9 -0
- package/dist/data/auth.d.ts.map +1 -0
- package/dist/data/facets.d.ts +39 -0
- package/dist/data/facets.d.ts.map +1 -0
- package/dist/data/loaders.d.ts +56 -0
- package/dist/data/loaders.d.ts.map +1 -0
- package/dist/data/order-status.d.ts.map +1 -0
- package/dist/data/product-options.d.ts +36 -0
- package/dist/data/product-options.d.ts.map +1 -0
- package/dist/diagnostics/seo-audit.d.ts +54 -0
- package/dist/diagnostics/seo-audit.d.ts.map +1 -0
- package/dist/diagnostics/seo-audit.js +3 -0
- package/dist/diagnostics/seo-audit.js.map +1 -0
- package/dist/document/change.d.ts +100 -0
- package/dist/document/change.d.ts.map +1 -0
- package/dist/{config.d.ts → document/config.d.ts} +126 -7
- package/dist/document/config.d.ts.map +1 -0
- package/dist/document/describe.d.ts +135 -0
- package/dist/document/describe.d.ts.map +1 -0
- package/dist/document/overlay.d.ts +58 -0
- package/dist/document/overlay.d.ts.map +1 -0
- package/dist/{presets.d.ts → document/presets.d.ts} +2 -2
- package/dist/document/presets.d.ts.map +1 -0
- package/dist/document/render.d.ts +171 -0
- package/dist/document/render.d.ts.map +1 -0
- package/dist/document/round-trip.d.ts +46 -0
- package/dist/document/round-trip.d.ts.map +1 -0
- package/dist/document/sections.d.ts +194 -0
- package/dist/document/sections.d.ts.map +1 -0
- package/dist/document/upgrade.d.ts +112 -0
- package/dist/document/upgrade.d.ts.map +1 -0
- package/dist/document/validate.d.ts +24 -0
- package/dist/document/validate.d.ts.map +1 -0
- package/dist/{component-contracts.d.ts → extend/component-contracts.d.ts} +74 -22
- package/dist/extend/component-contracts.d.ts.map +1 -0
- package/dist/extend/component-override.d.ts +38 -0
- package/dist/extend/component-override.d.ts.map +1 -0
- package/dist/extend/component-selectors.d.ts.map +1 -0
- package/dist/extend/editor-bridge.d.ts +59 -0
- package/dist/extend/editor-bridge.d.ts.map +1 -0
- package/dist/extend/extension-points.d.ts +103 -0
- package/dist/extend/extension-points.d.ts.map +1 -0
- package/dist/extend/slots.d.ts +216 -0
- package/dist/extend/slots.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +25 -6
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/use-address.d.ts +26 -0
- package/dist/hooks/use-address.d.ts.map +1 -0
- package/dist/hooks/use-cart.d.ts +3 -0
- package/dist/hooks/use-cart.d.ts.map +1 -0
- package/dist/hooks/use-catalog.d.ts +24 -0
- package/dist/hooks/use-catalog.d.ts.map +1 -0
- package/dist/hooks/use-categories.d.ts +19 -0
- package/dist/hooks/use-categories.d.ts.map +1 -0
- package/dist/hooks/use-checkout.d.ts +41 -0
- package/dist/hooks/use-checkout.d.ts.map +1 -0
- package/dist/hooks/use-discount-validator.d.ts +20 -0
- package/dist/hooks/use-discount-validator.d.ts.map +1 -0
- package/dist/hooks/use-enquiry.d.ts +7 -0
- package/dist/hooks/use-enquiry.d.ts.map +1 -0
- package/dist/hooks/{useMyOrders.d.ts → use-my-orders.d.ts} +1 -1
- package/dist/hooks/use-my-orders.d.ts.map +1 -0
- package/dist/hooks/use-order.d.ts +17 -0
- package/dist/hooks/use-order.d.ts.map +1 -0
- package/dist/hooks/use-payment-methods.d.ts +19 -0
- package/dist/hooks/use-payment-methods.d.ts.map +1 -0
- package/dist/hooks/use-policies.d.ts +25 -0
- package/dist/hooks/use-policies.d.ts.map +1 -0
- package/dist/hooks/use-product.d.ts +15 -0
- package/dist/hooks/use-product.d.ts.map +1 -0
- package/dist/hooks/{useProducts.d.ts → use-products.d.ts} +1 -1
- package/dist/hooks/use-products.d.ts.map +1 -0
- package/dist/hooks/use-region.d.ts +15 -0
- package/dist/hooks/use-region.d.ts.map +1 -0
- package/dist/hooks/use-shipping.d.ts +29 -0
- package/dist/hooks/use-shipping.d.ts.map +1 -0
- package/dist/hooks/use-storefront-actions.d.ts +12 -0
- package/dist/hooks/use-storefront-actions.d.ts.map +1 -0
- package/dist/hooks/use-storefront-head.d.ts +19 -0
- package/dist/hooks/use-storefront-head.d.ts.map +1 -0
- package/dist/hooks/use-storefront.d.ts +3 -0
- package/dist/hooks/use-storefront.d.ts.map +1 -0
- package/dist/index.d.ts +123 -54
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1311 -5012
- package/dist/index.js.map +1 -1
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/motion.d.ts.map +1 -0
- package/dist/lib/seo.d.ts +26 -0
- package/dist/lib/seo.d.ts.map +1 -0
- package/dist/{testids.d.ts → lib/testids.d.ts} +21 -0
- package/dist/lib/testids.d.ts.map +1 -0
- package/dist/{theme.d.ts → lib/theme.d.ts} +41 -3
- package/dist/lib/theme.d.ts.map +1 -0
- package/dist/render.d.ts +5 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +359 -0
- package/dist/render.js.map +1 -0
- package/dist/stores/cart.store.d.ts +6 -2
- package/dist/stores/cart.store.d.ts.map +1 -1
- package/dist/stores/catalog.store.d.ts +11 -0
- package/dist/stores/catalog.store.d.ts.map +1 -1
- package/dist/views/CatalogPage.d.ts.map +1 -0
- package/dist/views/CheckoutPage.d.ts.map +1 -0
- package/dist/views/ContentPage.d.ts +11 -0
- package/dist/views/ContentPage.d.ts.map +1 -0
- package/dist/views/HomePage.d.ts +9 -0
- package/dist/views/HomePage.d.ts.map +1 -0
- package/dist/views/MyPurchasesPage.d.ts.map +1 -0
- package/dist/views/NotFoundPage.d.ts.map +1 -0
- package/dist/{pages → views}/OrderConfirmationPage.d.ts.map +1 -1
- package/dist/{pages → views}/PolicyPage.d.ts.map +1 -1
- package/dist/views/ProductDetailPage.d.ts.map +1 -0
- package/dist/views/StorefrontPage.d.ts +35 -0
- package/dist/views/StorefrontPage.d.ts.map +1 -0
- package/dist/workflows/checkout.d.ts +1 -1
- package/dist/workflows/checkout.d.ts.map +1 -1
- package/package.json +21 -14
- package/dist/auth.d.ts.map +0 -1
- package/dist/blocks.d.ts.map +0 -1
- package/dist/component-contracts.d.ts.map +0 -1
- package/dist/component-selectors.d.ts.map +0 -1
- package/dist/components/CartDrawer.d.ts.map +0 -1
- package/dist/components/CustomerAccountShell.d.ts.map +0 -1
- package/dist/components/DeliveryEstimator.d.ts.map +0 -1
- package/dist/components/FiltersPanel.d.ts +0 -3
- package/dist/components/FiltersPanel.d.ts.map +0 -1
- package/dist/components/OrderTrackingTimeline.d.ts.map +0 -1
- package/dist/components/ProductCard.d.ts +0 -4
- package/dist/components/ProductCard.d.ts.map +0 -1
- package/dist/components/ProductEnquiryForm.d.ts.map +0 -1
- package/dist/components/ProductGallery.d.ts +0 -22
- package/dist/components/ProductGallery.d.ts.map +0 -1
- package/dist/components/ProductGrid.d.ts.map +0 -1
- package/dist/components/ProductOptionSelector.d.ts.map +0 -1
- package/dist/components/ProductSpecs.d.ts.map +0 -1
- package/dist/components/QuantityInput.d.ts.map +0 -1
- package/dist/components/RelatedProducts.d.ts.map +0 -1
- package/dist/components/SignInModal.d.ts.map +0 -1
- package/dist/components/StorefrontErrorBoundary.d.ts.map +0 -1
- package/dist/components/StorefrontFooter.d.ts.map +0 -1
- package/dist/components/StorefrontHeader.d.ts.map +0 -1
- package/dist/components/Toaster.d.ts.map +0 -1
- package/dist/components/sections/CategoryShowcase.d.ts +0 -6
- package/dist/components/sections/CategoryShowcase.d.ts.map +0 -1
- package/dist/components/sections/HeroSection.d.ts +0 -9
- package/dist/components/sections/HeroSection.d.ts.map +0 -1
- package/dist/components/sections/MediaCarousel.d.ts +0 -11
- package/dist/components/sections/MediaCarousel.d.ts.map +0 -1
- package/dist/components/sections/MiscSections.d.ts +0 -33
- package/dist/components/sections/MiscSections.d.ts.map +0 -1
- package/dist/components/sections/ProductRail.d.ts +0 -9
- package/dist/components/sections/ProductRail.d.ts.map +0 -1
- package/dist/components/sections/ProductSlider.d.ts +0 -15
- package/dist/components/sections/ProductSlider.d.ts.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/createStorefrontApp.d.ts +0 -28
- package/dist/createStorefrontApp.d.ts.map +0 -1
- package/dist/define.d.ts.map +0 -1
- package/dist/format.d.ts.map +0 -1
- package/dist/hooks/useCategories.d.ts +0 -6
- package/dist/hooks/useCategories.d.ts.map +0 -1
- package/dist/hooks/useDiscountValidator.d.ts +0 -14
- package/dist/hooks/useDiscountValidator.d.ts.map +0 -1
- package/dist/hooks/useMyOrders.d.ts.map +0 -1
- package/dist/hooks/useProduct.d.ts +0 -7
- package/dist/hooks/useProduct.d.ts.map +0 -1
- package/dist/hooks/useProducts.d.ts.map +0 -1
- package/dist/hooks/useStorefront.d.ts +0 -35
- package/dist/hooks/useStorefront.d.ts.map +0 -1
- package/dist/hooks/useStorefrontHead.d.ts +0 -11
- package/dist/hooks/useStorefrontHead.d.ts.map +0 -1
- package/dist/motion.d.ts.map +0 -1
- package/dist/order-status.d.ts.map +0 -1
- package/dist/pages/CatalogPage.d.ts.map +0 -1
- package/dist/pages/CheckoutPage.d.ts.map +0 -1
- package/dist/pages/HomePage.d.ts +0 -9
- package/dist/pages/HomePage.d.ts.map +0 -1
- package/dist/pages/MyPurchasesPage.d.ts.map +0 -1
- package/dist/pages/NotFoundPage.d.ts.map +0 -1
- package/dist/pages/ProductDetailPage.d.ts.map +0 -1
- package/dist/presets.d.ts.map +0 -1
- package/dist/product-options.d.ts +0 -12
- package/dist/product-options.d.ts.map +0 -1
- package/dist/router.d.ts.map +0 -1
- package/dist/scaffold.d.ts.map +0 -1
- package/dist/sections.d.ts.map +0 -1
- package/dist/testids.d.ts.map +0 -1
- package/dist/theme.d.ts.map +0 -1
- package/src/auth.ts +0 -227
- package/src/blocks.tsx +0 -51
- package/src/catalog.ts +0 -3
- package/src/component-contracts.ts +0 -154
- package/src/component-selectors.ts +0 -31
- package/src/components/CartDrawer.tsx +0 -279
- package/src/components/CustomerAccountShell.tsx +0 -102
- package/src/components/DeliveryEstimator.tsx +0 -157
- package/src/components/FiltersPanel.tsx +0 -109
- package/src/components/OrderTrackingTimeline.tsx +0 -135
- package/src/components/Price.tsx +0 -32
- package/src/components/ProductCard.tsx +0 -120
- package/src/components/ProductEnquiryForm.tsx +0 -175
- package/src/components/ProductGallery.tsx +0 -174
- package/src/components/ProductGrid.tsx +0 -43
- package/src/components/ProductOptionSelector.tsx +0 -50
- package/src/components/ProductSpecs.tsx +0 -75
- package/src/components/QuantityInput.tsx +0 -52
- package/src/components/RelatedProducts.tsx +0 -40
- package/src/components/SignInModal.tsx +0 -120
- package/src/components/SmoothImage.tsx +0 -59
- package/src/components/StorefrontErrorBoundary.tsx +0 -51
- package/src/components/StorefrontFooter.tsx +0 -110
- package/src/components/StorefrontHeader.tsx +0 -477
- package/src/components/Toaster.tsx +0 -60
- package/src/components/sections/CategoryShowcase.tsx +0 -80
- package/src/components/sections/HeroSection.tsx +0 -193
- package/src/components/sections/MediaCarousel.tsx +0 -141
- package/src/components/sections/MiscSections.tsx +0 -158
- package/src/components/sections/ProductRail.tsx +0 -81
- package/src/components/sections/ProductSlider.tsx +0 -119
- package/src/config.ts +0 -229
- package/src/createStorefrontApp.tsx +0 -247
- package/src/define.ts +0 -34
- package/src/format.ts +0 -7
- package/src/hooks/index.ts +0 -6
- package/src/hooks/useCategories.ts +0 -26
- package/src/hooks/useDiscountValidator.ts +0 -43
- package/src/hooks/useMyOrders.ts +0 -41
- package/src/hooks/useProduct.ts +0 -36
- package/src/hooks/useProducts.ts +0 -39
- package/src/hooks/useStorefront.ts +0 -58
- package/src/hooks/useStorefrontHead.ts +0 -25
- package/src/index.ts +0 -202
- package/src/motion.tsx +0 -94
- package/src/order-status.ts +0 -33
- package/src/pages/CatalogPage.tsx +0 -118
- package/src/pages/CheckoutPage.tsx +0 -689
- package/src/pages/HomePage.tsx +0 -16
- package/src/pages/MyPurchasesPage.tsx +0 -311
- package/src/pages/NotFoundPage.tsx +0 -25
- package/src/pages/OrderConfirmationPage.tsx +0 -190
- package/src/pages/PolicyPage.tsx +0 -47
- package/src/pages/ProductDetailPage.tsx +0 -264
- package/src/presets.ts +0 -324
- package/src/product-options.ts +0 -63
- package/src/router.ts +0 -49
- package/src/scaffold.tsx +0 -115
- package/src/sections.ts +0 -116
- package/src/shop.ts +0 -4
- package/src/stores/cart.store.ts +0 -185
- package/src/stores/catalog.store.ts +0 -39
- package/src/stores/delivery.store.ts +0 -152
- package/src/stores/session.store.ts +0 -28
- package/src/stores/toast.store.ts +0 -43
- package/src/testids.ts +0 -136
- package/src/theme.ts +0 -194
- package/src/ui.ts +0 -5
- package/src/workflows/checkout.ts +0 -119
- /package/dist/components/{CustomerAccountShell.d.ts → account/CustomerAccountShell.d.ts} +0 -0
- /package/dist/components/{SignInModal.d.ts → account/SignInModal.d.ts} +0 -0
- /package/dist/components/{CartDrawer.d.ts → cart/CartDrawer.d.ts} +0 -0
- /package/dist/components/{QuantityInput.d.ts → cart/QuantityInput.d.ts} +0 -0
- /package/dist/components/{DeliveryEstimator.d.ts → checkout/DeliveryEstimator.d.ts} +0 -0
- /package/dist/components/{ProductGrid.d.ts → collection/ProductGrid.d.ts} +0 -0
- /package/dist/components/{StorefrontErrorBoundary.d.ts → layout/StorefrontErrorBoundary.d.ts} +0 -0
- /package/dist/components/{StorefrontFooter.d.ts → layout/StorefrontFooter.d.ts} +0 -0
- /package/dist/components/{StorefrontHeader.d.ts → layout/StorefrontHeader.d.ts} +0 -0
- /package/dist/components/{Toaster.d.ts → layout/Toaster.d.ts} +0 -0
- /package/dist/components/{OrderTrackingTimeline.d.ts → order/OrderTrackingTimeline.d.ts} +0 -0
- /package/dist/components/{ProductEnquiryForm.d.ts → product/ProductEnquiryForm.d.ts} +0 -0
- /package/dist/components/{ProductSpecs.d.ts → product/ProductSpecs.d.ts} +0 -0
- /package/dist/components/{RelatedProducts.d.ts → product/RelatedProducts.d.ts} +0 -0
- /package/dist/{order-status.d.ts → data/order-status.d.ts} +0 -0
- /package/dist/{component-selectors.d.ts → extend/component-selectors.d.ts} +0 -0
- /package/dist/{format.d.ts → lib/format.d.ts} +0 -0
- /package/dist/{motion.d.ts → lib/motion.d.ts} +0 -0
- /package/dist/{pages → views}/CatalogPage.d.ts +0 -0
- /package/dist/{pages → views}/CheckoutPage.d.ts +0 -0
- /package/dist/{pages → views}/MyPurchasesPage.d.ts +0 -0
- /package/dist/{pages → views}/NotFoundPage.d.ts +0 -0
- /package/dist/{pages → views}/OrderConfirmationPage.d.ts +0 -0
- /package/dist/{pages → views}/PolicyPage.d.ts +0 -0
- /package/dist/{pages → views}/ProductDetailPage.d.ts +0 -0
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react'
|
|
2
|
-
import { ChevronLeft, RefreshCcw, ShieldCheck, Truck } from 'lucide-react'
|
|
3
|
-
import { useProduct } from '../hooks/useProduct'
|
|
4
|
-
import { useCartStore } from '../stores/cart.store'
|
|
5
|
-
import { getStorefrontComponents, useStorefrontConfig } from '../config'
|
|
6
|
-
import { useStorefrontHead } from '../hooks/useStorefrontHead'
|
|
7
|
-
import { useStorefrontActions } from '../hooks/useStorefront'
|
|
8
|
-
import { Link } from '../router'
|
|
9
|
-
import { Price } from '../components/Price'
|
|
10
|
-
import { QuantityInput } from '../components/QuantityInput'
|
|
11
|
-
import { ProductOptionSelector } from '../components/ProductOptionSelector'
|
|
12
|
-
import { ProductSpecs } from '../components/ProductSpecs'
|
|
13
|
-
import { RelatedProducts } from '../components/RelatedProducts'
|
|
14
|
-
import { ProductEnquiryForm } from '../components/ProductEnquiryForm'
|
|
15
|
-
import { SmoothImage } from '../components/SmoothImage'
|
|
16
|
-
import { ProductGallery } from '../components/ProductGallery'
|
|
17
|
-
import { DeliveryEstimator } from '../components/DeliveryEstimator'
|
|
18
|
-
import { getProductOptionGroups, type ProductOptionSelection } from '../product-options'
|
|
19
|
-
import { storefrontComponentContracts } from '../component-selectors'
|
|
20
|
-
import { TID } from '../testids'
|
|
21
|
-
|
|
22
|
-
export function ProductDetailPage({ slug }: { slug: string }) {
|
|
23
|
-
const config = useStorefrontConfig()
|
|
24
|
-
const actions = useStorefrontActions()
|
|
25
|
-
const { product, loading } = useProduct(slug)
|
|
26
|
-
const addItem = useCartStore((s) => s.addItem)
|
|
27
|
-
const [qty, setQty] = useState(1)
|
|
28
|
-
const [selectedOptions, setSelectedOptions] = useState<ProductOptionSelection>({})
|
|
29
|
-
|
|
30
|
-
const optionGroups = product ? getProductOptionGroups(product) : []
|
|
31
|
-
useStorefrontHead({
|
|
32
|
-
title: product ? `${product.name} — ${config.name}` : config.name,
|
|
33
|
-
description: product?.description,
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (!product) return
|
|
38
|
-
const defaults = Object.fromEntries(
|
|
39
|
-
getProductOptionGroups(product).map((group) => [group.label, group.values[0] ?? '']),
|
|
40
|
-
)
|
|
41
|
-
setSelectedOptions(defaults)
|
|
42
|
-
}, [product?.id])
|
|
43
|
-
|
|
44
|
-
if (loading) {
|
|
45
|
-
return (
|
|
46
|
-
<main className="mx-auto max-w-5xl px-4 py-8 sm:px-6">
|
|
47
|
-
<div className="grid animate-pulse gap-10 md:grid-cols-2">
|
|
48
|
-
<div className="aspect-square rounded-2xl bg-muted" />
|
|
49
|
-
<div className="space-y-4 py-4">
|
|
50
|
-
<div className="h-8 w-3/4 rounded bg-muted" />
|
|
51
|
-
<div className="h-6 w-1/3 rounded bg-muted" />
|
|
52
|
-
<div className="h-24 rounded bg-muted" />
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
</main>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!product) {
|
|
60
|
-
return (
|
|
61
|
-
<main className="mx-auto max-w-5xl px-4 py-20 text-center sm:px-6">
|
|
62
|
-
<p className="text-lg text-muted-foreground">Produto não encontrado.</p>
|
|
63
|
-
<Link to="/" className="mt-4 inline-block text-primary underline">Voltar à loja</Link>
|
|
64
|
-
</main>
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const soldOut = product.inventoryCount <= 0
|
|
69
|
-
const lowStock = !soldOut && product.inventoryCount <= 5
|
|
70
|
-
const image = product.images.find((i) => i.isPrimary) ?? product.images[0]
|
|
71
|
-
const components = getStorefrontComponents(config)
|
|
72
|
-
const ProductDetailComponent = components.ProductDetail
|
|
73
|
-
// Honours the ProductGallery override contract, which has existed in
|
|
74
|
-
// component-contracts.ts since before any component implemented it.
|
|
75
|
-
const GalleryComponent = components.ProductGallery ?? ProductGallery
|
|
76
|
-
const addToCart = () => addItem(product, qty, selectedOptions)
|
|
77
|
-
const openEnquiry = () => {
|
|
78
|
-
document.getElementById('storefront-product-enquiry')?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
79
|
-
}
|
|
80
|
-
const discountPct =
|
|
81
|
-
product.compareAtPrice && product.compareAtPrice > product.price
|
|
82
|
-
? Math.round((1 - product.price / product.compareAtPrice) * 100)
|
|
83
|
-
: 0
|
|
84
|
-
|
|
85
|
-
if (ProductDetailComponent) {
|
|
86
|
-
return (
|
|
87
|
-
<ProductDetailComponent
|
|
88
|
-
product={product}
|
|
89
|
-
config={config}
|
|
90
|
-
commerceMode={config.commerceMode}
|
|
91
|
-
primaryImage={image}
|
|
92
|
-
optionGroups={optionGroups}
|
|
93
|
-
selectedOptions={selectedOptions}
|
|
94
|
-
setSelectedOptions={setSelectedOptions}
|
|
95
|
-
quantity={qty}
|
|
96
|
-
setQuantity={setQty}
|
|
97
|
-
soldOut={soldOut}
|
|
98
|
-
lowStock={lowStock}
|
|
99
|
-
actions={actions}
|
|
100
|
-
addToCart={addToCart}
|
|
101
|
-
openEnquiry={openEnquiry}
|
|
102
|
-
/>
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return (
|
|
107
|
-
<main {...storefrontComponentContracts.productDetail.root} className="mx-auto max-w-5xl px-4 py-8 sm:px-6">
|
|
108
|
-
<Link to="/" className="mb-6 inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground">
|
|
109
|
-
<ChevronLeft className="h-4 w-4" /> Continuar comprando
|
|
110
|
-
</Link>
|
|
111
|
-
|
|
112
|
-
<div className="grid animate-fade-up grid-cols-1 gap-10 md:grid-cols-2">
|
|
113
|
-
<GalleryComponent
|
|
114
|
-
product={product}
|
|
115
|
-
images={product.images}
|
|
116
|
-
primaryImage={image}
|
|
117
|
-
config={config}
|
|
118
|
-
commerceMode={config.commerceMode}
|
|
119
|
-
/>
|
|
120
|
-
|
|
121
|
-
<div className="flex flex-col py-2 lg:sticky lg:top-24 lg:self-start">
|
|
122
|
-
<nav className="mb-3 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground">
|
|
123
|
-
<Link to="/" className="transition-colors hover:text-foreground">Início</Link>
|
|
124
|
-
{product.categoryName && (
|
|
125
|
-
<>
|
|
126
|
-
<span aria-hidden>/</span>
|
|
127
|
-
<span>{product.categoryName}</span>
|
|
128
|
-
</>
|
|
129
|
-
)}
|
|
130
|
-
<span aria-hidden>/</span>
|
|
131
|
-
<span className="text-foreground">{product.name}</span>
|
|
132
|
-
</nav>
|
|
133
|
-
|
|
134
|
-
<h1 data-testid={TID.pdpName} className="sf-heading text-3xl font-bold tracking-tight">
|
|
135
|
-
{product.name}
|
|
136
|
-
</h1>
|
|
137
|
-
|
|
138
|
-
{config.commerceMode === 'checkout' && (
|
|
139
|
-
<div className="mt-3 flex items-center gap-3">
|
|
140
|
-
<div className="text-xl">
|
|
141
|
-
<Price
|
|
142
|
-
value={product.price}
|
|
143
|
-
compareAt={product.compareAtPrice}
|
|
144
|
-
testId={TID.pdpPrice}
|
|
145
|
-
compareTestId={TID.pdpComparePrice}
|
|
146
|
-
/>
|
|
147
|
-
</div>
|
|
148
|
-
{discountPct > 0 && (
|
|
149
|
-
<span className="rounded-full bg-primary/10 px-2.5 py-1 text-xs font-bold text-primary">
|
|
150
|
-
−{discountPct}%
|
|
151
|
-
</span>
|
|
152
|
-
)}
|
|
153
|
-
</div>
|
|
154
|
-
)}
|
|
155
|
-
|
|
156
|
-
<p data-testid={TID.pdpDescription} className="mt-5 leading-relaxed text-muted-foreground">
|
|
157
|
-
{product.description}
|
|
158
|
-
</p>
|
|
159
|
-
|
|
160
|
-
{config.commerceMode === 'checkout' && (
|
|
161
|
-
<div className="mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm">
|
|
162
|
-
{soldOut ? (
|
|
163
|
-
<span className="inline-flex items-center gap-2 font-medium text-muted-foreground">
|
|
164
|
-
<span className="h-2 w-2 rounded-full bg-muted-foreground" /> Sem estoque
|
|
165
|
-
</span>
|
|
166
|
-
) : lowStock ? (
|
|
167
|
-
<span className="inline-flex items-center gap-2 font-semibold text-amber-600">
|
|
168
|
-
<span className="h-2 w-2 animate-pulse rounded-full bg-amber-500" />
|
|
169
|
-
Últimas unidades — só {product.inventoryCount} restantes
|
|
170
|
-
</span>
|
|
171
|
-
) : (
|
|
172
|
-
<span className="inline-flex items-center gap-2 font-medium text-emerald-600">
|
|
173
|
-
<span className="h-2 w-2 rounded-full bg-emerald-500" /> Em estoque
|
|
174
|
-
</span>
|
|
175
|
-
)}
|
|
176
|
-
{product.sku && <span className="text-muted-foreground">SKU: {product.sku}</span>}
|
|
177
|
-
</div>
|
|
178
|
-
)}
|
|
179
|
-
|
|
180
|
-
<ProductOptionSelector
|
|
181
|
-
groups={optionGroups}
|
|
182
|
-
value={selectedOptions}
|
|
183
|
-
onChange={setSelectedOptions}
|
|
184
|
-
/>
|
|
185
|
-
|
|
186
|
-
<div {...storefrontComponentContracts.productDetail.actions} className="mt-8">
|
|
187
|
-
{config.commerceMode === 'checkout' ? (
|
|
188
|
-
<div className="flex items-center gap-4">
|
|
189
|
-
<QuantityInput
|
|
190
|
-
value={qty}
|
|
191
|
-
onChange={setQty}
|
|
192
|
-
max={soldOut ? 1 : product.inventoryCount}
|
|
193
|
-
testId={TID.pdpQtyInput}
|
|
194
|
-
/>
|
|
195
|
-
<button
|
|
196
|
-
type="button"
|
|
197
|
-
data-testid={TID.pdpAddToCart}
|
|
198
|
-
disabled={soldOut}
|
|
199
|
-
onClick={addToCart}
|
|
200
|
-
className="sf-cta flex-1 bg-primary py-3.5 font-semibold text-primary-foreground shadow-md transition-all hover:-translate-y-0.5 hover:shadow-lg disabled:cursor-not-allowed disabled:opacity-40 disabled:shadow-none disabled:hover:translate-y-0"
|
|
201
|
-
style={{ borderRadius: 'var(--sf-radius-button)' }}
|
|
202
|
-
>
|
|
203
|
-
{soldOut ? 'Esgotado' : 'Adicionar ao carrinho'}
|
|
204
|
-
</button>
|
|
205
|
-
</div>
|
|
206
|
-
) : config.commerceMode === 'enquiry' ? (
|
|
207
|
-
<button
|
|
208
|
-
type="button"
|
|
209
|
-
{...storefrontComponentContracts.productDetail.enquiryButton}
|
|
210
|
-
onClick={openEnquiry}
|
|
211
|
-
className="sf-cta w-full bg-primary py-3.5 font-semibold text-primary-foreground shadow-md transition-all hover:-translate-y-0.5 hover:shadow-lg"
|
|
212
|
-
style={{ borderRadius: 'var(--sf-radius-button)' }}
|
|
213
|
-
>
|
|
214
|
-
{config.enquiry.label}
|
|
215
|
-
</button>
|
|
216
|
-
) : (
|
|
217
|
-
<Link
|
|
218
|
-
to={config.catalogPath}
|
|
219
|
-
className="inline-flex border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted"
|
|
220
|
-
style={{ borderRadius: 'var(--sf-radius-button)' }}
|
|
221
|
-
>
|
|
222
|
-
Voltar ao catálogo
|
|
223
|
-
</Link>
|
|
224
|
-
)}
|
|
225
|
-
</div>
|
|
226
|
-
|
|
227
|
-
{/* Asked here, before the cart exists: "do you deliver to me and for
|
|
228
|
-
how much" is the question that decides whether this page converts,
|
|
229
|
-
and it used to be answerable only at the end of checkout. */}
|
|
230
|
-
{config.commerceMode === 'checkout' && <DeliveryEstimator />}
|
|
231
|
-
|
|
232
|
-
{config.commerceMode === 'checkout' && (
|
|
233
|
-
<div className="mt-6 grid grid-cols-3 gap-3 border-t pt-6 text-center">
|
|
234
|
-
<div className="flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground">
|
|
235
|
-
<Truck className="h-5 w-5 text-primary" /> Envio rápido
|
|
236
|
-
</div>
|
|
237
|
-
<div className="flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground">
|
|
238
|
-
<RefreshCcw className="h-5 w-5 text-primary" /> Troca fácil
|
|
239
|
-
</div>
|
|
240
|
-
<div className="flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground">
|
|
241
|
-
<ShieldCheck className="h-5 w-5 text-primary" /> Pagamento seguro
|
|
242
|
-
</div>
|
|
243
|
-
</div>
|
|
244
|
-
)}
|
|
245
|
-
</div>
|
|
246
|
-
</div>
|
|
247
|
-
|
|
248
|
-
{config.commerceMode === 'enquiry' && (
|
|
249
|
-
<section id="storefront-product-enquiry" className="mt-12 border-t pt-10">
|
|
250
|
-
<div className="mx-auto max-w-3xl">
|
|
251
|
-
<h2 className="sf-heading text-2xl font-semibold">Need more information?</h2>
|
|
252
|
-
<p className="mt-2 text-muted-foreground">Send an enquiry about this product.</p>
|
|
253
|
-
<div className="mt-6">
|
|
254
|
-
<ProductEnquiryForm product={product} />
|
|
255
|
-
</div>
|
|
256
|
-
</div>
|
|
257
|
-
</section>
|
|
258
|
-
)}
|
|
259
|
-
|
|
260
|
-
<ProductSpecs product={product} />
|
|
261
|
-
<RelatedProducts product={product} />
|
|
262
|
-
</main>
|
|
263
|
-
)
|
|
264
|
-
}
|
package/src/presets.ts
DELETED
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import type { StorefrontTheme } from './theme'
|
|
2
|
-
import type { HomeConfig } from './sections'
|
|
3
|
-
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
// Template presets — recreations of the four Nuvemshop patterns selected in
|
|
6
|
-
// docs/storefront-templates-research.md. Each is { theme, home() } so an app
|
|
7
|
-
// (or the Fayz agent) starts from a complete professional store and overrides
|
|
8
|
-
// copy, colors, hero images and sections — the saas-core theming workflow.
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
/** Real imagery for a template home — resolved at build time (e.g. via
|
|
12
|
-
* scripts/fetch-unsplash.mjs); falls back to gradient placeholders when omitted. */
|
|
13
|
-
export interface TemplateImages {
|
|
14
|
-
hero?: Array<string | undefined>
|
|
15
|
-
banners?: Array<string | undefined>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface StorefrontTemplate {
|
|
19
|
-
id: string
|
|
20
|
-
/** The Nuvemshop pattern this recreates */
|
|
21
|
-
inspiration: string
|
|
22
|
-
theme: StorefrontTheme
|
|
23
|
-
/** Default home blueprint — pass the store name for copy interpolation */
|
|
24
|
-
home: (storeName: string, images?: TemplateImages) => HomeConfig
|
|
25
|
-
announcement?: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** maré — elegant fashion (Rio): airy warm paper, olive ink, sand CTA, centered logo. */
|
|
29
|
-
export const mareTemplate: StorefrontTemplate = {
|
|
30
|
-
id: 'mare',
|
|
31
|
-
inspiration: 'Nuvemshop Rio',
|
|
32
|
-
announcement: 'FRETE GRÁTIS EM COMPRAS ACIMA DE R$ 300',
|
|
33
|
-
theme: {
|
|
34
|
-
name: 'mare',
|
|
35
|
-
colors: {
|
|
36
|
-
background: '40 14% 93%',
|
|
37
|
-
foreground: '90 8% 38%',
|
|
38
|
-
primary: '42 32% 61%',
|
|
39
|
-
primaryForeground: '40 30% 12%',
|
|
40
|
-
card: '40 20% 97%',
|
|
41
|
-
cardForeground: '90 10% 25%',
|
|
42
|
-
muted: '40 12% 88%',
|
|
43
|
-
mutedForeground: '90 6% 50%',
|
|
44
|
-
border: '40 10% 82%',
|
|
45
|
-
announcementBackground: '90 10% 30%',
|
|
46
|
-
announcementForeground: '40 20% 95%',
|
|
47
|
-
},
|
|
48
|
-
font: { heading: 'Plus Jakarta Sans', body: 'Plus Jakarta Sans' },
|
|
49
|
-
radius: 'soft',
|
|
50
|
-
header: { variant: 'centered' },
|
|
51
|
-
productCard: { style: 'editorial', imageAspect: 'portrait' },
|
|
52
|
-
},
|
|
53
|
-
home: (storeName, images) => ({
|
|
54
|
-
sections: [
|
|
55
|
-
{
|
|
56
|
-
type: 'hero',
|
|
57
|
-
variant: 'banner',
|
|
58
|
-
height: 'tall',
|
|
59
|
-
slides: [
|
|
60
|
-
{ title: 'Feito para durar', subtitle: `A nova coleção ${storeName} chegou — peças atemporais em materiais honestos.`, cta: 'Ver coleção', hue: 75, image: images?.hero?.[0] },
|
|
61
|
-
],
|
|
62
|
-
},
|
|
63
|
-
{ type: 'categories', style: 'bubbles' },
|
|
64
|
-
{ type: 'products', title: 'Destaques', filter: 'all', limit: 4 },
|
|
65
|
-
{ type: 'banner', title: 'Até 30% off', eyebrow: 'por tempo limitado', subtitle: 'Peças selecionadas da estação', cta: 'Aproveitar', hue: 40, image: images?.banners?.[0] },
|
|
66
|
-
{ type: 'products', title: 'Promoções', filter: 'sale', limit: 4 },
|
|
67
|
-
{ type: 'products', title: 'Lançamentos', filter: 'new', limit: 4 },
|
|
68
|
-
{ type: 'newsletter', title: 'Assine nossa newsletter', subtitle: 'Novidades e ofertas exclusivas, sem spam.' },
|
|
69
|
-
],
|
|
70
|
-
}),
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** sertão — editorial organic (Uyuni): cream paper, coffee serif ink, boxed hero, manifesto. */
|
|
74
|
-
export const sertaoTemplate: StorefrontTemplate = {
|
|
75
|
-
id: 'sertao',
|
|
76
|
-
inspiration: 'Nuvemshop Uyuni',
|
|
77
|
-
theme: {
|
|
78
|
-
name: 'sertao',
|
|
79
|
-
colors: {
|
|
80
|
-
background: '30 100% 98%',
|
|
81
|
-
foreground: '28 42% 32%',
|
|
82
|
-
primary: '28 42% 32%',
|
|
83
|
-
primaryForeground: '30 100% 97%',
|
|
84
|
-
card: '32 60% 96%',
|
|
85
|
-
cardForeground: '28 40% 26%',
|
|
86
|
-
muted: '32 40% 92%',
|
|
87
|
-
mutedForeground: '28 22% 48%',
|
|
88
|
-
border: '28 30% 80%',
|
|
89
|
-
},
|
|
90
|
-
font: { heading: 'Marcellus', body: 'Marcellus', fallback: 'serif' },
|
|
91
|
-
radius: 'none',
|
|
92
|
-
header: { variant: 'minimal', uppercaseNav: true },
|
|
93
|
-
productCard: { style: 'editorial', imageAspect: 'portrait' },
|
|
94
|
-
uppercaseButtons: true,
|
|
95
|
-
},
|
|
96
|
-
home: (storeName, images) => ({
|
|
97
|
-
sections: [
|
|
98
|
-
{
|
|
99
|
-
type: 'hero',
|
|
100
|
-
variant: 'banner',
|
|
101
|
-
height: 'tall',
|
|
102
|
-
slides: [
|
|
103
|
-
{ title: 'Essenciais naturais', subtitle: 'Conheça nossa coleção', cta: 'Ver mais', hue: 28, boxed: true, image: images?.hero?.[0] },
|
|
104
|
-
],
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
type: 'manifesto',
|
|
108
|
-
text: `Num mundo de pressa e excesso, ${storeName} escolhe o caminho lento: ingredientes honestos, produção pequena e cuidado em cada detalhe.`,
|
|
109
|
-
},
|
|
110
|
-
{ type: 'categories', style: 'tiles' },
|
|
111
|
-
{ type: 'products', title: 'ESSENCIAIS', filter: 'all', limit: 4 },
|
|
112
|
-
{
|
|
113
|
-
type: 'testimonials',
|
|
114
|
-
title: 'Nossos clientes',
|
|
115
|
-
items: [
|
|
116
|
-
{ quote: 'Qualidade impecável e um cheiro que abraça.', author: 'Helena M.' },
|
|
117
|
-
{ quote: 'Compro há dois anos e nunca me decepcionou.', author: 'Rafael T.' },
|
|
118
|
-
{ quote: 'Entrega rápida e embalagem linda, dá gosto de presentear.', author: 'Cláudia P.' },
|
|
119
|
-
],
|
|
120
|
-
},
|
|
121
|
-
{ type: 'products', title: 'NOVIDADES', filter: 'new', limit: 4 },
|
|
122
|
-
{
|
|
123
|
-
type: 'benefits',
|
|
124
|
-
items: [
|
|
125
|
-
{ icon: 'Truck', title: 'Frete para todo o Brasil', text: 'Envio em até 48h' },
|
|
126
|
-
{ icon: 'Leaf', title: '100% vegano', text: 'Sem testes em animais' },
|
|
127
|
-
{ icon: 'RotateCcw', title: 'Troca fácil', text: '30 dias para trocar' },
|
|
128
|
-
],
|
|
129
|
-
},
|
|
130
|
-
{ type: 'newsletter', title: 'NEWSLETTER', subtitle: 'Histórias, lançamentos e cuidado — direto no seu e-mail.' },
|
|
131
|
-
],
|
|
132
|
-
}),
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** volt — bold tech (Brasília): white canvas, near-black ink, vivid CTA, dark search-first header, split hero. */
|
|
136
|
-
export const voltTemplate: StorefrontTemplate = {
|
|
137
|
-
id: 'volt',
|
|
138
|
-
inspiration: 'Nuvemshop Brasília',
|
|
139
|
-
announcement: 'CUPOM BEMVINDO10 — 10% DE DESCONTO NA PRIMEIRA COMPRA',
|
|
140
|
-
theme: {
|
|
141
|
-
name: 'volt',
|
|
142
|
-
colors: {
|
|
143
|
-
background: '0 0% 100%',
|
|
144
|
-
foreground: '0 0% 7%',
|
|
145
|
-
primary: '341 100% 45%',
|
|
146
|
-
primaryForeground: '0 0% 100%',
|
|
147
|
-
card: '0 0% 98%',
|
|
148
|
-
cardForeground: '0 0% 10%',
|
|
149
|
-
muted: '0 0% 94%',
|
|
150
|
-
mutedForeground: '0 0% 40%',
|
|
151
|
-
border: '0 0% 88%',
|
|
152
|
-
headerBackground: '0 0% 6%',
|
|
153
|
-
headerForeground: '0 0% 98%',
|
|
154
|
-
announcementBackground: '341 100% 45%',
|
|
155
|
-
announcementForeground: '0 0% 100%',
|
|
156
|
-
},
|
|
157
|
-
font: { heading: 'Chakra Petch', body: 'Rubik' },
|
|
158
|
-
radius: 'soft',
|
|
159
|
-
header: { variant: 'search', uppercaseNav: true },
|
|
160
|
-
productCard: { style: 'card', imageAspect: 'square' },
|
|
161
|
-
},
|
|
162
|
-
home: (storeName, images) => ({
|
|
163
|
-
sections: [
|
|
164
|
-
{
|
|
165
|
-
type: 'hero',
|
|
166
|
-
variant: 'split',
|
|
167
|
-
slides: [
|
|
168
|
-
{ title: 'Tecnologia', subtitle: 'O melhor do hardware e dos acessórios', hue: 250, image: images?.hero?.[0] },
|
|
169
|
-
{ title: 'Ofertas', subtitle: `Descontos semanais ${storeName}`, hue: 341, href: '/catalog', image: images?.hero?.[1] },
|
|
170
|
-
],
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
type: 'benefits',
|
|
174
|
-
items: [
|
|
175
|
-
{ icon: 'CreditCard', title: '3x sem juros', text: 'Em todo o site' },
|
|
176
|
-
{ icon: 'Truck', title: 'Envio em 24h', text: 'Para todo o Brasil' },
|
|
177
|
-
{ icon: 'ShieldCheck', title: 'Compra segura', text: 'Seus dados protegidos' },
|
|
178
|
-
],
|
|
179
|
-
},
|
|
180
|
-
{ type: 'products', title: 'NOVIDADES', filter: 'new', limit: 4 },
|
|
181
|
-
{ type: 'banner', title: 'DESCONTOS SEMANAIS', eyebrow: 'toda sexta', subtitle: 'Ofertas novas, estoque limitado', cta: 'Conferir', hue: 341, image: images?.banners?.[0] },
|
|
182
|
-
{ type: 'products', title: 'PROMOÇÕES', filter: 'sale', limit: 4 },
|
|
183
|
-
{ type: 'newsletter', title: 'Entre para o clube', subtitle: 'Cupons e lançamentos antes de todo mundo.' },
|
|
184
|
-
],
|
|
185
|
-
}),
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/** atelier — classic premium retail (Flex): white, near-black, gold accent, slider hero, trust badges. */
|
|
189
|
-
export const atelierTemplate: StorefrontTemplate = {
|
|
190
|
-
id: 'atelier',
|
|
191
|
-
inspiration: 'Nuvemshop Flex',
|
|
192
|
-
theme: {
|
|
193
|
-
name: 'atelier',
|
|
194
|
-
colors: {
|
|
195
|
-
background: '0 0% 100%',
|
|
196
|
-
foreground: '0 0% 9%',
|
|
197
|
-
primary: '40 45% 48%',
|
|
198
|
-
primaryForeground: '0 0% 100%',
|
|
199
|
-
card: '0 0% 99%',
|
|
200
|
-
cardForeground: '0 0% 9%',
|
|
201
|
-
muted: '40 10% 95%',
|
|
202
|
-
mutedForeground: '0 0% 42%',
|
|
203
|
-
border: '0 0% 88%',
|
|
204
|
-
announcementBackground: '0 0% 9%',
|
|
205
|
-
announcementForeground: '0 0% 98%',
|
|
206
|
-
},
|
|
207
|
-
font: { heading: 'Outfit', body: 'Outfit' },
|
|
208
|
-
radius: 'none',
|
|
209
|
-
header: { variant: 'centered', uppercaseNav: true },
|
|
210
|
-
productCard: { style: 'card', imageAspect: 'square' },
|
|
211
|
-
uppercaseButtons: true,
|
|
212
|
-
},
|
|
213
|
-
announcement: 'PARCELE EM ATÉ 10X · 5% OFF NO PIX',
|
|
214
|
-
home: (storeName, images) => ({
|
|
215
|
-
sections: [
|
|
216
|
-
{
|
|
217
|
-
type: 'hero',
|
|
218
|
-
variant: 'slider',
|
|
219
|
-
height: 'medium',
|
|
220
|
-
slides: [
|
|
221
|
-
{ title: 'Feito para quem valoriza detalhes', subtitle: `Coleção exclusiva ${storeName}`, cta: 'APROVEITE', hue: 40, image: images?.hero?.[0] },
|
|
222
|
-
{ title: 'Clássicos que não saem de moda', subtitle: 'Peças autorais, produção limitada', cta: 'VER COLEÇÃO', hue: 210, image: images?.hero?.[1] },
|
|
223
|
-
],
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
type: 'benefits',
|
|
227
|
-
items: [
|
|
228
|
-
{ icon: 'CreditCard', title: 'Parcele em até 10x', text: 'Ou 5% off no PIX' },
|
|
229
|
-
{ icon: 'Lock', title: 'Site 100% seguro', text: 'Seus dados protegidos' },
|
|
230
|
-
{ icon: 'Award', title: 'Peças exclusivas', text: 'Produtos 100% autorais' },
|
|
231
|
-
],
|
|
232
|
-
},
|
|
233
|
-
{ type: 'categories', style: 'tiles' },
|
|
234
|
-
{ type: 'products', title: 'Novidades', filter: 'new', limit: 4 },
|
|
235
|
-
{ type: 'banner', title: 'Promoções', eyebrow: 'seleção especial', subtitle: 'Selecionados com até 25% off', cta: 'Ver ofertas', hue: 40, image: images?.banners?.[0] },
|
|
236
|
-
{ type: 'products', title: 'Promoções', filter: 'sale', limit: 4 },
|
|
237
|
-
{
|
|
238
|
-
type: 'testimonials',
|
|
239
|
-
title: 'O que dizem sobre nós',
|
|
240
|
-
items: [
|
|
241
|
-
{ quote: 'Acabamento perfeito, superou a expectativa.', author: 'Mariana C.' },
|
|
242
|
-
{ quote: 'Atendimento impecável do pedido à entrega.', author: 'Jorge L.' },
|
|
243
|
-
{ quote: 'Virou minha loja favorita para presentes.', author: 'Paula R.' },
|
|
244
|
-
],
|
|
245
|
-
},
|
|
246
|
-
{ type: 'newsletter' },
|
|
247
|
-
],
|
|
248
|
-
}),
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/** food — warm appetizing delivery (steakhouse / burger / comfort food): dark inviting
|
|
252
|
-
* header, ember CTA, cream canvas, rounded cards, menu-first home with "mais pedidos"
|
|
253
|
-
* rail and social proof. Built for restaurant/food stores — override to go fully dark. */
|
|
254
|
-
export const foodTemplate: StorefrontTemplate = {
|
|
255
|
-
id: 'food',
|
|
256
|
-
inspiration: 'Delivery / restaurant storefront',
|
|
257
|
-
announcement: 'ENTREGA GRÁTIS ACIMA DE R$ 150 · PEÇA JÁ',
|
|
258
|
-
theme: {
|
|
259
|
-
name: 'food',
|
|
260
|
-
colors: {
|
|
261
|
-
background: '30 33% 97%',
|
|
262
|
-
foreground: '20 14% 15%',
|
|
263
|
-
primary: '14 84% 50%',
|
|
264
|
-
primaryForeground: '0 0% 100%',
|
|
265
|
-
card: '30 33% 99%',
|
|
266
|
-
cardForeground: '20 14% 15%',
|
|
267
|
-
muted: '28 20% 92%',
|
|
268
|
-
mutedForeground: '22 10% 42%',
|
|
269
|
-
border: '28 18% 85%',
|
|
270
|
-
headerBackground: '20 14% 11%',
|
|
271
|
-
headerForeground: '30 24% 96%',
|
|
272
|
-
announcementBackground: '14 84% 50%',
|
|
273
|
-
announcementForeground: '0 0% 100%',
|
|
274
|
-
},
|
|
275
|
-
font: { heading: 'Poppins', body: 'Inter' },
|
|
276
|
-
radius: 'round',
|
|
277
|
-
header: { variant: 'classic' },
|
|
278
|
-
productCard: { style: 'card', imageAspect: 'square' },
|
|
279
|
-
},
|
|
280
|
-
home: (storeName, images) => ({
|
|
281
|
-
sections: [
|
|
282
|
-
{
|
|
283
|
-
type: 'hero',
|
|
284
|
-
variant: 'banner',
|
|
285
|
-
height: 'tall',
|
|
286
|
-
slides: [
|
|
287
|
-
{ title: 'Sabor de verdade, na sua porta', subtitle: `${storeName} — feito na hora, do jeito que você ama.`, cta: 'Ver cardápio', href: '/catalog', hue: 20, image: images?.hero?.[0] },
|
|
288
|
-
],
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
type: 'benefits',
|
|
292
|
-
items: [
|
|
293
|
-
{ icon: 'Flame', title: 'Feito na hora', text: 'Preparado com capricho' },
|
|
294
|
-
{ icon: 'Truck', title: 'Entrega rápida', text: 'Frete grátis acima de R$ 150' },
|
|
295
|
-
{ icon: 'CreditCard', title: 'Pagamento fácil', text: 'Pix, cartão e mais' },
|
|
296
|
-
],
|
|
297
|
-
},
|
|
298
|
-
{ type: 'categories', style: 'tiles', title: 'Nosso cardápio' },
|
|
299
|
-
{ type: 'products', title: 'Mais pedidos', eyebrow: 'favoritos da casa', filter: 'all', limit: 8 },
|
|
300
|
-
{ type: 'banner', title: 'Combo do dia', eyebrow: 'oferta', subtitle: 'Seleção especial com desconto', cta: 'Aproveitar', hue: 14, image: images?.banners?.[0] },
|
|
301
|
-
{ type: 'products', title: 'Novidades no cardápio', filter: 'new', limit: 4 },
|
|
302
|
-
{
|
|
303
|
-
type: 'testimonials',
|
|
304
|
-
title: 'O que dizem por aí',
|
|
305
|
-
items: [
|
|
306
|
-
{ quote: 'Chegou quentinho e absurdo de bom. Virei cliente!', author: 'Rafael M.' },
|
|
307
|
-
{ quote: 'Sabor de restaurante em casa, sem complicação.', author: 'Beatriz L.' },
|
|
308
|
-
{ quote: 'Entrega rápida e porção generosa. Recomendo demais.', author: 'Diego S.' },
|
|
309
|
-
],
|
|
310
|
-
},
|
|
311
|
-
{ type: 'newsletter', title: 'Receba nossas ofertas', subtitle: 'Promoções e novidades do cardápio, direto no seu e-mail.' },
|
|
312
|
-
],
|
|
313
|
-
}),
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export const storefrontTemplates = {
|
|
317
|
-
mare: mareTemplate,
|
|
318
|
-
sertao: sertaoTemplate,
|
|
319
|
-
volt: voltTemplate,
|
|
320
|
-
atelier: atelierTemplate,
|
|
321
|
-
food: foodTemplate,
|
|
322
|
-
} as const
|
|
323
|
-
|
|
324
|
-
export type StorefrontTemplateId = keyof typeof storefrontTemplates
|
package/src/product-options.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import type { Product } from '@fayz-ai/shop/types'
|
|
2
|
-
|
|
3
|
-
export interface ProductOptionGroup {
|
|
4
|
-
id: string
|
|
5
|
-
label: string
|
|
6
|
-
values: string[]
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type ProductOptionSelection = Record<string, string>
|
|
10
|
-
|
|
11
|
-
function cleanValues(value: unknown): string[] {
|
|
12
|
-
if (!Array.isArray(value)) return []
|
|
13
|
-
return Array.from(new Set(value.filter((item): item is string => typeof item === 'string' && item.trim().length > 0)))
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function cleanOptionGroup(value: unknown): ProductOptionGroup | null {
|
|
17
|
-
if (!value || typeof value !== 'object') return null
|
|
18
|
-
const record = value as Record<string, unknown>
|
|
19
|
-
const id = typeof record.id === 'string' ? record.id : typeof record.name === 'string' ? record.name : null
|
|
20
|
-
const label = typeof record.label === 'string' ? record.label : typeof record.name === 'string' ? record.name : id
|
|
21
|
-
const values = cleanValues(record.values)
|
|
22
|
-
if (!id || !label || values.length === 0) return null
|
|
23
|
-
return { id, label, values }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function getProductOptionGroups(product: Product): ProductOptionGroup[] {
|
|
27
|
-
const options = product.metadata.options
|
|
28
|
-
const explicit = Array.isArray(options)
|
|
29
|
-
? options.map(cleanOptionGroup).filter((group): group is ProductOptionGroup => Boolean(group))
|
|
30
|
-
: []
|
|
31
|
-
|
|
32
|
-
if (explicit.length > 0) return explicit
|
|
33
|
-
|
|
34
|
-
const groups: ProductOptionGroup[] = []
|
|
35
|
-
const sizes = cleanValues(product.metadata.sizes)
|
|
36
|
-
const colors = cleanValues(product.metadata.colors)
|
|
37
|
-
|
|
38
|
-
if (sizes.length > 0) groups.push({ id: 'Tamanho', label: 'Tamanho', values: sizes })
|
|
39
|
-
if (colors.length > 0) groups.push({ id: 'Cor', label: 'Cor', values: colors })
|
|
40
|
-
|
|
41
|
-
return groups
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function normalizeProductOptionSelection(selection?: ProductOptionSelection | null): ProductOptionSelection {
|
|
45
|
-
if (!selection) return {}
|
|
46
|
-
return Object.fromEntries(
|
|
47
|
-
Object.entries(selection)
|
|
48
|
-
.filter((entry): entry is [string, string] => entry[0].trim().length > 0 && entry[1].trim().length > 0)
|
|
49
|
-
.sort(([a], [b]) => a.localeCompare(b)),
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function formatProductOptionSelection(selection?: ProductOptionSelection | null): string {
|
|
54
|
-
return Object.entries(normalizeProductOptionSelection(selection))
|
|
55
|
-
.map(([label, value]) => `${label}: ${value}`)
|
|
56
|
-
.join(' · ')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function productOptionSelectionKey(selection?: ProductOptionSelection | null): string {
|
|
60
|
-
const normalized = normalizeProductOptionSelection(selection)
|
|
61
|
-
const entries = Object.entries(normalized)
|
|
62
|
-
return entries.length > 0 ? JSON.stringify(entries) : ''
|
|
63
|
-
}
|