@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,193 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from 'react'
|
|
2
|
-
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
|
3
|
-
import type { HeroSlide } from '../../sections'
|
|
4
|
-
import type { HeroVariant } from '../../theme'
|
|
5
|
-
import { bannerPlaceholder } from '../../sections'
|
|
6
|
-
import { useStorefrontConfig } from '../../config'
|
|
7
|
-
import { prefersReducedMotion } from '../../motion'
|
|
8
|
-
import { Link } from '../../router'
|
|
9
|
-
import { TID } from '../../testids'
|
|
10
|
-
import { SmoothImage } from '../SmoothImage'
|
|
11
|
-
|
|
12
|
-
function slideImage(slide: HeroSlide, w = 1600, h = 700): string {
|
|
13
|
-
return slide.image ?? bannerPlaceholder(slide.title, slide.hue ?? 200, (slide.hue ?? 200) + 40, w, h)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function SlideCopy({ slide, active }: { slide: HeroSlide; active: boolean }) {
|
|
17
|
-
const stagger = (i: number) =>
|
|
18
|
-
active ? { className: 'animate-fade-up', style: { animationDelay: `${i * 120}ms` } } : { className: 'opacity-0' }
|
|
19
|
-
|
|
20
|
-
const inner = (
|
|
21
|
-
<>
|
|
22
|
-
<h2 className={`sf-heading text-3xl font-bold leading-tight tracking-tight sm:text-5xl ${stagger(0).className}`} style={stagger(0).style}>
|
|
23
|
-
{slide.title}
|
|
24
|
-
</h2>
|
|
25
|
-
{slide.subtitle && (
|
|
26
|
-
<p className={`mt-3 max-w-md text-base sm:text-lg ${stagger(1).className}`} style={stagger(1).style}>
|
|
27
|
-
{slide.subtitle}
|
|
28
|
-
</p>
|
|
29
|
-
)}
|
|
30
|
-
{slide.cta && (
|
|
31
|
-
<div className={stagger(2).className} style={stagger(2).style}>
|
|
32
|
-
<Link
|
|
33
|
-
to={slide.href ?? '/catalog'}
|
|
34
|
-
className="sf-cta mt-6 inline-block bg-primary px-8 py-3.5 font-semibold text-primary-foreground shadow-lg transition-all hover:-translate-y-0.5 hover:shadow-xl"
|
|
35
|
-
style={{ borderRadius: 'var(--sf-radius-button)' }}
|
|
36
|
-
>
|
|
37
|
-
{slide.cta}
|
|
38
|
-
</Link>
|
|
39
|
-
</div>
|
|
40
|
-
)}
|
|
41
|
-
</>
|
|
42
|
-
)
|
|
43
|
-
if (slide.boxed) {
|
|
44
|
-
return (
|
|
45
|
-
<div className="border border-foreground/30 bg-background/90 p-8 backdrop-blur-sm sm:p-10">{inner}</div>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
return <div className="text-white">{inner}</div>
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function HeroSection({
|
|
52
|
-
variant,
|
|
53
|
-
slides,
|
|
54
|
-
height = 'tall',
|
|
55
|
-
}: {
|
|
56
|
-
variant: HeroVariant
|
|
57
|
-
slides: HeroSlide[]
|
|
58
|
-
height?: 'tall' | 'medium'
|
|
59
|
-
}) {
|
|
60
|
-
const [index, setIndex] = useState(0)
|
|
61
|
-
const [loadedSlides, setLoadedSlides] = useState<Record<number, boolean>>({})
|
|
62
|
-
const config = useStorefrontConfig()
|
|
63
|
-
const interacted = useRef(false)
|
|
64
|
-
const hClass = height === 'tall' ? 'h-[72vh] min-h-[440px]' : 'h-[48vh] min-h-[340px]'
|
|
65
|
-
const revealImages = config.imageLoading.mode !== 'none'
|
|
66
|
-
const revealStyle = (loaded: boolean): React.CSSProperties | undefined =>
|
|
67
|
-
revealImages
|
|
68
|
-
? {
|
|
69
|
-
filter: config.imageLoading.blur && !loaded ? 'blur(10px)' : 'blur(0px)',
|
|
70
|
-
transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`,
|
|
71
|
-
}
|
|
72
|
-
: undefined
|
|
73
|
-
|
|
74
|
-
// Auto-advance until the visitor takes over; never under reduced motion.
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
if (variant !== 'slider' || slides.length < 2 || prefersReducedMotion()) return
|
|
77
|
-
const timer = setInterval(() => {
|
|
78
|
-
if (!interacted.current) setIndex((i) => (i + 1) % slides.length)
|
|
79
|
-
}, 7000)
|
|
80
|
-
return () => clearInterval(timer)
|
|
81
|
-
}, [variant, slides.length])
|
|
82
|
-
|
|
83
|
-
const goTo = (i: number) => {
|
|
84
|
-
interacted.current = true
|
|
85
|
-
setIndex((i + slides.length) % slides.length)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (slides.length === 0) return null
|
|
89
|
-
|
|
90
|
-
if (variant === 'split') {
|
|
91
|
-
return (
|
|
92
|
-
<section data-testid={TID.hero} className="grid gap-px sm:grid-cols-2">
|
|
93
|
-
{slides.slice(0, 2).map((slide, i) => (
|
|
94
|
-
<Link
|
|
95
|
-
key={i}
|
|
96
|
-
to={slide.href ?? '/catalog'}
|
|
97
|
-
className="group relative block h-[42vh] min-h-[320px] overflow-hidden"
|
|
98
|
-
>
|
|
99
|
-
<SmoothImage
|
|
100
|
-
src={slideImage(slide, 900, 700)}
|
|
101
|
-
alt={slide.title}
|
|
102
|
-
className="h-full w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
|
103
|
-
/>
|
|
104
|
-
<div className="absolute inset-0 flex flex-col items-center justify-center bg-gradient-to-t from-black/65 via-black/25 to-black/10 p-6 text-center text-white">
|
|
105
|
-
<h2
|
|
106
|
-
className="sf-heading animate-fade-up text-3xl font-bold uppercase tracking-wide drop-shadow-md sm:text-4xl"
|
|
107
|
-
style={{ animationDelay: `${i * 150}ms` }}
|
|
108
|
-
>
|
|
109
|
-
{slide.title}
|
|
110
|
-
</h2>
|
|
111
|
-
{slide.subtitle && (
|
|
112
|
-
<p className="animate-fade-up mt-2 max-w-sm drop-shadow" style={{ animationDelay: `${i * 150 + 120}ms` }}>
|
|
113
|
-
{slide.subtitle}
|
|
114
|
-
</p>
|
|
115
|
-
)}
|
|
116
|
-
<span className="mt-4 inline-block border-b-2 border-white/0 pb-0.5 text-sm font-semibold uppercase tracking-wider opacity-0 transition-all duration-300 group-hover:border-white group-hover:opacity-100">
|
|
117
|
-
Explorar →
|
|
118
|
-
</span>
|
|
119
|
-
</div>
|
|
120
|
-
</Link>
|
|
121
|
-
))}
|
|
122
|
-
</section>
|
|
123
|
-
)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const active = slides[Math.min(index, slides.length - 1)]!
|
|
127
|
-
|
|
128
|
-
return (
|
|
129
|
-
<section data-testid={TID.hero} className={`relative w-full overflow-hidden ${hClass}`}>
|
|
130
|
-
{/* stacked slides, crossfade + Ken Burns on the active one */}
|
|
131
|
-
{slides.map((slide, i) => (
|
|
132
|
-
<img
|
|
133
|
-
key={i}
|
|
134
|
-
src={slideImage(slide)}
|
|
135
|
-
alt={slide.title}
|
|
136
|
-
aria-hidden={i !== index}
|
|
137
|
-
loading={i === 0 ? 'eager' : 'lazy'}
|
|
138
|
-
decoding="async"
|
|
139
|
-
onLoad={() => setLoadedSlides((current) => ({ ...current, [i]: true }))}
|
|
140
|
-
style={revealStyle(Boolean(loadedSlides[i]))}
|
|
141
|
-
className={`absolute inset-0 h-full w-full object-cover transition-opacity duration-700 ${
|
|
142
|
-
i === index && (!revealImages || loadedSlides[i]) ? 'animate-kenburns opacity-100' : 'opacity-0'
|
|
143
|
-
}`}
|
|
144
|
-
/>
|
|
145
|
-
))}
|
|
146
|
-
<div
|
|
147
|
-
className={`absolute inset-0 flex items-end p-6 sm:p-12 ${
|
|
148
|
-
active.boxed ? '' : 'bg-gradient-to-t from-black/60 via-black/20 to-transparent'
|
|
149
|
-
}`}
|
|
150
|
-
>
|
|
151
|
-
<div className="mx-auto w-full max-w-7xl">
|
|
152
|
-
<div className="max-w-xl">
|
|
153
|
-
<SlideCopy key={index} slide={active} active />
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
{variant === 'slider' && slides.length > 1 && (
|
|
159
|
-
<>
|
|
160
|
-
<button
|
|
161
|
-
type="button"
|
|
162
|
-
aria-label="Slide anterior"
|
|
163
|
-
data-testid={TID.heroPrev}
|
|
164
|
-
onClick={() => goTo(index - 1)}
|
|
165
|
-
className="absolute left-4 top-1/2 -translate-y-1/2 rounded-full bg-white/85 p-2.5 shadow transition-transform hover:scale-110 hover:bg-white"
|
|
166
|
-
>
|
|
167
|
-
<ChevronLeft className="h-5 w-5 text-gray-900" />
|
|
168
|
-
</button>
|
|
169
|
-
<button
|
|
170
|
-
type="button"
|
|
171
|
-
aria-label="Próximo slide"
|
|
172
|
-
data-testid={TID.heroNext}
|
|
173
|
-
onClick={() => goTo(index + 1)}
|
|
174
|
-
className="absolute right-4 top-1/2 -translate-y-1/2 rounded-full bg-white/85 p-2.5 shadow transition-transform hover:scale-110 hover:bg-white"
|
|
175
|
-
>
|
|
176
|
-
<ChevronRight className="h-5 w-5 text-gray-900" />
|
|
177
|
-
</button>
|
|
178
|
-
<div className="absolute bottom-4 left-1/2 flex -translate-x-1/2 gap-2">
|
|
179
|
-
{slides.map((_, i) => (
|
|
180
|
-
<button
|
|
181
|
-
key={i}
|
|
182
|
-
type="button"
|
|
183
|
-
aria-label={`Ir para o slide ${i + 1}`}
|
|
184
|
-
onClick={() => goTo(i)}
|
|
185
|
-
className={`h-2 rounded-full transition-all duration-300 ${i === index ? 'w-7 bg-white' : 'w-2 bg-white/60 hover:bg-white/80'}`}
|
|
186
|
-
/>
|
|
187
|
-
))}
|
|
188
|
-
</div>
|
|
189
|
-
</>
|
|
190
|
-
)}
|
|
191
|
-
</section>
|
|
192
|
-
)
|
|
193
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from 'react'
|
|
2
|
-
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
|
3
|
-
import { Link } from '../../router'
|
|
4
|
-
import { TID } from '../../testids'
|
|
5
|
-
import { prefersReducedMotion } from '../../motion'
|
|
6
|
-
import type { MediaCarouselItem } from '../../sections'
|
|
7
|
-
import { useStorefrontConfig } from '../../config'
|
|
8
|
-
|
|
9
|
-
export interface MediaCarouselProps {
|
|
10
|
-
items: MediaCarouselItem[]
|
|
11
|
-
height?: 'medium' | 'tall' | 'screen'
|
|
12
|
-
autoplayMs?: number
|
|
13
|
-
overlay?: 'dark' | 'soft' | 'none'
|
|
14
|
-
className?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const heightClass = {
|
|
18
|
-
medium: 'h-[58vh] min-h-[420px]',
|
|
19
|
-
tall: 'h-[74vh] min-h-[520px]',
|
|
20
|
-
screen: 'min-h-[calc(100vh-64px)]',
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const alignClass = {
|
|
24
|
-
start: 'items-start text-left',
|
|
25
|
-
center: 'items-center text-center',
|
|
26
|
-
end: 'items-end text-right',
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function MediaCarousel({
|
|
30
|
-
items,
|
|
31
|
-
height = 'tall',
|
|
32
|
-
autoplayMs = 7000,
|
|
33
|
-
overlay = 'dark',
|
|
34
|
-
className = '',
|
|
35
|
-
}: MediaCarouselProps) {
|
|
36
|
-
const [index, setIndex] = useState(0)
|
|
37
|
-
const [loadedItems, setLoadedItems] = useState<Record<number, boolean>>({})
|
|
38
|
-
const config = useStorefrontConfig()
|
|
39
|
-
const interacted = useRef(false)
|
|
40
|
-
const revealImages = config.imageLoading.mode !== 'none'
|
|
41
|
-
const revealStyle = (loaded: boolean): React.CSSProperties | undefined =>
|
|
42
|
-
revealImages
|
|
43
|
-
? {
|
|
44
|
-
filter: config.imageLoading.blur && !loaded ? 'blur(10px)' : 'blur(0px)',
|
|
45
|
-
transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`,
|
|
46
|
-
}
|
|
47
|
-
: undefined
|
|
48
|
-
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
if (items.length < 2 || autoplayMs <= 0 || prefersReducedMotion()) return
|
|
51
|
-
const timer = setInterval(() => {
|
|
52
|
-
if (!interacted.current) setIndex((i) => (i + 1) % items.length)
|
|
53
|
-
}, autoplayMs)
|
|
54
|
-
return () => clearInterval(timer)
|
|
55
|
-
}, [autoplayMs, items.length])
|
|
56
|
-
|
|
57
|
-
if (items.length === 0) return null
|
|
58
|
-
|
|
59
|
-
const active = items[Math.min(index, items.length - 1)]!
|
|
60
|
-
const goTo = (next: number) => {
|
|
61
|
-
interacted.current = true
|
|
62
|
-
setIndex((next + items.length) % items.length)
|
|
63
|
-
}
|
|
64
|
-
const overlayClass =
|
|
65
|
-
overlay === 'dark'
|
|
66
|
-
? 'bg-gradient-to-r from-black/70 via-black/25 to-black/25'
|
|
67
|
-
: overlay === 'soft'
|
|
68
|
-
? 'bg-gradient-to-t from-black/45 via-black/10 to-transparent'
|
|
69
|
-
: ''
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<section data-testid={TID.mediaCarousel} className={`relative isolate overflow-hidden ${heightClass[height]} ${className}`}>
|
|
73
|
-
{items.map((item, i) => (
|
|
74
|
-
<img
|
|
75
|
-
key={`${item.title}-${i}`}
|
|
76
|
-
src={item.image}
|
|
77
|
-
alt={item.imageAlt ?? item.title}
|
|
78
|
-
aria-hidden={i !== index}
|
|
79
|
-
loading={i === 0 ? 'eager' : 'lazy'}
|
|
80
|
-
decoding="async"
|
|
81
|
-
onLoad={() => setLoadedItems((current) => ({ ...current, [i]: true }))}
|
|
82
|
-
style={revealStyle(Boolean(loadedItems[i]))}
|
|
83
|
-
className={`absolute inset-0 -z-10 h-full w-full object-cover transition-opacity duration-700 ${
|
|
84
|
-
i === index && (!revealImages || loadedItems[i]) ? 'opacity-100' : 'opacity-0'
|
|
85
|
-
}`}
|
|
86
|
-
/>
|
|
87
|
-
))}
|
|
88
|
-
<div className={`absolute inset-0 -z-10 ${overlayClass}`} />
|
|
89
|
-
<div className={`flex h-full px-5 py-16 sm:px-10 lg:px-16 ${alignClass[active.align ?? 'start']}`}>
|
|
90
|
-
<div className="my-auto max-w-2xl text-white drop-shadow-md">
|
|
91
|
-
{active.eyebrow && (
|
|
92
|
-
<p className="mb-4 text-xs font-semibold uppercase tracking-[0.24em] text-white/80">{active.eyebrow}</p>
|
|
93
|
-
)}
|
|
94
|
-
<h1 className="sf-heading text-5xl font-normal leading-[0.95] sm:text-7xl lg:text-8xl">{active.title}</h1>
|
|
95
|
-
{active.subtitle && <p className="mt-5 max-w-xl text-base leading-8 text-white/88 sm:text-lg">{active.subtitle}</p>}
|
|
96
|
-
{active.cta && (
|
|
97
|
-
<Link
|
|
98
|
-
to={active.href ?? '/catalog'}
|
|
99
|
-
className="mt-8 inline-flex border border-white/75 px-6 py-3 text-xs font-semibold uppercase tracking-[0.18em] text-white transition hover:bg-white hover:text-black"
|
|
100
|
-
>
|
|
101
|
-
{active.cta}
|
|
102
|
-
</Link>
|
|
103
|
-
)}
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
{items.length > 1 && (
|
|
107
|
-
<>
|
|
108
|
-
<button
|
|
109
|
-
type="button"
|
|
110
|
-
aria-label="Previous slide"
|
|
111
|
-
data-testid={TID.mediaCarouselPrev}
|
|
112
|
-
onClick={() => goTo(index - 1)}
|
|
113
|
-
className="absolute left-4 top-1/2 inline-flex h-10 w-10 -translate-y-1/2 items-center justify-center border border-white/60 bg-black/20 text-white backdrop-blur transition hover:bg-white hover:text-black"
|
|
114
|
-
>
|
|
115
|
-
<ChevronLeft className="h-5 w-5" />
|
|
116
|
-
</button>
|
|
117
|
-
<button
|
|
118
|
-
type="button"
|
|
119
|
-
aria-label="Next slide"
|
|
120
|
-
data-testid={TID.mediaCarouselNext}
|
|
121
|
-
onClick={() => goTo(index + 1)}
|
|
122
|
-
className="absolute right-4 top-1/2 inline-flex h-10 w-10 -translate-y-1/2 items-center justify-center border border-white/60 bg-black/20 text-white backdrop-blur transition hover:bg-white hover:text-black"
|
|
123
|
-
>
|
|
124
|
-
<ChevronRight className="h-5 w-5" />
|
|
125
|
-
</button>
|
|
126
|
-
<div className="absolute bottom-5 left-1/2 flex -translate-x-1/2 gap-2">
|
|
127
|
-
{items.map((item, i) => (
|
|
128
|
-
<button
|
|
129
|
-
key={`${item.title}-dot-${i}`}
|
|
130
|
-
type="button"
|
|
131
|
-
aria-label={`Go to slide ${i + 1}`}
|
|
132
|
-
onClick={() => goTo(i)}
|
|
133
|
-
className={`h-2 rounded-full transition-all ${i === index ? 'w-8 bg-white' : 'w-2 bg-white/55 hover:bg-white'}`}
|
|
134
|
-
/>
|
|
135
|
-
))}
|
|
136
|
-
</div>
|
|
137
|
-
</>
|
|
138
|
-
)}
|
|
139
|
-
</section>
|
|
140
|
-
)
|
|
141
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import { getLucideIcon } from '@fayz-ai/ui/icons'
|
|
3
|
-
import { bannerPlaceholder } from '../../sections'
|
|
4
|
-
import { Reveal } from '../../motion'
|
|
5
|
-
import { Link } from '../../router'
|
|
6
|
-
import { TID } from '../../testids'
|
|
7
|
-
import { SmoothImage } from '../SmoothImage'
|
|
8
|
-
|
|
9
|
-
function Icon({ name, className }: { name: string; className?: string }) {
|
|
10
|
-
const C = getLucideIcon(name)
|
|
11
|
-
return C ? <C className={className} /> : null
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function BenefitsRow({ items }: { items: Array<{ icon: string; title: string; text?: string }> }) {
|
|
15
|
-
return (
|
|
16
|
-
<section data-testid={TID.benefits} className="border-y bg-card">
|
|
17
|
-
<div className="mx-auto grid max-w-7xl grid-cols-1 gap-6 px-4 py-9 sm:grid-cols-3 sm:px-6">
|
|
18
|
-
{items.slice(0, 4).map((item, i) => (
|
|
19
|
-
<Reveal key={i} delay={i * 120} className="flex items-center justify-center gap-3 text-center sm:text-left">
|
|
20
|
-
<span className="flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-primary/10">
|
|
21
|
-
<Icon name={item.icon} className="h-6 w-6 text-primary" />
|
|
22
|
-
</span>
|
|
23
|
-
<div>
|
|
24
|
-
<p className="font-semibold">{item.title}</p>
|
|
25
|
-
{item.text && <p className="text-sm text-muted-foreground">{item.text}</p>}
|
|
26
|
-
</div>
|
|
27
|
-
</Reveal>
|
|
28
|
-
))}
|
|
29
|
-
</div>
|
|
30
|
-
</section>
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function PromoBanner({
|
|
35
|
-
title,
|
|
36
|
-
subtitle,
|
|
37
|
-
eyebrow,
|
|
38
|
-
cta,
|
|
39
|
-
href,
|
|
40
|
-
image,
|
|
41
|
-
hue = 160,
|
|
42
|
-
}: {
|
|
43
|
-
title: string
|
|
44
|
-
subtitle?: string
|
|
45
|
-
eyebrow?: string
|
|
46
|
-
cta?: string
|
|
47
|
-
href?: string
|
|
48
|
-
image?: string
|
|
49
|
-
hue?: number
|
|
50
|
-
}) {
|
|
51
|
-
return (
|
|
52
|
-
<section data-testid={TID.promoBanner} className="group relative my-12 overflow-hidden">
|
|
53
|
-
<SmoothImage
|
|
54
|
-
src={image ?? bannerPlaceholder(title, hue, hue + 50, 1600, 420)}
|
|
55
|
-
alt={title}
|
|
56
|
-
className="h-80 w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
|
57
|
-
/>
|
|
58
|
-
<div className="absolute inset-0 flex flex-col items-center justify-center bg-gradient-to-t from-black/65 via-black/35 to-black/20 p-6 text-center text-white">
|
|
59
|
-
<Reveal>
|
|
60
|
-
{eyebrow && (
|
|
61
|
-
<p className="mb-2 text-xs font-semibold uppercase tracking-[0.25em] text-white/80">{eyebrow}</p>
|
|
62
|
-
)}
|
|
63
|
-
<h2 className="sf-heading text-3xl font-bold drop-shadow sm:text-5xl">{title}</h2>
|
|
64
|
-
{subtitle && <p className="mt-3 max-w-lg text-white/90">{subtitle}</p>}
|
|
65
|
-
{cta && (
|
|
66
|
-
<Link
|
|
67
|
-
to={href ?? '/catalog'}
|
|
68
|
-
className="sf-cta mt-6 inline-block bg-white px-8 py-3 font-semibold text-gray-900 shadow-lg transition-all hover:-translate-y-0.5 hover:shadow-xl"
|
|
69
|
-
style={{ borderRadius: 'var(--sf-radius-button)' }}
|
|
70
|
-
>
|
|
71
|
-
{cta}
|
|
72
|
-
</Link>
|
|
73
|
-
)}
|
|
74
|
-
</Reveal>
|
|
75
|
-
</div>
|
|
76
|
-
</section>
|
|
77
|
-
)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function ManifestoBlock({ title, text }: { title?: string; text: string }) {
|
|
81
|
-
return (
|
|
82
|
-
<section data-testid={TID.manifesto} className="mx-auto max-w-3xl px-4 py-14 text-center sm:px-6">
|
|
83
|
-
{title && <h2 className="sf-heading mb-5 text-2xl font-bold">{title}</h2>}
|
|
84
|
-
<p className="sf-heading text-xl leading-relaxed text-muted-foreground sm:text-2xl">{text}</p>
|
|
85
|
-
</section>
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function Testimonials({ title, items }: { title?: string; items: Array<{ quote: string; author: string }> }) {
|
|
90
|
-
return (
|
|
91
|
-
<section data-testid={TID.testimonials} className="bg-muted/50 py-14">
|
|
92
|
-
<div className="mx-auto max-w-7xl px-4 sm:px-6">
|
|
93
|
-
{title && (
|
|
94
|
-
<Reveal>
|
|
95
|
-
<h2 className="sf-heading mb-10 text-center text-3xl font-bold">{title}</h2>
|
|
96
|
-
</Reveal>
|
|
97
|
-
)}
|
|
98
|
-
<div className="grid gap-6 md:grid-cols-3">
|
|
99
|
-
{items.slice(0, 3).map((t, i) => (
|
|
100
|
-
<Reveal key={i} delay={i * 130}>
|
|
101
|
-
<figure
|
|
102
|
-
className="h-full bg-card p-7 shadow-sm transition-shadow hover:shadow-md"
|
|
103
|
-
style={{ borderRadius: 'var(--sf-radius-card)' }}
|
|
104
|
-
>
|
|
105
|
-
<div className="mb-3 text-primary" aria-hidden>★★★★★</div>
|
|
106
|
-
<blockquote className="leading-relaxed">“{t.quote}”</blockquote>
|
|
107
|
-
<figcaption className="mt-4 text-sm font-semibold text-muted-foreground">— {t.author}</figcaption>
|
|
108
|
-
</figure>
|
|
109
|
-
</Reveal>
|
|
110
|
-
))}
|
|
111
|
-
</div>
|
|
112
|
-
</div>
|
|
113
|
-
</section>
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function NewsletterBand({ title, subtitle }: { title?: string; subtitle?: string }) {
|
|
118
|
-
const [email, setEmail] = useState('')
|
|
119
|
-
const [done, setDone] = useState(false)
|
|
120
|
-
return (
|
|
121
|
-
<section data-testid={TID.newsletter} className="bg-primary py-16 text-primary-foreground">
|
|
122
|
-
<Reveal className="mx-auto max-w-xl px-4 text-center sm:px-6">
|
|
123
|
-
<h2 className="sf-heading text-3xl font-bold">{title ?? 'Receba nossas novidades'}</h2>
|
|
124
|
-
{subtitle && <p className="mt-2 opacity-90">{subtitle}</p>}
|
|
125
|
-
{done ? (
|
|
126
|
-
<p data-testid={TID.newsletterDone} className="mt-6 font-medium">Inscrição confirmada! 🎉</p>
|
|
127
|
-
) : (
|
|
128
|
-
<form
|
|
129
|
-
className="mt-6 flex gap-2"
|
|
130
|
-
onSubmit={(e) => {
|
|
131
|
-
e.preventDefault()
|
|
132
|
-
if (email.trim()) setDone(true)
|
|
133
|
-
}}
|
|
134
|
-
>
|
|
135
|
-
<input
|
|
136
|
-
data-testid={TID.newsletterEmail}
|
|
137
|
-
type="email"
|
|
138
|
-
required
|
|
139
|
-
placeholder="Seu e-mail"
|
|
140
|
-
value={email}
|
|
141
|
-
onChange={(e) => setEmail(e.target.value)}
|
|
142
|
-
className="flex-1 border-0 bg-white/95 px-4 py-3 text-sm text-gray-900 outline-none"
|
|
143
|
-
style={{ borderRadius: 'var(--sf-radius-input)' }}
|
|
144
|
-
/>
|
|
145
|
-
<button
|
|
146
|
-
type="submit"
|
|
147
|
-
data-testid={TID.newsletterSubmit}
|
|
148
|
-
className="sf-cta bg-foreground px-6 py-3 text-sm font-semibold text-background"
|
|
149
|
-
style={{ borderRadius: 'var(--sf-radius-button)' }}
|
|
150
|
-
>
|
|
151
|
-
Assinar
|
|
152
|
-
</button>
|
|
153
|
-
</form>
|
|
154
|
-
)}
|
|
155
|
-
</Reveal>
|
|
156
|
-
</section>
|
|
157
|
-
)
|
|
158
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react'
|
|
2
|
-
import { useProducts } from '../../hooks/useProducts'
|
|
3
|
-
import { getStorefrontComponents, useStorefrontConfig } from '../../config'
|
|
4
|
-
import { useStorefrontActions } from '../../hooks/useStorefront'
|
|
5
|
-
import { ProductCard } from '../ProductCard'
|
|
6
|
-
import { Reveal } from '../../motion'
|
|
7
|
-
import { Link } from '../../router'
|
|
8
|
-
import { TID } from '../../testids'
|
|
9
|
-
|
|
10
|
-
export function ProductRail({
|
|
11
|
-
title,
|
|
12
|
-
subtitle,
|
|
13
|
-
eyebrow,
|
|
14
|
-
filter = 'all',
|
|
15
|
-
limit = 4,
|
|
16
|
-
}: {
|
|
17
|
-
title: string
|
|
18
|
-
subtitle?: string
|
|
19
|
-
eyebrow?: string
|
|
20
|
-
filter?: 'sale' | 'new' | 'all'
|
|
21
|
-
limit?: number
|
|
22
|
-
}) {
|
|
23
|
-
const config = useStorefrontConfig()
|
|
24
|
-
const actions = useStorefrontActions()
|
|
25
|
-
const components = getStorefrontComponents(config)
|
|
26
|
-
const ProductCardComponent = components.ProductCard ?? ProductCard
|
|
27
|
-
const { products, loading } = useProducts(
|
|
28
|
-
filter === 'new'
|
|
29
|
-
? { status: 'active', orderBy: 'created_at', order: 'desc' }
|
|
30
|
-
: { status: 'active' },
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
const visible = useMemo(() => {
|
|
34
|
-
let list = products
|
|
35
|
-
if (filter === 'sale') list = list.filter((p) => p.compareAtPrice != null && p.compareAtPrice > p.price)
|
|
36
|
-
return list.slice(0, limit)
|
|
37
|
-
}, [products, filter, limit])
|
|
38
|
-
|
|
39
|
-
if (!loading && visible.length === 0) return null
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<section data-testid={TID.productRail} className="mx-auto max-w-7xl px-4 py-14 sm:px-6">
|
|
43
|
-
<Reveal className="mb-10 text-center">
|
|
44
|
-
{eyebrow && (
|
|
45
|
-
<p className="mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-primary">{eyebrow}</p>
|
|
46
|
-
)}
|
|
47
|
-
<h2 className="sf-heading text-3xl font-bold tracking-tight">{title}</h2>
|
|
48
|
-
{subtitle && <p className="mt-2 text-muted-foreground">{subtitle}</p>}
|
|
49
|
-
</Reveal>
|
|
50
|
-
{loading ? (
|
|
51
|
-
<div className="grid grid-cols-2 gap-5 md:grid-cols-4">
|
|
52
|
-
{Array.from({ length: limit }, (_, i) => (
|
|
53
|
-
<div key={i} className="aspect-[3/4] animate-pulse bg-muted" style={{ borderRadius: 'var(--sf-radius-card)' }} />
|
|
54
|
-
))}
|
|
55
|
-
</div>
|
|
56
|
-
) : (
|
|
57
|
-
<div className="grid grid-cols-2 gap-5 md:grid-cols-4">
|
|
58
|
-
{visible.map((p, i) => (
|
|
59
|
-
<Reveal key={p.id} delay={i * 90}>
|
|
60
|
-
<ProductCardComponent
|
|
61
|
-
product={p}
|
|
62
|
-
config={config}
|
|
63
|
-
commerceMode={config.commerceMode}
|
|
64
|
-
actions={actions}
|
|
65
|
-
/>
|
|
66
|
-
</Reveal>
|
|
67
|
-
))}
|
|
68
|
-
</div>
|
|
69
|
-
)}
|
|
70
|
-
<Reveal className="mt-10 text-center" delay={200}>
|
|
71
|
-
<Link
|
|
72
|
-
to={config.catalogPath}
|
|
73
|
-
className="sf-cta inline-block border border-foreground/30 px-7 py-2.5 text-sm font-semibold transition-all hover:-translate-y-0.5 hover:bg-foreground hover:text-background hover:shadow"
|
|
74
|
-
style={{ borderRadius: 'var(--sf-radius-button)' }}
|
|
75
|
-
>
|
|
76
|
-
Ver tudo
|
|
77
|
-
</Link>
|
|
78
|
-
</Reveal>
|
|
79
|
-
</section>
|
|
80
|
-
)
|
|
81
|
-
}
|