@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,477 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import { CreditCard, LogOut, Mail, Menu, Package, Phone, Search, ShoppingBag, User, UserCircle, X } from 'lucide-react'
|
|
3
|
-
import { signOutCustomer } from '../auth'
|
|
4
|
-
import { useCartStore, selectCount } from '../stores/cart.store'
|
|
5
|
-
import { useCatalogStore } from '../stores/catalog.store'
|
|
6
|
-
import { useSessionStore } from '../stores/session.store'
|
|
7
|
-
import { useCategories } from '../hooks/useCategories'
|
|
8
|
-
import { useStorefrontConfig } from '../config'
|
|
9
|
-
import { useScrolled, usePopOnChange } from '../motion'
|
|
10
|
-
import { Link, navigateTo, useHashPath } from '../router'
|
|
11
|
-
import { TID } from '../testids'
|
|
12
|
-
|
|
13
|
-
// Header tokens come from the theme (--sf-header-bg/fg) so dark headers
|
|
14
|
-
// (Brasília pattern) work without component changes. Dividers derive from the
|
|
15
|
-
// header FOREGROUND (not the page --border token, which is light and shows as
|
|
16
|
-
// white lines on dark headers).
|
|
17
|
-
const headerStyle: React.CSSProperties = {
|
|
18
|
-
backgroundColor: 'hsl(var(--sf-header-bg, var(--background)))',
|
|
19
|
-
color: 'hsl(var(--sf-header-fg, var(--foreground)))',
|
|
20
|
-
borderColor: 'hsl(var(--sf-header-fg, var(--foreground)) / 0.14)',
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const headerDivider: React.CSSProperties = {
|
|
24
|
-
borderColor: 'hsl(var(--sf-header-fg, var(--foreground)) / 0.14)',
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const ANNOUNCEMENT_KEY = 'fayz.storefront.announcement.dismissed'
|
|
28
|
-
|
|
29
|
-
function AnnouncementBar() {
|
|
30
|
-
const config = useStorefrontConfig()
|
|
31
|
-
const [dismissed, setDismissed] = useState(() => {
|
|
32
|
-
try {
|
|
33
|
-
return localStorage.getItem(ANNOUNCEMENT_KEY) === config.announcement
|
|
34
|
-
} catch {
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
if (!config.announcement || dismissed) return null
|
|
39
|
-
|
|
40
|
-
const dismiss = () => {
|
|
41
|
-
setDismissed(true)
|
|
42
|
-
try {
|
|
43
|
-
localStorage.setItem(ANNOUNCEMENT_KEY, config.announcement ?? '')
|
|
44
|
-
} catch {
|
|
45
|
-
/* storage unavailable — dismiss for this session only */
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div
|
|
51
|
-
data-testid={TID.announcementBar}
|
|
52
|
-
className="relative px-4 py-2 text-center text-xs font-semibold tracking-wide"
|
|
53
|
-
style={{
|
|
54
|
-
backgroundColor: 'hsl(var(--sf-announcement-bg, var(--primary)))',
|
|
55
|
-
color: 'hsl(var(--sf-announcement-fg, var(--primary-foreground)))',
|
|
56
|
-
}}
|
|
57
|
-
>
|
|
58
|
-
{config.announcement}
|
|
59
|
-
<button
|
|
60
|
-
type="button"
|
|
61
|
-
aria-label="Fechar aviso"
|
|
62
|
-
onClick={dismiss}
|
|
63
|
-
className="absolute right-2 top-1/2 -translate-y-1/2 rounded-full p-1 opacity-70 transition-opacity hover:opacity-100"
|
|
64
|
-
>
|
|
65
|
-
<X className="h-3.5 w-3.5" />
|
|
66
|
-
</button>
|
|
67
|
-
</div>
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Slim utility strip (Flex pattern): contact on the left, social on the right. */
|
|
72
|
-
function UtilityBar() {
|
|
73
|
-
const config = useStorefrontConfig()
|
|
74
|
-
const contact = config.footer?.contact
|
|
75
|
-
const social = config.footer?.social
|
|
76
|
-
if (!contact?.phone && !contact?.email && !social?.length) return null
|
|
77
|
-
return (
|
|
78
|
-
<div className="hidden border-b text-xs opacity-80 md:block" style={headerDivider}>
|
|
79
|
-
<div className="mx-auto flex h-8 max-w-7xl items-center gap-5 px-4 sm:px-6">
|
|
80
|
-
{contact?.phone && (
|
|
81
|
-
<span className="inline-flex items-center gap-1.5">
|
|
82
|
-
<Phone className="h-3 w-3" /> {contact.phone}
|
|
83
|
-
</span>
|
|
84
|
-
)}
|
|
85
|
-
{contact?.email && (
|
|
86
|
-
<span className="inline-flex items-center gap-1.5">
|
|
87
|
-
<Mail className="h-3 w-3" /> {contact.email}
|
|
88
|
-
</span>
|
|
89
|
-
)}
|
|
90
|
-
<span className="ml-auto flex items-center gap-4">
|
|
91
|
-
{social?.map((s) => (
|
|
92
|
-
<a key={s.href} href={s.href} target="_blank" rel="noreferrer" className="hover:underline">
|
|
93
|
-
{s.label}
|
|
94
|
-
</a>
|
|
95
|
-
))}
|
|
96
|
-
</span>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function SearchInput({ className, iconOnly = false }: { className?: string; iconOnly?: boolean }) {
|
|
103
|
-
const config = useStorefrontConfig()
|
|
104
|
-
const search = useCatalogStore((s) => s.search)
|
|
105
|
-
const setSearch = useCatalogStore((s) => s.setSearch)
|
|
106
|
-
const path = useHashPath()
|
|
107
|
-
const [open, setOpen] = useState(false)
|
|
108
|
-
|
|
109
|
-
// Icon-only: a compact search button that expands into the input on click and
|
|
110
|
-
// collapses again on Escape / blur-when-empty.
|
|
111
|
-
if (iconOnly && !open) {
|
|
112
|
-
return (
|
|
113
|
-
<button
|
|
114
|
-
type="button"
|
|
115
|
-
aria-label="Buscar produtos"
|
|
116
|
-
data-testid={TID.headerSearchToggle}
|
|
117
|
-
onClick={() => setOpen(true)}
|
|
118
|
-
className="rounded-full p-2.5 transition-opacity hover:opacity-70"
|
|
119
|
-
>
|
|
120
|
-
<Search className="h-5 w-5" />
|
|
121
|
-
</button>
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return (
|
|
126
|
-
<div className={`relative ${iconOnly ? 'w-full max-w-xs' : (className ?? '')}`}>
|
|
127
|
-
<Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 opacity-60" />
|
|
128
|
-
<input
|
|
129
|
-
data-testid={TID.headerSearch}
|
|
130
|
-
type="search"
|
|
131
|
-
placeholder="Buscar produtos..."
|
|
132
|
-
value={search}
|
|
133
|
-
autoFocus={iconOnly}
|
|
134
|
-
onChange={(e) => {
|
|
135
|
-
setSearch(e.target.value)
|
|
136
|
-
if (path !== config.catalogPath) navigateTo(config.catalogPath)
|
|
137
|
-
}}
|
|
138
|
-
onKeyDown={(e) => {
|
|
139
|
-
if (e.key === 'Escape' && iconOnly) setOpen(false)
|
|
140
|
-
}}
|
|
141
|
-
onBlur={() => {
|
|
142
|
-
if (iconOnly && !search) setOpen(false)
|
|
143
|
-
}}
|
|
144
|
-
className="w-full border bg-white/10 py-2 pl-9 pr-4 text-sm outline-none transition-colors placeholder:opacity-60 focus:border-primary focus:bg-white focus:text-gray-900"
|
|
145
|
-
style={{
|
|
146
|
-
borderRadius: 'var(--sf-radius-input)',
|
|
147
|
-
borderColor: 'hsl(var(--sf-header-fg, var(--foreground)) / 0.25)',
|
|
148
|
-
}}
|
|
149
|
-
/>
|
|
150
|
-
</div>
|
|
151
|
-
)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function HeaderActions({ onMenuClick }: { onMenuClick?: () => void }) {
|
|
155
|
-
const config = useStorefrontConfig()
|
|
156
|
-
const count = useCartStore(selectCount)
|
|
157
|
-
const openDrawer = useCartStore((s) => s.openDrawer)
|
|
158
|
-
const session = useSessionStore()
|
|
159
|
-
const [accountOpen, setAccountOpen] = useState(false)
|
|
160
|
-
const pop = usePopOnChange(count)
|
|
161
|
-
const firstName = session.name?.split(/\s+/)[0]
|
|
162
|
-
const initial = (firstName ?? session.email ?? 'C').slice(0, 1).toUpperCase()
|
|
163
|
-
return (
|
|
164
|
-
<nav className="flex items-center gap-1">
|
|
165
|
-
{config.features.accounts && (
|
|
166
|
-
<div className="relative">
|
|
167
|
-
{session.email ? (
|
|
168
|
-
<button
|
|
169
|
-
type="button"
|
|
170
|
-
data-testid={TID.accountLink}
|
|
171
|
-
aria-label="Abrir menu da conta"
|
|
172
|
-
aria-expanded={accountOpen}
|
|
173
|
-
onClick={() => setAccountOpen((open) => !open)}
|
|
174
|
-
className="flex items-center gap-1.5 rounded-full p-1.5 transition-opacity hover:opacity-80"
|
|
175
|
-
>
|
|
176
|
-
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-sm font-bold text-primary-foreground">
|
|
177
|
-
{initial}
|
|
178
|
-
</span>
|
|
179
|
-
{firstName && (
|
|
180
|
-
<span data-testid={TID.accountName} className="hidden max-w-24 truncate text-sm font-medium lg:inline">
|
|
181
|
-
{firstName}
|
|
182
|
-
</span>
|
|
183
|
-
)}
|
|
184
|
-
</button>
|
|
185
|
-
) : (
|
|
186
|
-
<Link
|
|
187
|
-
to="/account"
|
|
188
|
-
data-testid={TID.accountLink}
|
|
189
|
-
aria-label="Minhas compras"
|
|
190
|
-
className="flex items-center gap-1.5 rounded-full p-2.5 transition-opacity hover:opacity-70"
|
|
191
|
-
>
|
|
192
|
-
<User className="h-5 w-5" />
|
|
193
|
-
</Link>
|
|
194
|
-
)}
|
|
195
|
-
{accountOpen && session.email && (
|
|
196
|
-
<div
|
|
197
|
-
data-testid={TID.accountDropdown}
|
|
198
|
-
className="absolute right-0 top-full z-50 mt-3 w-72 overflow-hidden rounded-xl border bg-background text-foreground shadow-xl"
|
|
199
|
-
>
|
|
200
|
-
<div className="border-b p-4">
|
|
201
|
-
<p className="font-semibold">{session.name || firstName || 'Cliente'}</p>
|
|
202
|
-
<p className="truncate text-sm text-muted-foreground">{session.email}</p>
|
|
203
|
-
</div>
|
|
204
|
-
<div className="p-2">
|
|
205
|
-
<Link to="/account" className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold hover:bg-muted">
|
|
206
|
-
<UserCircle className="h-4 w-4" />
|
|
207
|
-
Perfil
|
|
208
|
-
</Link>
|
|
209
|
-
<Link to="/account" className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold hover:bg-muted">
|
|
210
|
-
<Package className="h-4 w-4" />
|
|
211
|
-
Meus pedidos
|
|
212
|
-
</Link>
|
|
213
|
-
<Link to="/account" className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold hover:bg-muted">
|
|
214
|
-
<CreditCard className="h-4 w-4" />
|
|
215
|
-
Pagamentos
|
|
216
|
-
</Link>
|
|
217
|
-
<button
|
|
218
|
-
type="button"
|
|
219
|
-
onClick={() => {
|
|
220
|
-
setAccountOpen(false)
|
|
221
|
-
void signOutCustomer()
|
|
222
|
-
}}
|
|
223
|
-
className="flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-semibold hover:bg-muted"
|
|
224
|
-
>
|
|
225
|
-
<LogOut className="h-4 w-4" />
|
|
226
|
-
Sair
|
|
227
|
-
</button>
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
)}
|
|
231
|
-
</div>
|
|
232
|
-
)}
|
|
233
|
-
{config.features.cart && (
|
|
234
|
-
<button
|
|
235
|
-
type="button"
|
|
236
|
-
data-testid={TID.cartButton}
|
|
237
|
-
aria-label="Carrinho"
|
|
238
|
-
onClick={openDrawer}
|
|
239
|
-
className="relative rounded-full p-2.5 transition-opacity hover:opacity-70"
|
|
240
|
-
>
|
|
241
|
-
<ShoppingBag className="h-5 w-5" />
|
|
242
|
-
{count > 0 && (
|
|
243
|
-
<span
|
|
244
|
-
data-testid={TID.cartCount}
|
|
245
|
-
className={`absolute -right-0.5 -top-0.5 flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1 text-xs font-semibold text-primary-foreground ${
|
|
246
|
-
pop ? 'animate-badge-pop' : ''
|
|
247
|
-
}`}
|
|
248
|
-
>
|
|
249
|
-
{count}
|
|
250
|
-
</span>
|
|
251
|
-
)}
|
|
252
|
-
</button>
|
|
253
|
-
)}
|
|
254
|
-
{onMenuClick && (
|
|
255
|
-
<button
|
|
256
|
-
type="button"
|
|
257
|
-
aria-label="Abrir menu"
|
|
258
|
-
onClick={onMenuClick}
|
|
259
|
-
className="relative rounded-full p-2.5 transition-opacity hover:opacity-70 md:hidden"
|
|
260
|
-
>
|
|
261
|
-
<Menu className="h-5 w-5" />
|
|
262
|
-
</button>
|
|
263
|
-
)}
|
|
264
|
-
</nav>
|
|
265
|
-
)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function NavLinks({ className }: { className?: string }) {
|
|
269
|
-
const config = useStorefrontConfig()
|
|
270
|
-
const { categories } = useCategories()
|
|
271
|
-
const setCategoryId = useCatalogStore((s) => s.setCategoryId)
|
|
272
|
-
const showCategories = config.theme?.header?.showCategories !== false
|
|
273
|
-
|
|
274
|
-
const goCategory = (categoryId: string) => {
|
|
275
|
-
useCatalogStore.getState().reset()
|
|
276
|
-
setCategoryId(categoryId)
|
|
277
|
-
navigateTo(config.catalogPath)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return (
|
|
281
|
-
<nav className={`flex min-w-max items-center gap-6 ${className ?? ''}`}>
|
|
282
|
-
{config.nav.map((link) => (
|
|
283
|
-
<Link
|
|
284
|
-
key={link.to}
|
|
285
|
-
to={link.to}
|
|
286
|
-
data-testid={TID.navLink(link.label)}
|
|
287
|
-
className="sf-nav-link text-sm font-medium transition-opacity hover:opacity-70"
|
|
288
|
-
>
|
|
289
|
-
{link.label}
|
|
290
|
-
</Link>
|
|
291
|
-
))}
|
|
292
|
-
{showCategories &&
|
|
293
|
-
categories.slice(0, 6).map((c) => (
|
|
294
|
-
<button
|
|
295
|
-
key={c.id}
|
|
296
|
-
type="button"
|
|
297
|
-
data-testid={TID.navCategory(c.slug)}
|
|
298
|
-
onClick={() => goCategory(c.id)}
|
|
299
|
-
className="sf-nav-link group relative hidden text-sm font-medium transition-opacity hover:opacity-80 lg:inline"
|
|
300
|
-
>
|
|
301
|
-
{c.name}
|
|
302
|
-
<span className="absolute -bottom-1 left-0 h-0.5 w-0 bg-primary transition-all duration-300 group-hover:w-full" />
|
|
303
|
-
</button>
|
|
304
|
-
))}
|
|
305
|
-
</nav>
|
|
306
|
-
)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function MobileMenuDrawer({ open, onClose }: { open: boolean; onClose: () => void }) {
|
|
310
|
-
const config = useStorefrontConfig()
|
|
311
|
-
const { categories } = useCategories()
|
|
312
|
-
const setCategoryId = useCatalogStore((s) => s.setCategoryId)
|
|
313
|
-
const showCategories = config.theme?.header?.showCategories !== false
|
|
314
|
-
|
|
315
|
-
const goCategory = (categoryId: string) => {
|
|
316
|
-
useCatalogStore.getState().reset()
|
|
317
|
-
setCategoryId(categoryId)
|
|
318
|
-
navigateTo(config.catalogPath)
|
|
319
|
-
onClose()
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// a11y: close on Escape
|
|
323
|
-
React.useEffect(() => {
|
|
324
|
-
if (!open) return
|
|
325
|
-
const onKey = (e: KeyboardEvent) => {
|
|
326
|
-
if (e.key === 'Escape') onClose()
|
|
327
|
-
}
|
|
328
|
-
document.addEventListener('keydown', onKey)
|
|
329
|
-
return () => document.removeEventListener('keydown', onKey)
|
|
330
|
-
}, [open, onClose])
|
|
331
|
-
|
|
332
|
-
if (!open) return null
|
|
333
|
-
|
|
334
|
-
return (
|
|
335
|
-
<div className="fixed inset-0 z-50 md:hidden">
|
|
336
|
-
<div
|
|
337
|
-
className="absolute inset-0 animate-fade-in bg-black/40"
|
|
338
|
-
aria-hidden
|
|
339
|
-
onClick={onClose}
|
|
340
|
-
/>
|
|
341
|
-
<div
|
|
342
|
-
data-testid="mobile-menu-drawer"
|
|
343
|
-
role="dialog"
|
|
344
|
-
aria-label="Menu Principal"
|
|
345
|
-
className="absolute right-0 top-0 flex h-full w-full max-w-[280px] animate-slide-in-from-right flex-col bg-background shadow-2xl"
|
|
346
|
-
>
|
|
347
|
-
<div className="flex items-center justify-between border-b px-5 py-4">
|
|
348
|
-
<h2 className="text-lg font-semibold">Menu</h2>
|
|
349
|
-
<button
|
|
350
|
-
type="button"
|
|
351
|
-
aria-label="Fechar menu"
|
|
352
|
-
onClick={onClose}
|
|
353
|
-
className="rounded-full p-1.5 hover:bg-muted"
|
|
354
|
-
>
|
|
355
|
-
<X className="h-5 w-5" />
|
|
356
|
-
</button>
|
|
357
|
-
</div>
|
|
358
|
-
<div className="flex-1 overflow-y-auto p-5">
|
|
359
|
-
<nav className="flex flex-col gap-4">
|
|
360
|
-
{config.nav.map((link) => (
|
|
361
|
-
<Link
|
|
362
|
-
key={link.to}
|
|
363
|
-
to={link.to}
|
|
364
|
-
onClick={onClose}
|
|
365
|
-
className="text-base font-semibold transition-opacity hover:opacity-70"
|
|
366
|
-
>
|
|
367
|
-
{link.label}
|
|
368
|
-
</Link>
|
|
369
|
-
))}
|
|
370
|
-
{showCategories && categories.length > 0 && (
|
|
371
|
-
<>
|
|
372
|
-
<div className="my-2 border-t" />
|
|
373
|
-
<div className="mb-2 text-xs font-bold uppercase tracking-wider text-muted-foreground">
|
|
374
|
-
Categorias
|
|
375
|
-
</div>
|
|
376
|
-
{categories.map((c) => (
|
|
377
|
-
<button
|
|
378
|
-
key={c.id}
|
|
379
|
-
type="button"
|
|
380
|
-
onClick={() => goCategory(c.id)}
|
|
381
|
-
className="text-left text-sm font-medium transition-opacity hover:opacity-80"
|
|
382
|
-
>
|
|
383
|
-
{c.name}
|
|
384
|
-
</button>
|
|
385
|
-
))}
|
|
386
|
-
</>
|
|
387
|
-
)}
|
|
388
|
-
</nav>
|
|
389
|
-
</div>
|
|
390
|
-
</div>
|
|
391
|
-
</div>
|
|
392
|
-
)
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
export function StorefrontHeader() {
|
|
396
|
-
const config = useStorefrontConfig()
|
|
397
|
-
const variant = config.theme?.header?.variant ?? 'classic'
|
|
398
|
-
const scrolled = useScrolled()
|
|
399
|
-
const showSearch = config.theme?.header?.showSearch !== false
|
|
400
|
-
const iconSearch = config.theme?.header?.searchStyle === 'icon'
|
|
401
|
-
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
402
|
-
const logo = (
|
|
403
|
-
<Link to="/" className="sf-heading shrink-0 text-xl font-bold tracking-tight">
|
|
404
|
-
{config.logo ?? config.name}
|
|
405
|
-
</Link>
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
return (
|
|
409
|
-
<header
|
|
410
|
-
className={`sticky top-0 z-40 border-b transition-shadow duration-300 ${scrolled ? 'shadow-md' : ''}`}
|
|
411
|
-
style={headerStyle}
|
|
412
|
-
>
|
|
413
|
-
<AnnouncementBar />
|
|
414
|
-
<UtilityBar />
|
|
415
|
-
|
|
416
|
-
{variant === 'centered' ? (
|
|
417
|
-
// Rio/Flex pattern: centered logo row, nav row below
|
|
418
|
-
<>
|
|
419
|
-
<div className="mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center px-4 sm:px-6">
|
|
420
|
-
{showSearch ? <SearchInput iconOnly={iconSearch} className="hidden w-full max-w-xs sm:block" /> : <div />}
|
|
421
|
-
<div className="text-center">{logo}</div>
|
|
422
|
-
<div className="justify-self-end">
|
|
423
|
-
<HeaderActions />
|
|
424
|
-
</div>
|
|
425
|
-
</div>
|
|
426
|
-
<div className="flex overflow-x-auto border-t py-2.5 sm:justify-center" style={headerDivider}>
|
|
427
|
-
<NavLinks />
|
|
428
|
-
</div>
|
|
429
|
-
</>
|
|
430
|
-
) : variant === 'search' ? (
|
|
431
|
-
// Brasília pattern: prominent search left, logo center, actions right; nav row below
|
|
432
|
-
<>
|
|
433
|
-
<div className="mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center gap-6 px-4 sm:px-6">
|
|
434
|
-
{showSearch ? <SearchInput iconOnly={iconSearch} className="hidden w-full max-w-sm sm:block" /> : <div />}
|
|
435
|
-
<div className="text-center">{logo}</div>
|
|
436
|
-
<div className="justify-self-end">
|
|
437
|
-
<HeaderActions />
|
|
438
|
-
</div>
|
|
439
|
-
</div>
|
|
440
|
-
<div className="overflow-x-auto border-t" style={headerDivider}>
|
|
441
|
-
<div className="mx-auto max-w-7xl px-4 py-2.5 sm:px-6">
|
|
442
|
-
<NavLinks />
|
|
443
|
-
</div>
|
|
444
|
-
</div>
|
|
445
|
-
</>
|
|
446
|
-
) : variant === 'minimal' ? (
|
|
447
|
-
// Uyuni pattern: nav left, centered logo, actions right — single compact row
|
|
448
|
-
<div className="mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center px-4 sm:px-6">
|
|
449
|
-
<div className="flex items-center gap-5">
|
|
450
|
-
<NavLinks className="hidden sm:flex" />
|
|
451
|
-
</div>
|
|
452
|
-
<div className="text-center">{logo}</div>
|
|
453
|
-
<div className="flex items-center justify-end gap-3">
|
|
454
|
-
{showSearch && <SearchInput iconOnly={iconSearch} className="hidden w-44 lg:block" />}
|
|
455
|
-
<HeaderActions />
|
|
456
|
-
</div>
|
|
457
|
-
</div>
|
|
458
|
-
) : (
|
|
459
|
-
// classic: logo left, nav, then a right-aligned search + actions cluster.
|
|
460
|
-
// NavLinks is hidden below md in the primary row (no room for it there) —
|
|
461
|
-
// below md a hamburger opens MobileMenuDrawer instead, otherwise mobile
|
|
462
|
-
// has no way to reach nav/category links at all.
|
|
463
|
-
<>
|
|
464
|
-
<div className="mx-auto flex h-16 max-w-7xl items-center gap-6 px-4 sm:px-6">
|
|
465
|
-
{logo}
|
|
466
|
-
<NavLinks className="hidden md:flex" />
|
|
467
|
-
<div className="ml-auto flex items-center gap-2 sm:gap-3">
|
|
468
|
-
{showSearch && <SearchInput iconOnly={iconSearch} className={iconSearch ? '' : 'hidden w-full max-w-sm sm:block'} />}
|
|
469
|
-
<HeaderActions onMenuClick={() => setMobileMenuOpen(true)} />
|
|
470
|
-
</div>
|
|
471
|
-
</div>
|
|
472
|
-
<MobileMenuDrawer open={mobileMenuOpen} onClose={() => setMobileMenuOpen(false)} />
|
|
473
|
-
</>
|
|
474
|
-
)}
|
|
475
|
-
</header>
|
|
476
|
-
)
|
|
477
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import React, { useEffect } from 'react'
|
|
2
|
-
import { AlertCircle, Check, Info, X } from 'lucide-react'
|
|
3
|
-
import { useToastStore, type Toast } from '../stores/toast.store'
|
|
4
|
-
|
|
5
|
-
const ICON: Record<Toast['variant'], React.ComponentType<{ className?: string }>> = {
|
|
6
|
-
success: Check,
|
|
7
|
-
error: AlertCircle,
|
|
8
|
-
info: Info,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const ACCENT: Record<Toast['variant'], string> = {
|
|
12
|
-
success: 'bg-emerald-500/10 text-emerald-600',
|
|
13
|
-
error: 'bg-destructive/10 text-destructive',
|
|
14
|
-
info: 'bg-primary/10 text-primary',
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function ToastCard({ toast }: { toast: Toast }) {
|
|
18
|
-
const dismiss = useToastStore((s) => s.dismiss)
|
|
19
|
-
const Icon = ICON[toast.variant]
|
|
20
|
-
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
const timer = window.setTimeout(() => dismiss(toast.id), toast.durationMs)
|
|
23
|
-
return () => window.clearTimeout(timer)
|
|
24
|
-
}, [toast.id, toast.durationMs, dismiss])
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div
|
|
28
|
-
role="status"
|
|
29
|
-
className="pointer-events-auto flex w-80 max-w-[calc(100vw-2rem)] animate-slide-in-from-right items-start gap-3 rounded-xl border bg-card p-3.5 shadow-lg"
|
|
30
|
-
>
|
|
31
|
-
<span className={`grid h-8 w-8 shrink-0 place-items-center rounded-full ${ACCENT[toast.variant]}`}>
|
|
32
|
-
<Icon className="h-4 w-4" />
|
|
33
|
-
</span>
|
|
34
|
-
<div className="min-w-0 flex-1">
|
|
35
|
-
<p className="text-sm font-semibold leading-tight">{toast.title}</p>
|
|
36
|
-
{toast.description && <p className="mt-0.5 text-xs text-muted-foreground">{toast.description}</p>}
|
|
37
|
-
</div>
|
|
38
|
-
<button
|
|
39
|
-
type="button"
|
|
40
|
-
aria-label="Fechar"
|
|
41
|
-
onClick={() => dismiss(toast.id)}
|
|
42
|
-
className="text-muted-foreground transition-colors hover:text-foreground"
|
|
43
|
-
>
|
|
44
|
-
<X className="h-4 w-4" />
|
|
45
|
-
</button>
|
|
46
|
-
</div>
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function Toaster() {
|
|
51
|
-
const toasts = useToastStore((s) => s.toasts)
|
|
52
|
-
if (toasts.length === 0) return null
|
|
53
|
-
return (
|
|
54
|
-
<div className="pointer-events-none fixed bottom-4 right-4 z-[70] flex flex-col gap-2">
|
|
55
|
-
{toasts.map((t) => (
|
|
56
|
-
<ToastCard key={t.id} toast={t} />
|
|
57
|
-
))}
|
|
58
|
-
</div>
|
|
59
|
-
)
|
|
60
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { useCategories } from '../../hooks/useCategories'
|
|
3
|
-
import { useCatalogStore } from '../../stores/catalog.store'
|
|
4
|
-
import { useStorefrontConfig } from '../../config'
|
|
5
|
-
import { bannerPlaceholder } from '../../sections'
|
|
6
|
-
import { Reveal } from '../../motion'
|
|
7
|
-
import { navigateTo } from '../../router'
|
|
8
|
-
import { TID } from '../../testids'
|
|
9
|
-
import { SmoothImage } from '../SmoothImage'
|
|
10
|
-
|
|
11
|
-
export function CategoryShowcase({ style, title }: { style: 'bubbles' | 'tiles'; title?: string }) {
|
|
12
|
-
const { categories } = useCategories()
|
|
13
|
-
const config = useStorefrontConfig()
|
|
14
|
-
const setCategoryId = useCatalogStore((s) => s.setCategoryId)
|
|
15
|
-
|
|
16
|
-
if (categories.length === 0) return null
|
|
17
|
-
|
|
18
|
-
const go = (categoryId: string) => {
|
|
19
|
-
useCatalogStore.getState().reset()
|
|
20
|
-
setCategoryId(categoryId)
|
|
21
|
-
navigateTo(config.catalogPath)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (style === 'bubbles') {
|
|
25
|
-
return (
|
|
26
|
-
<section data-testid={TID.categoryShowcase} className="mx-auto max-w-7xl px-4 py-12 sm:px-6">
|
|
27
|
-
{title && (
|
|
28
|
-
<Reveal>
|
|
29
|
-
<h2 className="sf-heading mb-8 text-center text-3xl font-bold">{title}</h2>
|
|
30
|
-
</Reveal>
|
|
31
|
-
)}
|
|
32
|
-
<div className="flex flex-wrap items-start justify-center gap-8">
|
|
33
|
-
{categories.map((c, i) => (
|
|
34
|
-
<Reveal key={c.id} delay={i * 80}>
|
|
35
|
-
<button type="button" onClick={() => go(c.id)} className="group flex w-28 flex-col items-center gap-3">
|
|
36
|
-
<span className="block h-24 w-24 overflow-hidden rounded-full border-2 border-transparent shadow-sm transition-all duration-300 group-hover:scale-105 group-hover:border-primary group-hover:shadow-md">
|
|
37
|
-
<SmoothImage
|
|
38
|
-
src={c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 200, 200)}
|
|
39
|
-
alt={c.name}
|
|
40
|
-
className="h-full w-full object-cover"
|
|
41
|
-
/>
|
|
42
|
-
</span>
|
|
43
|
-
<span className="text-center text-xs font-medium leading-tight">{c.name}</span>
|
|
44
|
-
</button>
|
|
45
|
-
</Reveal>
|
|
46
|
-
))}
|
|
47
|
-
</div>
|
|
48
|
-
</section>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<section data-testid={TID.categoryShowcase} className="mx-auto max-w-7xl px-4 py-12 sm:px-6">
|
|
54
|
-
{title && (
|
|
55
|
-
<Reveal>
|
|
56
|
-
<h2 className="sf-heading mb-8 text-center text-3xl font-bold">{title}</h2>
|
|
57
|
-
</Reveal>
|
|
58
|
-
)}
|
|
59
|
-
<div className="grid grid-cols-2 gap-4 lg:grid-cols-4">
|
|
60
|
-
{categories.map((c, i) => (
|
|
61
|
-
<Reveal key={c.id} delay={i * 90}>
|
|
62
|
-
<button type="button" onClick={() => go(c.id)} className="group relative block aspect-[4/5] w-full overflow-hidden" style={{ borderRadius: 'var(--sf-radius-card)' }}>
|
|
63
|
-
<SmoothImage
|
|
64
|
-
src={c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 480, 600)}
|
|
65
|
-
alt={c.name}
|
|
66
|
-
className="h-full w-full object-cover transition-transform duration-700 ease-out group-hover:scale-110"
|
|
67
|
-
/>
|
|
68
|
-
<span className="absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent p-5 pt-12 text-left">
|
|
69
|
-
<span className="sf-heading block text-lg font-semibold text-white">{c.name}</span>
|
|
70
|
-
<span className="mt-0.5 block text-xs text-white/80 opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
|
71
|
-
Explorar →
|
|
72
|
-
</span>
|
|
73
|
-
</span>
|
|
74
|
-
</button>
|
|
75
|
-
</Reveal>
|
|
76
|
-
))}
|
|
77
|
-
</div>
|
|
78
|
-
</section>
|
|
79
|
-
)
|
|
80
|
-
}
|