@fayz-ai/storefront 0.6.0 → 0.7.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/dist/auth.d.ts +12 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/blocks.d.ts +3 -3
- package/dist/blocks.d.ts.map +1 -1
- package/dist/component-contracts.d.ts +124 -0
- package/dist/component-contracts.d.ts.map +1 -0
- package/dist/component-selectors.d.ts +61 -0
- package/dist/component-selectors.d.ts.map +1 -0
- package/dist/components/CartDrawer.d.ts.map +1 -1
- package/dist/components/ProductCard.d.ts +2 -5
- package/dist/components/ProductCard.d.ts.map +1 -1
- package/dist/components/ProductEnquiryForm.d.ts +8 -0
- package/dist/components/ProductEnquiryForm.d.ts.map +1 -0
- package/dist/components/ProductGrid.d.ts.map +1 -1
- package/dist/components/RelatedProducts.d.ts +1 -1
- package/dist/components/RelatedProducts.d.ts.map +1 -1
- package/dist/components/SmoothImage.d.ts +7 -0
- package/dist/components/SmoothImage.d.ts.map +1 -0
- package/dist/components/StorefrontFooter.d.ts.map +1 -1
- package/dist/components/StorefrontHeader.d.ts.map +1 -1
- package/dist/components/sections/CategoryShowcase.d.ts.map +1 -1
- package/dist/components/sections/HeroSection.d.ts.map +1 -1
- package/dist/components/sections/MediaCarousel.d.ts +11 -0
- package/dist/components/sections/MediaCarousel.d.ts.map +1 -0
- package/dist/components/sections/MiscSections.d.ts.map +1 -1
- package/dist/components/sections/ProductRail.d.ts.map +1 -1
- package/dist/components/sections/ProductSlider.d.ts +15 -0
- package/dist/components/sections/ProductSlider.d.ts.map +1 -0
- package/dist/config.d.ts +38 -21
- package/dist/config.d.ts.map +1 -1
- package/dist/createStorefrontApp.d.ts +12 -0
- package/dist/createStorefrontApp.d.ts.map +1 -1
- package/dist/define.d.ts +17 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useStorefront.d.ts +35 -0
- package/dist/hooks/useStorefront.d.ts.map +1 -0
- package/dist/index.cjs +904 -185
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +20 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +797 -95
- package/dist/index.js.map +1 -1
- package/dist/pages/CheckoutPage.d.ts.map +1 -1
- package/dist/pages/HomePage.d.ts +5 -2
- package/dist/pages/HomePage.d.ts.map +1 -1
- package/dist/pages/MyPurchasesPage.d.ts.map +1 -1
- package/dist/pages/OrderConfirmationPage.d.ts.map +1 -1
- package/dist/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/sections.d.ts +45 -2
- package/dist/sections.d.ts.map +1 -1
- package/dist/testids.d.ts +20 -0
- package/dist/testids.d.ts.map +1 -1
- package/dist/theme.d.ts +4 -0
- package/dist/theme.d.ts.map +1 -1
- package/package.json +7 -9
- package/src/auth.ts +47 -10
- package/src/blocks.tsx +7 -3
- package/src/component-contracts.ts +140 -0
- package/src/component-selectors.ts +31 -0
- package/src/components/CartDrawer.tsx +2 -1
- package/src/components/ProductCard.tsx +39 -23
- package/src/components/ProductEnquiryForm.tsx +175 -0
- package/src/components/ProductGrid.tsx +12 -3
- package/src/components/RelatedProducts.tsx +13 -4
- package/src/components/SmoothImage.tsx +59 -0
- package/src/components/StorefrontFooter.tsx +24 -14
- package/src/components/StorefrontHeader.tsx +43 -38
- package/src/components/sections/CategoryShowcase.tsx +3 -2
- package/src/components/sections/HeroSection.tsx +18 -2
- package/src/components/sections/MediaCarousel.tsx +141 -0
- package/src/components/sections/MiscSections.tsx +2 -1
- package/src/components/sections/ProductRail.tsx +11 -3
- package/src/components/sections/ProductSlider.tsx +119 -0
- package/src/config.ts +65 -24
- package/src/createStorefrontApp.tsx +62 -9
- package/src/define.ts +34 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useStorefront.ts +58 -0
- package/src/index.ts +61 -11
- package/src/pages/CheckoutPage.tsx +2 -1
- package/src/pages/HomePage.tsx +6 -2
- package/src/pages/MyPurchasesPage.tsx +2 -1
- package/src/pages/OrderConfirmationPage.tsx +2 -1
- package/src/pages/ProductDetailPage.tsx +138 -62
- package/src/scaffold.tsx +1 -1
- package/src/sections.ts +49 -2
- package/src/testids.ts +20 -0
- package/src/theme.ts +4 -0
- package/src/workflows/checkout.ts +1 -1
- package/dist/slot-contracts.d.ts +0 -34
- package/dist/slot-contracts.d.ts.map +0 -1
- package/src/slot-contracts.ts +0 -20
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { setShopProvider, getShopProvider } from '@fayz-ai/shop/runtime';
|
|
2
2
|
import { createMockShopProvider } from '@fayz-ai/shop/mock';
|
|
3
|
-
import { createMockAuthAdapter } from '@fayz-ai/auth';
|
|
3
|
+
import { createAuthRuntime, createMockAuthAdapter } from '@fayz-ai/plugin-auth';
|
|
4
4
|
import { create } from 'zustand';
|
|
5
5
|
import { persist } from 'zustand/middleware';
|
|
6
|
-
import
|
|
6
|
+
import React6, { createContext, useState, useRef, useEffect, useMemo, useContext, useCallback } from 'react';
|
|
7
7
|
import { hashRouterAdapter, registerScaffold, registerBlock, renderBlocks, defineApp } from '@fayz-ai/core';
|
|
8
8
|
export { getSupabaseClientOptional, renderApp } from '@fayz-ai/core';
|
|
9
9
|
import * as LucideIcons from 'lucide-react';
|
|
10
|
-
import { ChevronLeft, ChevronRight, ShoppingBag, X, Trash2, Phone, Mail, Search, User, UserCircle, Package, CreditCard, LogOut, Truck, RefreshCcw, ShieldCheck, Lock, Minus, Plus, Info, AlertCircle, Check, MapPin, PackageCheck, QrCode, XCircle, RotateCcw, Clock } from 'lucide-react';
|
|
10
|
+
import { ChevronLeft, ChevronRight, ShoppingBag, X, Trash2, Phone, Mail, Search, User, UserCircle, Package, CreditCard, LogOut, Truck, RefreshCcw, ShieldCheck, Lock, Minus, Plus, Info, AlertCircle, Check, Send, MessageCircle, MapPin, PackageCheck, QrCode, XCircle, RotateCcw, Clock } from 'lucide-react';
|
|
11
11
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
12
12
|
import { validateDiscount } from '@fayz-ai/shop';
|
|
13
13
|
|
|
@@ -27,14 +27,31 @@ var useSessionStore = create()(
|
|
|
27
27
|
|
|
28
28
|
// src/auth.ts
|
|
29
29
|
var _adapter = null;
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
30
|
+
function isAuthAdapter(value) {
|
|
31
|
+
return Boolean(
|
|
32
|
+
value && typeof value === "object" && "getSession" in value && "signIn" in value && "signOut" in value
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function normalizeAuthConfig(configured) {
|
|
36
|
+
if (!configured) return {};
|
|
37
|
+
if (typeof configured === "string" || isAuthAdapter(configured)) return { adapter: configured };
|
|
38
|
+
return configured;
|
|
39
|
+
}
|
|
40
|
+
function resolveAuthAdapter(configured, fallback) {
|
|
41
|
+
const config = normalizeAuthConfig(configured);
|
|
42
|
+
return createAuthRuntime({
|
|
43
|
+
provider: config.provider ?? config.adapter ?? (fallback?.supabaseUrl ? "supabase" : "mock"),
|
|
44
|
+
adapter: config.adapter,
|
|
45
|
+
routes: config.routes,
|
|
46
|
+
requireAuth: false,
|
|
47
|
+
supabase: {
|
|
48
|
+
url: config.supabase?.url ?? fallback?.supabaseUrl,
|
|
49
|
+
anonKey: config.supabase?.anonKey ?? fallback?.supabaseAnonKey,
|
|
50
|
+
client: config.supabase?.client,
|
|
51
|
+
callbackUrl: config.supabase?.callbackUrl,
|
|
52
|
+
resetPasswordUrl: config.supabase?.resetPasswordUrl
|
|
53
|
+
}
|
|
54
|
+
}).adapter;
|
|
38
55
|
}
|
|
39
56
|
function initCustomerAuth(adapter2) {
|
|
40
57
|
_adapter = adapter2;
|
|
@@ -105,18 +122,37 @@ async function signOutCustomer() {
|
|
|
105
122
|
}
|
|
106
123
|
function resolveConfig(config) {
|
|
107
124
|
const catalogPath = config.home ? "/catalog" : "/";
|
|
125
|
+
const commerceMode = config.commerceMode ?? "checkout";
|
|
126
|
+
const checkoutEnabled = commerceMode === "checkout";
|
|
108
127
|
return {
|
|
109
128
|
...config,
|
|
110
129
|
currency: config.currency ?? "BRL",
|
|
111
130
|
locale: config.locale ?? "pt-BR",
|
|
112
131
|
shipping: { flatRate: config.shipping?.flatRate ?? 0, freeAbove: config.shipping?.freeAbove },
|
|
113
132
|
payments: { mode: config.payments?.mode ?? "mock" },
|
|
133
|
+
commerceMode,
|
|
134
|
+
enquiry: {
|
|
135
|
+
label: config.enquiry?.label ?? "Contact me",
|
|
136
|
+
successMessage: config.enquiry?.successMessage ?? "Thanks. We received your enquiry.",
|
|
137
|
+
subjectPrefix: config.enquiry?.subjectPrefix ?? "Product enquiry",
|
|
138
|
+
email: config.enquiry?.email,
|
|
139
|
+
whatsappUrl: config.enquiry?.whatsappUrl,
|
|
140
|
+
requirePhone: config.enquiry?.requirePhone
|
|
141
|
+
},
|
|
114
142
|
features: {
|
|
115
143
|
// Auto-enable the coupon UI when the store actually ships discount codes
|
|
116
144
|
// (via config.discounts or a seeded catalog), unless explicitly overridden —
|
|
117
145
|
// otherwise a store can advertise a coupon with no field to enter it.
|
|
118
146
|
discounts: config.features?.discounts ?? Boolean(config.discounts?.length || config.catalog?.discounts?.length),
|
|
119
|
-
accounts: config.features?.accounts ??
|
|
147
|
+
accounts: config.features?.accounts ?? checkoutEnabled,
|
|
148
|
+
cart: config.features?.cart ?? checkoutEnabled,
|
|
149
|
+
checkout: config.features?.checkout ?? checkoutEnabled
|
|
150
|
+
},
|
|
151
|
+
imageLoading: {
|
|
152
|
+
mode: config.imageLoading?.mode ?? "fade",
|
|
153
|
+
durationMs: config.imageLoading?.durationMs ?? 420,
|
|
154
|
+
easing: config.imageLoading?.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
155
|
+
blur: config.imageLoading?.blur ?? true
|
|
120
156
|
},
|
|
121
157
|
catalogPath,
|
|
122
158
|
nav: config.nav ?? (config.home ? [
|
|
@@ -132,6 +168,12 @@ function useStorefrontConfig() {
|
|
|
132
168
|
if (!ctx) throw new Error("useStorefrontConfig must be used inside createStorefrontApp");
|
|
133
169
|
return ctx;
|
|
134
170
|
}
|
|
171
|
+
function useStorefrontConfigOptional() {
|
|
172
|
+
return useContext(StorefrontConfigContext);
|
|
173
|
+
}
|
|
174
|
+
function getStorefrontComponents(config) {
|
|
175
|
+
return config.components ?? {};
|
|
176
|
+
}
|
|
135
177
|
var adapter = hashRouterAdapter();
|
|
136
178
|
function navigateTo(to) {
|
|
137
179
|
adapter.navigate(to);
|
|
@@ -162,7 +204,7 @@ function matchPath(pattern, path) {
|
|
|
162
204
|
return params;
|
|
163
205
|
}
|
|
164
206
|
function Link({ to, children, ...rest }) {
|
|
165
|
-
return
|
|
207
|
+
return React6.createElement("a", { href: `#${to}`, ...rest }, children);
|
|
166
208
|
}
|
|
167
209
|
var RADIUS_MAP = {
|
|
168
210
|
none: { button: "0px", card: "0px", input: "0px" },
|
|
@@ -248,7 +290,7 @@ function StorefrontThemeStyle({ theme }) {
|
|
|
248
290
|
}
|
|
249
291
|
link.href = fontsHref;
|
|
250
292
|
}, [fontsHref]);
|
|
251
|
-
return
|
|
293
|
+
return React6.createElement("style", { "data-sf-theme": theme.name }, themeToCss(theme));
|
|
252
294
|
}
|
|
253
295
|
|
|
254
296
|
// src/format.ts
|
|
@@ -496,6 +538,13 @@ var TID = {
|
|
|
496
538
|
hero: "home-hero",
|
|
497
539
|
heroPrev: "hero-prev",
|
|
498
540
|
heroNext: "hero-next",
|
|
541
|
+
mediaCarousel: "media-carousel",
|
|
542
|
+
mediaCarouselPrev: "media-carousel-prev",
|
|
543
|
+
mediaCarouselNext: "media-carousel-next",
|
|
544
|
+
productSlider: "product-slider",
|
|
545
|
+
productSliderViewport: "product-slider-viewport",
|
|
546
|
+
productSliderPrev: "product-slider-prev",
|
|
547
|
+
productSliderNext: "product-slider-next",
|
|
499
548
|
categoryShowcase: "category-showcase",
|
|
500
549
|
productRail: "product-rail",
|
|
501
550
|
benefits: "benefits-row",
|
|
@@ -530,6 +579,19 @@ var TID = {
|
|
|
530
579
|
productOptionValue: (id, value) => `product-option-${id.toLowerCase().replace(/\s+/g, "-")}-${value.toLowerCase().replace(/\s+/g, "-")}`,
|
|
531
580
|
pdpQtyInput: "pdp-qty-input",
|
|
532
581
|
pdpAddToCart: "pdp-add-to-cart",
|
|
582
|
+
pdpRoot: "pdp-root",
|
|
583
|
+
pdpGallery: "pdp-gallery",
|
|
584
|
+
pdpActions: "pdp-actions",
|
|
585
|
+
pdpEnquiryButton: "pdp-enquiry-button",
|
|
586
|
+
enquiryForm: "enquiry-form",
|
|
587
|
+
enquiryName: "enquiry-name",
|
|
588
|
+
enquiryEmail: "enquiry-email",
|
|
589
|
+
enquiryPhone: "enquiry-phone",
|
|
590
|
+
enquirySubject: "enquiry-subject",
|
|
591
|
+
enquiryMessage: "enquiry-message",
|
|
592
|
+
enquirySubmit: "enquiry-submit",
|
|
593
|
+
enquirySuccess: "enquiry-success",
|
|
594
|
+
enquiryError: "enquiry-error",
|
|
533
595
|
// cart
|
|
534
596
|
cartDrawer: "cart-drawer",
|
|
535
597
|
cartLine: "cart-line",
|
|
@@ -757,7 +819,7 @@ function HeaderActions() {
|
|
|
757
819
|
}
|
|
758
820
|
)
|
|
759
821
|
] }),
|
|
760
|
-
/* @__PURE__ */ jsxs(
|
|
822
|
+
config.features.cart && /* @__PURE__ */ jsxs(
|
|
761
823
|
"button",
|
|
762
824
|
{
|
|
763
825
|
type: "button",
|
|
@@ -784,12 +846,13 @@ function NavLinks({ className }) {
|
|
|
784
846
|
const config = useStorefrontConfig();
|
|
785
847
|
const { categories } = useCategories();
|
|
786
848
|
const setCategoryId = useCatalogStore((s) => s.setCategoryId);
|
|
849
|
+
const showCategories = config.theme?.header?.showCategories !== false;
|
|
787
850
|
const goCategory = (categoryId) => {
|
|
788
851
|
useCatalogStore.getState().reset();
|
|
789
852
|
setCategoryId(categoryId);
|
|
790
853
|
navigateTo(config.catalogPath);
|
|
791
854
|
};
|
|
792
|
-
return /* @__PURE__ */ jsxs("nav", { className: `flex items-center gap-6 ${className ?? ""}`, children: [
|
|
855
|
+
return /* @__PURE__ */ jsxs("nav", { className: `flex min-w-max items-center gap-6 ${className ?? ""}`, children: [
|
|
793
856
|
config.nav.map((link) => /* @__PURE__ */ jsx(
|
|
794
857
|
Link,
|
|
795
858
|
{
|
|
@@ -800,7 +863,7 @@ function NavLinks({ className }) {
|
|
|
800
863
|
},
|
|
801
864
|
link.to
|
|
802
865
|
)),
|
|
803
|
-
categories.slice(0, 6).map((c) => /* @__PURE__ */ jsxs(
|
|
866
|
+
showCategories && categories.slice(0, 6).map((c) => /* @__PURE__ */ jsxs(
|
|
804
867
|
"button",
|
|
805
868
|
{
|
|
806
869
|
type: "button",
|
|
@@ -820,6 +883,7 @@ function StorefrontHeader() {
|
|
|
820
883
|
const config = useStorefrontConfig();
|
|
821
884
|
const variant = config.theme?.header?.variant ?? "classic";
|
|
822
885
|
const scrolled = useScrolled();
|
|
886
|
+
const showSearch = config.theme?.header?.showSearch !== false;
|
|
823
887
|
const logo = /* @__PURE__ */ jsx(Link, { to: "/", className: "sf-heading shrink-0 text-xl font-bold tracking-tight", children: config.logo ?? config.name });
|
|
824
888
|
return /* @__PURE__ */ jsxs(
|
|
825
889
|
"header",
|
|
@@ -833,21 +897,21 @@ function StorefrontHeader() {
|
|
|
833
897
|
// Rio/Flex pattern: centered logo row, nav row below
|
|
834
898
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
835
899
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center px-4 sm:px-6", children: [
|
|
836
|
-
/* @__PURE__ */ jsx(SearchInput, { className: "hidden w-full max-w-xs sm:block" }),
|
|
900
|
+
showSearch ? /* @__PURE__ */ jsx(SearchInput, { className: "hidden w-full max-w-xs sm:block" }) : /* @__PURE__ */ jsx("div", {}),
|
|
837
901
|
/* @__PURE__ */ jsx("div", { className: "text-center", children: logo }),
|
|
838
902
|
/* @__PURE__ */ jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsx(HeaderActions, {}) })
|
|
839
903
|
] }),
|
|
840
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
904
|
+
/* @__PURE__ */ jsx("div", { className: "flex overflow-x-auto border-t py-2.5 sm:justify-center", style: headerDivider, children: /* @__PURE__ */ jsx(NavLinks, {}) })
|
|
841
905
|
] })
|
|
842
906
|
) : variant === "search" ? (
|
|
843
907
|
// Brasília pattern: prominent search left, logo center, actions right; nav row below
|
|
844
908
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
845
909
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center gap-6 px-4 sm:px-6", children: [
|
|
846
|
-
/* @__PURE__ */ jsx(SearchInput, { className: "hidden w-full max-w-sm sm:block" }),
|
|
910
|
+
showSearch ? /* @__PURE__ */ jsx(SearchInput, { className: "hidden w-full max-w-sm sm:block" }) : /* @__PURE__ */ jsx("div", {}),
|
|
847
911
|
/* @__PURE__ */ jsx("div", { className: "text-center", children: logo }),
|
|
848
912
|
/* @__PURE__ */ jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsx(HeaderActions, {}) })
|
|
849
913
|
] }),
|
|
850
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
914
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto border-t", style: headerDivider, children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-7xl px-4 py-2.5 sm:px-6", children: /* @__PURE__ */ jsx(NavLinks, {}) }) })
|
|
851
915
|
] })
|
|
852
916
|
) : variant === "minimal" ? (
|
|
853
917
|
// Uyuni pattern: nav left, centered logo, actions right — single compact row
|
|
@@ -855,7 +919,7 @@ function StorefrontHeader() {
|
|
|
855
919
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-5", children: /* @__PURE__ */ jsx(NavLinks, { className: "hidden sm:flex" }) }),
|
|
856
920
|
/* @__PURE__ */ jsx("div", { className: "text-center", children: logo }),
|
|
857
921
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3", children: [
|
|
858
|
-
/* @__PURE__ */ jsx(SearchInput, { className: "hidden w-44 lg:block" }),
|
|
922
|
+
showSearch && /* @__PURE__ */ jsx(SearchInput, { className: "hidden w-44 lg:block" }),
|
|
859
923
|
/* @__PURE__ */ jsx(HeaderActions, {})
|
|
860
924
|
] })
|
|
861
925
|
] })
|
|
@@ -864,7 +928,7 @@ function StorefrontHeader() {
|
|
|
864
928
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto flex h-16 max-w-7xl items-center gap-6 px-4 sm:px-6", children: [
|
|
865
929
|
logo,
|
|
866
930
|
/* @__PURE__ */ jsx(NavLinks, { className: "hidden md:flex" }),
|
|
867
|
-
/* @__PURE__ */ jsx(SearchInput, { className: "ml-auto hidden w-full max-w-sm sm:block" }),
|
|
931
|
+
showSearch && /* @__PURE__ */ jsx(SearchInput, { className: "ml-auto hidden w-full max-w-sm sm:block" }),
|
|
868
932
|
/* @__PURE__ */ jsx(HeaderActions, {})
|
|
869
933
|
] })
|
|
870
934
|
)
|
|
@@ -877,6 +941,17 @@ function StorefrontFooter() {
|
|
|
877
941
|
const { categories } = useCategories();
|
|
878
942
|
const visibleCategories = categories.slice(0, 8);
|
|
879
943
|
const hiddenCategoryCount = Math.max(categories.length - visibleCategories.length, 0);
|
|
944
|
+
const labels = {
|
|
945
|
+
categories: "Departamentos",
|
|
946
|
+
navigation: "Navega\xE7\xE3o",
|
|
947
|
+
contact: "Contato",
|
|
948
|
+
viewAll: "Ver todos",
|
|
949
|
+
purchases: "Minhas compras",
|
|
950
|
+
privacy: "Privacidade",
|
|
951
|
+
terms: "Termos",
|
|
952
|
+
returns: "Trocas e devolu\xE7\xF5es",
|
|
953
|
+
...config.footer?.labels
|
|
954
|
+
};
|
|
880
955
|
const goCategory = (categoryId) => {
|
|
881
956
|
useCatalogStore.getState().reset();
|
|
882
957
|
useCatalogStore.getState().setCategoryId(categoryId);
|
|
@@ -889,7 +964,7 @@ function StorefrontFooter() {
|
|
|
889
964
|
config.footer?.about && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm leading-relaxed text-muted-foreground", children: config.footer.about })
|
|
890
965
|
] }),
|
|
891
966
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
892
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children:
|
|
967
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.categories }),
|
|
893
968
|
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm", children: [
|
|
894
969
|
visibleCategories.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
895
970
|
"button",
|
|
@@ -901,21 +976,22 @@ function StorefrontFooter() {
|
|
|
901
976
|
}
|
|
902
977
|
) }, c.id)),
|
|
903
978
|
hiddenCategoryCount > 0 && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, { to: config.catalogPath, className: "font-medium underline-offset-2 transition-opacity hover:underline", children: [
|
|
904
|
-
|
|
979
|
+
labels.viewAll,
|
|
980
|
+
" (",
|
|
905
981
|
categories.length,
|
|
906
982
|
")"
|
|
907
983
|
] }) })
|
|
908
984
|
] })
|
|
909
985
|
] }),
|
|
910
986
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
911
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children:
|
|
987
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.navigation }),
|
|
912
988
|
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm", children: [
|
|
913
989
|
config.nav.map((link) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, { to: link.to, className: "transition-opacity hover:opacity-70", children: link.label }) }, link.to)),
|
|
914
|
-
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, { to: "/account", className: "transition-opacity hover:opacity-70", children:
|
|
990
|
+
config.features.accounts && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, { to: "/account", className: "transition-opacity hover:opacity-70", children: labels.purchases }) })
|
|
915
991
|
] })
|
|
916
992
|
] }),
|
|
917
993
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
918
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children:
|
|
994
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.contact }),
|
|
919
995
|
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm text-muted-foreground", children: [
|
|
920
996
|
config.footer?.contact?.phone && /* @__PURE__ */ jsx("li", { children: config.footer.contact.phone }),
|
|
921
997
|
config.footer?.contact?.email && /* @__PURE__ */ jsx("li", { children: config.footer.contact.email }),
|
|
@@ -926,17 +1002,15 @@ function StorefrontFooter() {
|
|
|
926
1002
|
] }),
|
|
927
1003
|
/* @__PURE__ */ jsx("div", { className: "border-t", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-7xl flex-col items-center gap-3 px-4 py-5 text-xs text-muted-foreground sm:flex-row sm:justify-between sm:px-6", children: [
|
|
928
1004
|
/* @__PURE__ */ jsxs("nav", { className: "flex flex-wrap items-center justify-center gap-x-5 gap-y-2", children: [
|
|
929
|
-
/* @__PURE__ */ jsx(Link, { to: "/privacy", className: "transition-colors hover:text-foreground", children:
|
|
930
|
-
/* @__PURE__ */ jsx(Link, { to: "/terms", className: "transition-colors hover:text-foreground", children:
|
|
931
|
-
/* @__PURE__ */ jsx(Link, { to: "/refunds", className: "transition-colors hover:text-foreground", children:
|
|
1005
|
+
/* @__PURE__ */ jsx(Link, { to: "/privacy", className: "transition-colors hover:text-foreground", children: labels.privacy }),
|
|
1006
|
+
/* @__PURE__ */ jsx(Link, { to: "/terms", className: "transition-colors hover:text-foreground", children: labels.terms }),
|
|
1007
|
+
/* @__PURE__ */ jsx(Link, { to: "/refunds", className: "transition-colors hover:text-foreground", children: labels.returns })
|
|
932
1008
|
] }),
|
|
933
|
-
/* @__PURE__ */
|
|
1009
|
+
/* @__PURE__ */ jsx("p", { className: "text-center", children: config.footer?.credit ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
934
1010
|
"\xA9 ",
|
|
935
1011
|
config.name,
|
|
936
|
-
" \u2014 powered by Fayz
|
|
937
|
-
|
|
938
|
-
/* @__PURE__ */ jsx("a", { href: "https://unsplash.com", target: "_blank", rel: "noreferrer", className: "underline-offset-2 hover:underline", children: "Unsplash" })
|
|
939
|
-
] })
|
|
1012
|
+
" \u2014 powered by Fayz"
|
|
1013
|
+
] }) })
|
|
940
1014
|
] }) })
|
|
941
1015
|
] });
|
|
942
1016
|
}
|
|
@@ -1029,6 +1103,53 @@ function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTe
|
|
|
1029
1103
|
)
|
|
1030
1104
|
] });
|
|
1031
1105
|
}
|
|
1106
|
+
function SmoothImage({
|
|
1107
|
+
imageLoading,
|
|
1108
|
+
loading = "lazy",
|
|
1109
|
+
decoding = "async",
|
|
1110
|
+
onLoad,
|
|
1111
|
+
style,
|
|
1112
|
+
...props
|
|
1113
|
+
}) {
|
|
1114
|
+
const config = useStorefrontConfigOptional();
|
|
1115
|
+
const resolved = {
|
|
1116
|
+
...config?.imageLoading,
|
|
1117
|
+
...imageLoading
|
|
1118
|
+
};
|
|
1119
|
+
const mode = resolved.mode ?? "fade";
|
|
1120
|
+
const durationMs = resolved.durationMs ?? 420;
|
|
1121
|
+
const easing = resolved.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)";
|
|
1122
|
+
const blur = resolved.blur ?? true;
|
|
1123
|
+
const ref = React6.useRef(null);
|
|
1124
|
+
const [loaded, setLoaded] = React6.useState(mode === "none");
|
|
1125
|
+
React6.useEffect(() => {
|
|
1126
|
+
if (mode === "none") {
|
|
1127
|
+
setLoaded(true);
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
if (ref.current?.complete && ref.current.naturalWidth > 0) setLoaded(true);
|
|
1131
|
+
}, [mode, props.src]);
|
|
1132
|
+
const revealStyle = mode === "none" ? {} : {
|
|
1133
|
+
opacity: loaded ? 1 : 0,
|
|
1134
|
+
filter: blur && !loaded ? "blur(10px)" : "blur(0px)",
|
|
1135
|
+
transition: `opacity ${durationMs}ms ${easing}, filter ${durationMs}ms ${easing}, transform ${durationMs}ms ${easing}`
|
|
1136
|
+
};
|
|
1137
|
+
return /* @__PURE__ */ jsx(
|
|
1138
|
+
"img",
|
|
1139
|
+
{
|
|
1140
|
+
...props,
|
|
1141
|
+
ref,
|
|
1142
|
+
loading,
|
|
1143
|
+
decoding,
|
|
1144
|
+
"data-sf-image-loaded": loaded ? "true" : "false",
|
|
1145
|
+
style: { ...style, ...revealStyle },
|
|
1146
|
+
onLoad: (event) => {
|
|
1147
|
+
setLoaded(true);
|
|
1148
|
+
onLoad?.(event);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1032
1153
|
function CartDrawer() {
|
|
1033
1154
|
const config = useStorefrontConfig();
|
|
1034
1155
|
const cart = useCartStore();
|
|
@@ -1128,7 +1249,7 @@ function CartDrawer() {
|
|
|
1128
1249
|
"data-line-id": line.lineId ?? line.productId,
|
|
1129
1250
|
className: `flex animate-fade-up gap-3 rounded-lg p-1.5 transition-all hover:bg-muted/40 ${(line.lineId ?? line.productId) === cart.justAddedLineId ? "bg-primary/5 ring-1 ring-primary/30" : ""}`,
|
|
1130
1251
|
children: [
|
|
1131
|
-
line.imageUrl && /* @__PURE__ */ jsx(
|
|
1252
|
+
line.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: line.imageUrl, alt: line.name, className: "h-20 w-20 shrink-0 rounded-lg border object-cover" }),
|
|
1132
1253
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
|
|
1133
1254
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
1134
1255
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium leading-snug", children: line.name }),
|
|
@@ -1298,36 +1419,91 @@ function Price({ value, compareAt, testId, compareTestId, className }) {
|
|
|
1298
1419
|
] });
|
|
1299
1420
|
}
|
|
1300
1421
|
|
|
1301
|
-
// src/
|
|
1302
|
-
var
|
|
1422
|
+
// src/component-selectors.ts
|
|
1423
|
+
var productCardComponentContract = {
|
|
1303
1424
|
root: { "data-testid": TID.productCard },
|
|
1304
1425
|
name: { "data-testid": TID.productCardName },
|
|
1305
1426
|
priceTestId: TID.productCardPrice,
|
|
1306
1427
|
addButton: { "data-testid": TID.productCardAdd }
|
|
1307
1428
|
};
|
|
1308
|
-
var
|
|
1309
|
-
productCard:
|
|
1429
|
+
var storefrontComponentContracts = {
|
|
1430
|
+
productCard: productCardComponentContract,
|
|
1431
|
+
productDetail: {
|
|
1432
|
+
root: { "data-testid": TID.pdpRoot },
|
|
1433
|
+
gallery: { "data-testid": TID.pdpGallery },
|
|
1434
|
+
name: { "data-testid": TID.pdpName },
|
|
1435
|
+
description: { "data-testid": TID.pdpDescription },
|
|
1436
|
+
actions: { "data-testid": TID.pdpActions },
|
|
1437
|
+
addButton: { "data-testid": TID.pdpAddToCart },
|
|
1438
|
+
enquiryButton: { "data-testid": TID.pdpEnquiryButton },
|
|
1439
|
+
enquiryForm: { "data-testid": TID.enquiryForm }
|
|
1440
|
+
}
|
|
1310
1441
|
};
|
|
1311
|
-
function
|
|
1442
|
+
function useStorefront() {
|
|
1443
|
+
return useStorefrontConfig();
|
|
1444
|
+
}
|
|
1445
|
+
function useCatalog() {
|
|
1312
1446
|
const config = useStorefrontConfig();
|
|
1313
|
-
const
|
|
1447
|
+
const catalog = useCatalogStore();
|
|
1448
|
+
return {
|
|
1449
|
+
...catalog,
|
|
1450
|
+
catalogPath: config.catalogPath,
|
|
1451
|
+
config
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
function useCart() {
|
|
1455
|
+
return useCartStore();
|
|
1456
|
+
}
|
|
1457
|
+
function useEnquiry() {
|
|
1458
|
+
const config = useStorefrontConfig();
|
|
1459
|
+
const createProductEnquiry = async (input) => {
|
|
1460
|
+
const provider = getShopProvider();
|
|
1461
|
+
if (!provider.createProductEnquiry) {
|
|
1462
|
+
throw new Error("The active shop provider does not support product enquiries.");
|
|
1463
|
+
}
|
|
1464
|
+
return provider.createProductEnquiry(input);
|
|
1465
|
+
};
|
|
1466
|
+
return {
|
|
1467
|
+
enquiry: config.enquiry,
|
|
1468
|
+
createProductEnquiry
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
function useStorefrontActions() {
|
|
1472
|
+
const addItem = useCartStore((state) => state.addItem);
|
|
1473
|
+
const openDrawer = useCartStore((state) => state.openDrawer);
|
|
1474
|
+
const closeDrawer = useCartStore((state) => state.closeDrawer);
|
|
1475
|
+
const { createProductEnquiry } = useEnquiry();
|
|
1476
|
+
return {
|
|
1477
|
+
navigateTo,
|
|
1478
|
+
addToCart: (product, quantity, options) => addItem(product, quantity, options),
|
|
1479
|
+
openCart: openDrawer,
|
|
1480
|
+
closeCart: closeDrawer,
|
|
1481
|
+
createProductEnquiry
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
function ProductCard({ product, config: providedConfig, actions: providedActions }) {
|
|
1485
|
+
const runtimeConfig = useStorefrontConfig();
|
|
1486
|
+
const runtimeActions = useStorefrontActions();
|
|
1487
|
+
const config = providedConfig ?? runtimeConfig;
|
|
1488
|
+
const actions = providedActions ?? runtimeActions;
|
|
1314
1489
|
const hasOptions = getProductOptionGroups(product).length > 0;
|
|
1315
1490
|
const soldOut = product.inventoryCount <= 0;
|
|
1316
1491
|
const onSale = product.compareAtPrice != null && product.compareAtPrice > product.price;
|
|
1317
1492
|
const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
|
|
1318
1493
|
const cardStyle = config.theme?.productCard?.style ?? "card";
|
|
1319
1494
|
const aspect = config.theme?.productCard?.imageAspect === "portrait" ? "aspect-[3/4]" : "aspect-square";
|
|
1495
|
+
const purchaseMode = config.commerceMode === "checkout";
|
|
1320
1496
|
return /* @__PURE__ */ jsxs(
|
|
1321
1497
|
"div",
|
|
1322
1498
|
{
|
|
1323
|
-
...
|
|
1499
|
+
...productCardComponentContract.root,
|
|
1324
1500
|
"data-slug": product.slug,
|
|
1325
1501
|
className: `group flex flex-col overflow-hidden transition-all duration-300 ${cardStyle === "editorial" ? "hover:-translate-y-1" : "border bg-card hover:-translate-y-1 hover:shadow-lg"}`,
|
|
1326
1502
|
style: { borderRadius: "var(--sf-radius-card)" },
|
|
1327
1503
|
children: [
|
|
1328
1504
|
/* @__PURE__ */ jsxs(Link, { to: `/product/${product.slug}`, className: `relative block ${aspect} overflow-hidden bg-muted`, style: { borderRadius: cardStyle === "editorial" ? "var(--sf-radius-card)" : void 0 }, children: [
|
|
1329
1505
|
image && /* @__PURE__ */ jsx(
|
|
1330
|
-
|
|
1506
|
+
SmoothImage,
|
|
1331
1507
|
{
|
|
1332
1508
|
src: image.url,
|
|
1333
1509
|
alt: image.altText ?? product.name,
|
|
@@ -1360,33 +1536,37 @@ function ProductCard({ product }) {
|
|
|
1360
1536
|
Link,
|
|
1361
1537
|
{
|
|
1362
1538
|
to: `/product/${product.slug}`,
|
|
1363
|
-
...
|
|
1539
|
+
...productCardComponentContract.name,
|
|
1364
1540
|
className: "font-medium leading-snug hover:underline",
|
|
1365
1541
|
children: product.name
|
|
1366
1542
|
}
|
|
1367
1543
|
),
|
|
1368
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-auto flex items-center justify-between pt-2", children: [
|
|
1369
|
-
/* @__PURE__ */ jsx(
|
|
1544
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-auto flex items-center justify-between gap-3 pt-2", children: [
|
|
1545
|
+
purchaseMode ? /* @__PURE__ */ jsx(
|
|
1370
1546
|
Price,
|
|
1371
1547
|
{
|
|
1372
1548
|
value: product.price,
|
|
1373
1549
|
compareAt: product.compareAtPrice,
|
|
1374
|
-
testId:
|
|
1550
|
+
testId: productCardComponentContract.priceTestId
|
|
1375
1551
|
}
|
|
1376
|
-
),
|
|
1552
|
+
) : /* @__PURE__ */ jsx("span", { "data-testid": productCardComponentContract.priceTestId, className: "text-sm text-muted-foreground", children: config.commerceMode === "enquiry" ? config.enquiry.label : "Ver detalhes" }),
|
|
1377
1553
|
/* @__PURE__ */ jsx(
|
|
1378
1554
|
"button",
|
|
1379
1555
|
{
|
|
1380
1556
|
type: "button",
|
|
1381
|
-
...
|
|
1382
|
-
disabled: soldOut,
|
|
1383
|
-
"aria-label": hasOptions ? `Escolher op\xE7\xF5es de ${product.name}` : `Adicionar ${product.name} ao carrinho`,
|
|
1557
|
+
...productCardComponentContract.addButton,
|
|
1558
|
+
disabled: purchaseMode && soldOut,
|
|
1559
|
+
"aria-label": purchaseMode ? hasOptions ? `Escolher op\xE7\xF5es de ${product.name}` : `Adicionar ${product.name} ao carrinho` : `${config.enquiry.label} ${product.name}`,
|
|
1384
1560
|
onClick: () => {
|
|
1561
|
+
if (!purchaseMode) {
|
|
1562
|
+
navigateTo(`/product/${product.slug}`);
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1385
1565
|
if (hasOptions) {
|
|
1386
1566
|
navigateTo(`/product/${product.slug}`);
|
|
1387
1567
|
return;
|
|
1388
1568
|
}
|
|
1389
|
-
|
|
1569
|
+
actions.addToCart(product);
|
|
1390
1570
|
},
|
|
1391
1571
|
className: "rounded-full border bg-background/90 p-2.5 text-foreground shadow-sm transition-all duration-200 hover:scale-110 hover:bg-primary hover:text-primary-foreground hover:shadow disabled:cursor-not-allowed disabled:opacity-40 lg:translate-y-1 lg:opacity-0 lg:group-hover:translate-y-0 lg:group-hover:opacity-100 lg:disabled:opacity-0 lg:group-hover:disabled:opacity-40",
|
|
1392
1572
|
children: /* @__PURE__ */ jsx(ShoppingBag, { className: "h-4 w-4" })
|
|
@@ -1400,14 +1580,25 @@ function ProductCard({ product }) {
|
|
|
1400
1580
|
}
|
|
1401
1581
|
function ProductGrid({ products, loading }) {
|
|
1402
1582
|
const config = useStorefrontConfig();
|
|
1403
|
-
const
|
|
1583
|
+
const actions = useStorefrontActions();
|
|
1584
|
+
const components = getStorefrontComponents(config);
|
|
1585
|
+
const ProductCardComponent = components.ProductCard ?? ProductCard;
|
|
1404
1586
|
if (loading) {
|
|
1405
1587
|
return /* @__PURE__ */ jsx("div", { "data-testid": TID.productGrid, className: "grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsx("div", { className: "aspect-[3/4] animate-pulse rounded-xl bg-muted" }, i)) });
|
|
1406
1588
|
}
|
|
1407
1589
|
if (products.length === 0) {
|
|
1408
1590
|
return /* @__PURE__ */ jsx("div", { "data-testid": TID.productGrid, className: "py-20 text-center text-muted-foreground", children: "Nenhum produto encontrado." });
|
|
1409
1591
|
}
|
|
1410
|
-
return /* @__PURE__ */ jsx("div", { "data-testid": TID.productGrid, className: "grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4", children: products.map((p) => /* @__PURE__ */ jsx(
|
|
1592
|
+
return /* @__PURE__ */ jsx("div", { "data-testid": TID.productGrid, className: "grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4", children: products.map((p) => /* @__PURE__ */ jsx(
|
|
1593
|
+
ProductCardComponent,
|
|
1594
|
+
{
|
|
1595
|
+
product: p,
|
|
1596
|
+
config,
|
|
1597
|
+
commerceMode: config.commerceMode,
|
|
1598
|
+
actions
|
|
1599
|
+
},
|
|
1600
|
+
p.id
|
|
1601
|
+
)) });
|
|
1411
1602
|
}
|
|
1412
1603
|
function FiltersPanel() {
|
|
1413
1604
|
const { categories } = useCategories();
|
|
@@ -1645,8 +1836,15 @@ function HeroSection({
|
|
|
1645
1836
|
height = "tall"
|
|
1646
1837
|
}) {
|
|
1647
1838
|
const [index, setIndex] = useState(0);
|
|
1839
|
+
const [loadedSlides, setLoadedSlides] = useState({});
|
|
1840
|
+
const config = useStorefrontConfig();
|
|
1648
1841
|
const interacted = useRef(false);
|
|
1649
1842
|
const hClass = height === "tall" ? "h-[72vh] min-h-[440px]" : "h-[48vh] min-h-[340px]";
|
|
1843
|
+
const revealImages = config.imageLoading.mode !== "none";
|
|
1844
|
+
const revealStyle = (loaded) => revealImages ? {
|
|
1845
|
+
filter: config.imageLoading.blur && !loaded ? "blur(10px)" : "blur(0px)",
|
|
1846
|
+
transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`
|
|
1847
|
+
} : void 0;
|
|
1650
1848
|
useEffect(() => {
|
|
1651
1849
|
if (variant !== "slider" || slides.length < 2 || prefersReducedMotion()) return;
|
|
1652
1850
|
const timer = setInterval(() => {
|
|
@@ -1667,7 +1865,7 @@ function HeroSection({
|
|
|
1667
1865
|
className: "group relative block h-[42vh] min-h-[320px] overflow-hidden",
|
|
1668
1866
|
children: [
|
|
1669
1867
|
/* @__PURE__ */ jsx(
|
|
1670
|
-
|
|
1868
|
+
SmoothImage,
|
|
1671
1869
|
{
|
|
1672
1870
|
src: slideImage(slide, 900, 700),
|
|
1673
1871
|
alt: slide.title,
|
|
@@ -1699,7 +1897,11 @@ function HeroSection({
|
|
|
1699
1897
|
src: slideImage(slide),
|
|
1700
1898
|
alt: slide.title,
|
|
1701
1899
|
"aria-hidden": i !== index,
|
|
1702
|
-
|
|
1900
|
+
loading: i === 0 ? "eager" : "lazy",
|
|
1901
|
+
decoding: "async",
|
|
1902
|
+
onLoad: () => setLoadedSlides((current) => ({ ...current, [i]: true })),
|
|
1903
|
+
style: revealStyle(Boolean(loadedSlides[i])),
|
|
1904
|
+
className: `absolute inset-0 h-full w-full object-cover transition-opacity duration-700 ${i === index && (!revealImages || loadedSlides[i]) ? "animate-kenburns opacity-100" : "opacity-0"}`
|
|
1703
1905
|
},
|
|
1704
1906
|
i
|
|
1705
1907
|
)),
|
|
@@ -1761,7 +1963,7 @@ function CategoryShowcase({ style, title }) {
|
|
|
1761
1963
|
title && /* @__PURE__ */ jsx(Reveal, { children: /* @__PURE__ */ jsx("h2", { className: "sf-heading mb-8 text-center text-3xl font-bold", children: title }) }),
|
|
1762
1964
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-start justify-center gap-8", children: categories.map((c, i) => /* @__PURE__ */ jsx(Reveal, { delay: i * 80, children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => go(c.id), className: "group flex w-28 flex-col items-center gap-3", children: [
|
|
1763
1965
|
/* @__PURE__ */ jsx("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", children: /* @__PURE__ */ jsx(
|
|
1764
|
-
|
|
1966
|
+
SmoothImage,
|
|
1765
1967
|
{
|
|
1766
1968
|
src: c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 200, 200),
|
|
1767
1969
|
alt: c.name,
|
|
@@ -1776,7 +1978,7 @@ function CategoryShowcase({ style, title }) {
|
|
|
1776
1978
|
title && /* @__PURE__ */ jsx(Reveal, { children: /* @__PURE__ */ jsx("h2", { className: "sf-heading mb-8 text-center text-3xl font-bold", children: title }) }),
|
|
1777
1979
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-4", children: categories.map((c, i) => /* @__PURE__ */ jsx(Reveal, { delay: i * 90, children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => go(c.id), className: "group relative block aspect-[4/5] w-full overflow-hidden", style: { borderRadius: "var(--sf-radius-card)" }, children: [
|
|
1778
1980
|
/* @__PURE__ */ jsx(
|
|
1779
|
-
|
|
1981
|
+
SmoothImage,
|
|
1780
1982
|
{
|
|
1781
1983
|
src: c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 480, 600),
|
|
1782
1984
|
alt: c.name,
|
|
@@ -1798,7 +2000,9 @@ function ProductRail({
|
|
|
1798
2000
|
limit = 4
|
|
1799
2001
|
}) {
|
|
1800
2002
|
const config = useStorefrontConfig();
|
|
1801
|
-
const
|
|
2003
|
+
const actions = useStorefrontActions();
|
|
2004
|
+
const components = getStorefrontComponents(config);
|
|
2005
|
+
const ProductCardComponent = components.ProductCard ?? ProductCard;
|
|
1802
2006
|
const { products, loading } = useProducts(
|
|
1803
2007
|
filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
|
|
1804
2008
|
);
|
|
@@ -1814,7 +2018,15 @@ function ProductRail({
|
|
|
1814
2018
|
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-3xl font-bold tracking-tight", children: title }),
|
|
1815
2019
|
subtitle && /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: subtitle })
|
|
1816
2020
|
] }),
|
|
1817
|
-
loading ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: Array.from({ length: limit }, (_, i) => /* @__PURE__ */ jsx("div", { className: "aspect-[3/4] animate-pulse bg-muted", style: { borderRadius: "var(--sf-radius-card)" } }, i)) }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: visible.map((p, i) => /* @__PURE__ */ jsx(Reveal, { delay: i * 90, children: /* @__PURE__ */ jsx(
|
|
2021
|
+
loading ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: Array.from({ length: limit }, (_, i) => /* @__PURE__ */ jsx("div", { className: "aspect-[3/4] animate-pulse bg-muted", style: { borderRadius: "var(--sf-radius-card)" } }, i)) }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: visible.map((p, i) => /* @__PURE__ */ jsx(Reveal, { delay: i * 90, children: /* @__PURE__ */ jsx(
|
|
2022
|
+
ProductCardComponent,
|
|
2023
|
+
{
|
|
2024
|
+
product: p,
|
|
2025
|
+
config,
|
|
2026
|
+
commerceMode: config.commerceMode,
|
|
2027
|
+
actions
|
|
2028
|
+
}
|
|
2029
|
+
) }, p.id)) }),
|
|
1818
2030
|
/* @__PURE__ */ jsx(Reveal, { className: "mt-10 text-center", delay: 200, children: /* @__PURE__ */ jsx(
|
|
1819
2031
|
Link,
|
|
1820
2032
|
{
|
|
@@ -1826,6 +2038,201 @@ function ProductRail({
|
|
|
1826
2038
|
) })
|
|
1827
2039
|
] });
|
|
1828
2040
|
}
|
|
2041
|
+
var heightClass = {
|
|
2042
|
+
medium: "h-[58vh] min-h-[420px]",
|
|
2043
|
+
tall: "h-[74vh] min-h-[520px]",
|
|
2044
|
+
screen: "min-h-[calc(100vh-64px)]"
|
|
2045
|
+
};
|
|
2046
|
+
var alignClass = {
|
|
2047
|
+
start: "items-start text-left",
|
|
2048
|
+
center: "items-center text-center",
|
|
2049
|
+
end: "items-end text-right"
|
|
2050
|
+
};
|
|
2051
|
+
function MediaCarousel({
|
|
2052
|
+
items,
|
|
2053
|
+
height = "tall",
|
|
2054
|
+
autoplayMs = 7e3,
|
|
2055
|
+
overlay = "dark",
|
|
2056
|
+
className = ""
|
|
2057
|
+
}) {
|
|
2058
|
+
const [index, setIndex] = useState(0);
|
|
2059
|
+
const [loadedItems, setLoadedItems] = useState({});
|
|
2060
|
+
const config = useStorefrontConfig();
|
|
2061
|
+
const interacted = useRef(false);
|
|
2062
|
+
const revealImages = config.imageLoading.mode !== "none";
|
|
2063
|
+
const revealStyle = (loaded) => revealImages ? {
|
|
2064
|
+
filter: config.imageLoading.blur && !loaded ? "blur(10px)" : "blur(0px)",
|
|
2065
|
+
transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`
|
|
2066
|
+
} : void 0;
|
|
2067
|
+
useEffect(() => {
|
|
2068
|
+
if (items.length < 2 || autoplayMs <= 0 || prefersReducedMotion()) return;
|
|
2069
|
+
const timer = setInterval(() => {
|
|
2070
|
+
if (!interacted.current) setIndex((i) => (i + 1) % items.length);
|
|
2071
|
+
}, autoplayMs);
|
|
2072
|
+
return () => clearInterval(timer);
|
|
2073
|
+
}, [autoplayMs, items.length]);
|
|
2074
|
+
if (items.length === 0) return null;
|
|
2075
|
+
const active = items[Math.min(index, items.length - 1)];
|
|
2076
|
+
const goTo = (next) => {
|
|
2077
|
+
interacted.current = true;
|
|
2078
|
+
setIndex((next + items.length) % items.length);
|
|
2079
|
+
};
|
|
2080
|
+
const overlayClass = overlay === "dark" ? "bg-gradient-to-r from-black/70 via-black/25 to-black/25" : overlay === "soft" ? "bg-gradient-to-t from-black/45 via-black/10 to-transparent" : "";
|
|
2081
|
+
return /* @__PURE__ */ jsxs("section", { "data-testid": TID.mediaCarousel, className: `relative isolate overflow-hidden ${heightClass[height]} ${className}`, children: [
|
|
2082
|
+
items.map((item, i) => /* @__PURE__ */ jsx(
|
|
2083
|
+
"img",
|
|
2084
|
+
{
|
|
2085
|
+
src: item.image,
|
|
2086
|
+
alt: item.imageAlt ?? item.title,
|
|
2087
|
+
"aria-hidden": i !== index,
|
|
2088
|
+
loading: i === 0 ? "eager" : "lazy",
|
|
2089
|
+
decoding: "async",
|
|
2090
|
+
onLoad: () => setLoadedItems((current) => ({ ...current, [i]: true })),
|
|
2091
|
+
style: revealStyle(Boolean(loadedItems[i])),
|
|
2092
|
+
className: `absolute inset-0 -z-10 h-full w-full object-cover transition-opacity duration-700 ${i === index && (!revealImages || loadedItems[i]) ? "opacity-100" : "opacity-0"}`
|
|
2093
|
+
},
|
|
2094
|
+
`${item.title}-${i}`
|
|
2095
|
+
)),
|
|
2096
|
+
/* @__PURE__ */ jsx("div", { className: `absolute inset-0 -z-10 ${overlayClass}` }),
|
|
2097
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-full px-5 py-16 sm:px-10 lg:px-16 ${alignClass[active.align ?? "start"]}`, children: /* @__PURE__ */ jsxs("div", { className: "my-auto max-w-2xl text-white drop-shadow-md", children: [
|
|
2098
|
+
active.eyebrow && /* @__PURE__ */ jsx("p", { className: "mb-4 text-xs font-semibold uppercase tracking-[0.24em] text-white/80", children: active.eyebrow }),
|
|
2099
|
+
/* @__PURE__ */ jsx("h1", { className: "sf-heading text-5xl font-normal leading-[0.95] sm:text-7xl lg:text-8xl", children: active.title }),
|
|
2100
|
+
active.subtitle && /* @__PURE__ */ jsx("p", { className: "mt-5 max-w-xl text-base leading-8 text-white/88 sm:text-lg", children: active.subtitle }),
|
|
2101
|
+
active.cta && /* @__PURE__ */ jsx(
|
|
2102
|
+
Link,
|
|
2103
|
+
{
|
|
2104
|
+
to: active.href ?? "/catalog",
|
|
2105
|
+
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",
|
|
2106
|
+
children: active.cta
|
|
2107
|
+
}
|
|
2108
|
+
)
|
|
2109
|
+
] }) }),
|
|
2110
|
+
items.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2111
|
+
/* @__PURE__ */ jsx(
|
|
2112
|
+
"button",
|
|
2113
|
+
{
|
|
2114
|
+
type: "button",
|
|
2115
|
+
"aria-label": "Previous slide",
|
|
2116
|
+
"data-testid": TID.mediaCarouselPrev,
|
|
2117
|
+
onClick: () => goTo(index - 1),
|
|
2118
|
+
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",
|
|
2119
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
|
|
2120
|
+
}
|
|
2121
|
+
),
|
|
2122
|
+
/* @__PURE__ */ jsx(
|
|
2123
|
+
"button",
|
|
2124
|
+
{
|
|
2125
|
+
type: "button",
|
|
2126
|
+
"aria-label": "Next slide",
|
|
2127
|
+
"data-testid": TID.mediaCarouselNext,
|
|
2128
|
+
onClick: () => goTo(index + 1),
|
|
2129
|
+
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",
|
|
2130
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
2131
|
+
}
|
|
2132
|
+
),
|
|
2133
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-5 left-1/2 flex -translate-x-1/2 gap-2", children: items.map((item, i) => /* @__PURE__ */ jsx(
|
|
2134
|
+
"button",
|
|
2135
|
+
{
|
|
2136
|
+
type: "button",
|
|
2137
|
+
"aria-label": `Go to slide ${i + 1}`,
|
|
2138
|
+
onClick: () => goTo(i),
|
|
2139
|
+
className: `h-2 rounded-full transition-all ${i === index ? "w-8 bg-white" : "w-2 bg-white/55 hover:bg-white"}`
|
|
2140
|
+
},
|
|
2141
|
+
`${item.title}-dot-${i}`
|
|
2142
|
+
)) })
|
|
2143
|
+
] })
|
|
2144
|
+
] });
|
|
2145
|
+
}
|
|
2146
|
+
function ProductSlider({
|
|
2147
|
+
title,
|
|
2148
|
+
subtitle,
|
|
2149
|
+
eyebrow,
|
|
2150
|
+
filter = "all",
|
|
2151
|
+
categoryName,
|
|
2152
|
+
collection,
|
|
2153
|
+
limit = 10,
|
|
2154
|
+
cta = "View all",
|
|
2155
|
+
href,
|
|
2156
|
+
className = ""
|
|
2157
|
+
}) {
|
|
2158
|
+
const config = useStorefrontConfig();
|
|
2159
|
+
const actions = useStorefrontActions();
|
|
2160
|
+
const viewportRef = useRef(null);
|
|
2161
|
+
const components = getStorefrontComponents(config);
|
|
2162
|
+
const ProductCardComponent = components.ProductCard ?? ProductCard;
|
|
2163
|
+
const { products, loading } = useProducts(
|
|
2164
|
+
filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
|
|
2165
|
+
);
|
|
2166
|
+
const visible = useMemo(() => {
|
|
2167
|
+
let list = products;
|
|
2168
|
+
if (filter === "sale") list = list.filter((p) => p.compareAtPrice != null && p.compareAtPrice > p.price);
|
|
2169
|
+
if (categoryName) list = list.filter((p) => p.categoryName === categoryName);
|
|
2170
|
+
if (collection) list = list.filter((p) => p.metadata?.collection === collection);
|
|
2171
|
+
return list.slice(0, limit);
|
|
2172
|
+
}, [categoryName, collection, filter, limit, products]);
|
|
2173
|
+
const scroll = (direction) => {
|
|
2174
|
+
viewportRef.current?.scrollBy({ left: direction * Math.max(320, viewportRef.current.clientWidth * 0.75), behavior: "smooth" });
|
|
2175
|
+
};
|
|
2176
|
+
if (!loading && visible.length === 0) return null;
|
|
2177
|
+
return /* @__PURE__ */ jsxs("section", { "data-testid": TID.productSlider, className: `mx-auto max-w-7xl px-4 py-14 sm:px-6 ${className}`, children: [
|
|
2178
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-8 flex flex-col gap-5 sm:flex-row sm:items-end sm:justify-between", children: [
|
|
2179
|
+
/* @__PURE__ */ jsxs("div", { className: "max-w-2xl", children: [
|
|
2180
|
+
eyebrow && /* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-primary", children: eyebrow }),
|
|
2181
|
+
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-3xl font-normal leading-tight tracking-normal sm:text-5xl", children: title }),
|
|
2182
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "mt-3 leading-7 text-muted-foreground", children: subtitle })
|
|
2183
|
+
] }),
|
|
2184
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2185
|
+
/* @__PURE__ */ jsx(
|
|
2186
|
+
"button",
|
|
2187
|
+
{
|
|
2188
|
+
type: "button",
|
|
2189
|
+
"aria-label": "Previous products",
|
|
2190
|
+
"data-testid": TID.productSliderPrev,
|
|
2191
|
+
onClick: () => scroll(-1),
|
|
2192
|
+
className: "inline-flex h-10 w-10 items-center justify-center border border-border bg-background transition hover:bg-foreground hover:text-background",
|
|
2193
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
|
|
2194
|
+
}
|
|
2195
|
+
),
|
|
2196
|
+
/* @__PURE__ */ jsx(
|
|
2197
|
+
"button",
|
|
2198
|
+
{
|
|
2199
|
+
type: "button",
|
|
2200
|
+
"aria-label": "Next products",
|
|
2201
|
+
"data-testid": TID.productSliderNext,
|
|
2202
|
+
onClick: () => scroll(1),
|
|
2203
|
+
className: "inline-flex h-10 w-10 items-center justify-center border border-border bg-background transition hover:bg-foreground hover:text-background",
|
|
2204
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
2205
|
+
}
|
|
2206
|
+
)
|
|
2207
|
+
] })
|
|
2208
|
+
] }),
|
|
2209
|
+
/* @__PURE__ */ jsx(
|
|
2210
|
+
"div",
|
|
2211
|
+
{
|
|
2212
|
+
ref: viewportRef,
|
|
2213
|
+
"data-testid": TID.productSliderViewport,
|
|
2214
|
+
className: "flex snap-x gap-5 overflow-x-auto pb-5 [scrollbar-width:thin]",
|
|
2215
|
+
children: loading ? Array.from({ length: Math.min(limit, 6) }, (_, i) => /* @__PURE__ */ jsx("div", { className: "h-[360px] min-w-[260px] snap-start animate-pulse bg-muted" }, i)) : visible.map((product) => /* @__PURE__ */ jsx("div", { className: "min-w-[260px] max-w-[300px] flex-[0_0_72vw] snap-start sm:flex-[0_0_300px]", children: /* @__PURE__ */ jsx(
|
|
2216
|
+
ProductCardComponent,
|
|
2217
|
+
{
|
|
2218
|
+
product,
|
|
2219
|
+
config,
|
|
2220
|
+
commerceMode: config.commerceMode,
|
|
2221
|
+
actions
|
|
2222
|
+
}
|
|
2223
|
+
) }, product.id))
|
|
2224
|
+
}
|
|
2225
|
+
),
|
|
2226
|
+
cta && /* @__PURE__ */ jsx(
|
|
2227
|
+
Link,
|
|
2228
|
+
{
|
|
2229
|
+
to: href ?? config.catalogPath,
|
|
2230
|
+
className: "mt-4 inline-flex border border-foreground/30 px-6 py-3 text-xs font-semibold uppercase tracking-[0.16em] transition hover:bg-foreground hover:text-background",
|
|
2231
|
+
children: cta
|
|
2232
|
+
}
|
|
2233
|
+
)
|
|
2234
|
+
] });
|
|
2235
|
+
}
|
|
1829
2236
|
function Icon({ name, className }) {
|
|
1830
2237
|
const C = LucideIcons[name];
|
|
1831
2238
|
return C ? /* @__PURE__ */ jsx(C, { className }) : null;
|
|
@@ -1850,7 +2257,7 @@ function PromoBanner({
|
|
|
1850
2257
|
}) {
|
|
1851
2258
|
return /* @__PURE__ */ jsxs("section", { "data-testid": TID.promoBanner, className: "group relative my-12 overflow-hidden", children: [
|
|
1852
2259
|
/* @__PURE__ */ jsx(
|
|
1853
|
-
|
|
2260
|
+
SmoothImage,
|
|
1854
2261
|
{
|
|
1855
2262
|
src: image ?? bannerPlaceholder(title, hue, hue + 50, 1600, 420),
|
|
1856
2263
|
alt: title,
|
|
@@ -1953,8 +2360,10 @@ function registerStorefrontBlocks() {
|
|
|
1953
2360
|
if (registered) return;
|
|
1954
2361
|
registered = true;
|
|
1955
2362
|
registerBlock("hero", HeroSection, { source: "sdk", label: "Hero" });
|
|
2363
|
+
registerBlock("mediaCarousel", MediaCarousel, { source: "sdk", label: "Media carousel" });
|
|
1956
2364
|
registerBlock("categories", CategoryShowcase, { source: "sdk", label: "Categories" });
|
|
1957
2365
|
registerBlock("products", ProductRail, { source: "sdk", label: "Product rail" });
|
|
2366
|
+
registerBlock("productSlider", ProductSlider, { source: "sdk", label: "Product slider" });
|
|
1958
2367
|
registerBlock("benefits", BenefitsRow, { source: "sdk", label: "Benefits row" });
|
|
1959
2368
|
registerBlock("banner", PromoBanner, { source: "sdk", label: "Promo banner" });
|
|
1960
2369
|
registerBlock("manifesto", ManifestoBlock, { source: "sdk", label: "Manifesto" });
|
|
@@ -1968,9 +2377,12 @@ function sectionsToBlocks(sections) {
|
|
|
1968
2377
|
return { type, id: `${type}-${i}`, props };
|
|
1969
2378
|
});
|
|
1970
2379
|
}
|
|
1971
|
-
function
|
|
2380
|
+
function StorefrontSections({ sections }) {
|
|
1972
2381
|
return /* @__PURE__ */ jsx("main", { children: renderBlocks(sectionsToBlocks(sections)) });
|
|
1973
2382
|
}
|
|
2383
|
+
function HomePage({ sections }) {
|
|
2384
|
+
return /* @__PURE__ */ jsx(StorefrontSections, { sections });
|
|
2385
|
+
}
|
|
1974
2386
|
function useProduct(slug2) {
|
|
1975
2387
|
const [product, setProduct] = useState(null);
|
|
1976
2388
|
const [loading, setLoading] = useState(true);
|
|
@@ -2082,18 +2494,208 @@ function ProductSpecs({ product }) {
|
|
|
2082
2494
|
}
|
|
2083
2495
|
function RelatedProducts({ product }) {
|
|
2084
2496
|
const config = useStorefrontConfig();
|
|
2085
|
-
const
|
|
2497
|
+
const actions = useStorefrontActions();
|
|
2498
|
+
const components = getStorefrontComponents(config);
|
|
2499
|
+
const Card = components.ProductCard ?? ProductCard;
|
|
2086
2500
|
const opts = product.categoryId ? { categoryId: product.categoryId, status: "active", limit: 5 } : { status: "active", limit: 5 };
|
|
2087
2501
|
const { products, loading } = useProducts(opts);
|
|
2088
2502
|
const related = products.filter((p) => p.id !== product.id).slice(0, 4);
|
|
2089
2503
|
if (loading || related.length === 0) return null;
|
|
2090
2504
|
return /* @__PURE__ */ jsxs("section", { className: "mt-16", children: [
|
|
2091
2505
|
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-xl font-semibold tracking-tight", children: "Voc\xEA tamb\xE9m pode gostar" }),
|
|
2092
|
-
/* @__PURE__ */ jsx("div", { className: "mt-6 grid grid-cols-2 gap-5 md:grid-cols-4", children: related.map((p) => /* @__PURE__ */ jsx(
|
|
2506
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6 grid grid-cols-2 gap-5 md:grid-cols-4", children: related.map((p) => /* @__PURE__ */ jsx(
|
|
2507
|
+
Card,
|
|
2508
|
+
{
|
|
2509
|
+
product: p,
|
|
2510
|
+
config,
|
|
2511
|
+
commerceMode: config.commerceMode,
|
|
2512
|
+
actions
|
|
2513
|
+
},
|
|
2514
|
+
p.id
|
|
2515
|
+
)) })
|
|
2093
2516
|
] });
|
|
2094
2517
|
}
|
|
2518
|
+
function defaultSubject(prefix, product) {
|
|
2519
|
+
return `${prefix}: ${product.name}`;
|
|
2520
|
+
}
|
|
2521
|
+
function buildWhatsAppHref(url, product) {
|
|
2522
|
+
const text = encodeURIComponent(`Hello, I would like more information about ${product.name}.`);
|
|
2523
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
2524
|
+
return `${url}${sep}text=${text}`;
|
|
2525
|
+
}
|
|
2526
|
+
function ProductEnquiryForm({ product, onSuccess }) {
|
|
2527
|
+
const config = useStorefrontConfig();
|
|
2528
|
+
const [customerName, setCustomerName] = useState("");
|
|
2529
|
+
const [customerEmail, setCustomerEmail] = useState("");
|
|
2530
|
+
const [customerPhone, setCustomerPhone] = useState("");
|
|
2531
|
+
const [subject, setSubject] = useState(defaultSubject(config.enquiry.subjectPrefix, product));
|
|
2532
|
+
const [message, setMessage] = useState(`I would like more information about ${product.name}.`);
|
|
2533
|
+
const [status, setStatus] = useState("idle");
|
|
2534
|
+
const [error, setError] = useState(null);
|
|
2535
|
+
const submit = async (event) => {
|
|
2536
|
+
event.preventDefault();
|
|
2537
|
+
setStatus("submitting");
|
|
2538
|
+
setError(null);
|
|
2539
|
+
try {
|
|
2540
|
+
const provider = getShopProvider();
|
|
2541
|
+
if (provider.createProductEnquiry) {
|
|
2542
|
+
await provider.createProductEnquiry({
|
|
2543
|
+
productId: product.id,
|
|
2544
|
+
productName: product.name,
|
|
2545
|
+
productSlug: product.slug,
|
|
2546
|
+
customerName,
|
|
2547
|
+
customerEmail,
|
|
2548
|
+
customerPhone: customerPhone || void 0,
|
|
2549
|
+
subject,
|
|
2550
|
+
message,
|
|
2551
|
+
sourceUrl: typeof window === "undefined" ? void 0 : window.location.href,
|
|
2552
|
+
metadata: { sku: product.sku, categoryName: product.categoryName }
|
|
2553
|
+
});
|
|
2554
|
+
} else if (config.enquiry.email) {
|
|
2555
|
+
window.location.href = `mailto:${config.enquiry.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(message)}`;
|
|
2556
|
+
}
|
|
2557
|
+
setStatus("success");
|
|
2558
|
+
onSuccess?.();
|
|
2559
|
+
} catch (e) {
|
|
2560
|
+
setStatus("error");
|
|
2561
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
2562
|
+
}
|
|
2563
|
+
};
|
|
2564
|
+
return /* @__PURE__ */ jsxs(
|
|
2565
|
+
"form",
|
|
2566
|
+
{
|
|
2567
|
+
...storefrontComponentContracts.productDetail.enquiryForm,
|
|
2568
|
+
onSubmit: submit,
|
|
2569
|
+
className: "space-y-4",
|
|
2570
|
+
children: [
|
|
2571
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
|
|
2572
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5 text-sm font-medium", children: [
|
|
2573
|
+
/* @__PURE__ */ jsx("span", { children: "Your name" }),
|
|
2574
|
+
/* @__PURE__ */ jsx(
|
|
2575
|
+
"input",
|
|
2576
|
+
{
|
|
2577
|
+
"data-testid": TID.enquiryName,
|
|
2578
|
+
required: true,
|
|
2579
|
+
value: customerName,
|
|
2580
|
+
onChange: (e) => setCustomerName(e.target.value),
|
|
2581
|
+
className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2582
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2583
|
+
}
|
|
2584
|
+
)
|
|
2585
|
+
] }),
|
|
2586
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5 text-sm font-medium", children: [
|
|
2587
|
+
/* @__PURE__ */ jsx("span", { children: "Your email" }),
|
|
2588
|
+
/* @__PURE__ */ jsx(
|
|
2589
|
+
"input",
|
|
2590
|
+
{
|
|
2591
|
+
"data-testid": TID.enquiryEmail,
|
|
2592
|
+
required: true,
|
|
2593
|
+
type: "email",
|
|
2594
|
+
value: customerEmail,
|
|
2595
|
+
onChange: (e) => setCustomerEmail(e.target.value),
|
|
2596
|
+
className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2597
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2598
|
+
}
|
|
2599
|
+
)
|
|
2600
|
+
] })
|
|
2601
|
+
] }),
|
|
2602
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
|
|
2603
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
2604
|
+
"Phone",
|
|
2605
|
+
config.enquiry.requirePhone ? "" : " (optional)"
|
|
2606
|
+
] }),
|
|
2607
|
+
/* @__PURE__ */ jsx(
|
|
2608
|
+
"input",
|
|
2609
|
+
{
|
|
2610
|
+
"data-testid": TID.enquiryPhone,
|
|
2611
|
+
required: config.enquiry.requirePhone,
|
|
2612
|
+
value: customerPhone,
|
|
2613
|
+
onChange: (e) => setCustomerPhone(e.target.value),
|
|
2614
|
+
className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2615
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2616
|
+
}
|
|
2617
|
+
)
|
|
2618
|
+
] }),
|
|
2619
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
|
|
2620
|
+
/* @__PURE__ */ jsx("span", { children: "Subject" }),
|
|
2621
|
+
/* @__PURE__ */ jsx(
|
|
2622
|
+
"input",
|
|
2623
|
+
{
|
|
2624
|
+
"data-testid": TID.enquirySubject,
|
|
2625
|
+
required: true,
|
|
2626
|
+
value: subject,
|
|
2627
|
+
onChange: (e) => setSubject(e.target.value),
|
|
2628
|
+
className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2629
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2630
|
+
}
|
|
2631
|
+
)
|
|
2632
|
+
] }),
|
|
2633
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
|
|
2634
|
+
/* @__PURE__ */ jsx("span", { children: "Message" }),
|
|
2635
|
+
/* @__PURE__ */ jsx(
|
|
2636
|
+
"textarea",
|
|
2637
|
+
{
|
|
2638
|
+
"data-testid": TID.enquiryMessage,
|
|
2639
|
+
required: true,
|
|
2640
|
+
rows: 5,
|
|
2641
|
+
value: message,
|
|
2642
|
+
onChange: (e) => setMessage(e.target.value),
|
|
2643
|
+
className: "w-full resize-none border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2644
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2645
|
+
}
|
|
2646
|
+
)
|
|
2647
|
+
] }),
|
|
2648
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
2649
|
+
/* @__PURE__ */ jsxs(
|
|
2650
|
+
"button",
|
|
2651
|
+
{
|
|
2652
|
+
"data-testid": TID.enquirySubmit,
|
|
2653
|
+
type: "submit",
|
|
2654
|
+
disabled: status === "submitting",
|
|
2655
|
+
className: "sf-cta inline-flex items-center gap-2 bg-primary px-5 py-3 font-semibold text-primary-foreground disabled:cursor-wait disabled:opacity-60",
|
|
2656
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2657
|
+
children: [
|
|
2658
|
+
/* @__PURE__ */ jsx(Send, { className: "h-4 w-4" }),
|
|
2659
|
+
status === "submitting" ? "Sending..." : "Send enquiry"
|
|
2660
|
+
]
|
|
2661
|
+
}
|
|
2662
|
+
),
|
|
2663
|
+
config.enquiry.whatsappUrl && /* @__PURE__ */ jsxs(
|
|
2664
|
+
"a",
|
|
2665
|
+
{
|
|
2666
|
+
href: buildWhatsAppHref(config.enquiry.whatsappUrl, product),
|
|
2667
|
+
target: "_blank",
|
|
2668
|
+
rel: "noreferrer",
|
|
2669
|
+
className: "inline-flex items-center gap-2 border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
|
|
2670
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2671
|
+
children: [
|
|
2672
|
+
/* @__PURE__ */ jsx(MessageCircle, { className: "h-4 w-4" }),
|
|
2673
|
+
"WhatsApp"
|
|
2674
|
+
]
|
|
2675
|
+
}
|
|
2676
|
+
),
|
|
2677
|
+
config.enquiry.email && /* @__PURE__ */ jsxs(
|
|
2678
|
+
"a",
|
|
2679
|
+
{
|
|
2680
|
+
href: `mailto:${config.enquiry.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(message)}`,
|
|
2681
|
+
className: "inline-flex items-center gap-2 border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
|
|
2682
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2683
|
+
children: [
|
|
2684
|
+
/* @__PURE__ */ jsx(Mail, { className: "h-4 w-4" }),
|
|
2685
|
+
"Email"
|
|
2686
|
+
]
|
|
2687
|
+
}
|
|
2688
|
+
)
|
|
2689
|
+
] }),
|
|
2690
|
+
status === "success" && /* @__PURE__ */ jsx("p", { "data-testid": TID.enquirySuccess, className: "text-sm font-medium text-emerald-600", children: config.enquiry.successMessage }),
|
|
2691
|
+
error && /* @__PURE__ */ jsx("p", { "data-testid": TID.enquiryError, className: "text-sm font-medium text-red-600", children: error })
|
|
2692
|
+
]
|
|
2693
|
+
}
|
|
2694
|
+
);
|
|
2695
|
+
}
|
|
2095
2696
|
function ProductDetailPage({ slug: slug2 }) {
|
|
2096
2697
|
const config = useStorefrontConfig();
|
|
2698
|
+
const actions = useStorefrontActions();
|
|
2097
2699
|
const { product, loading } = useProduct(slug2);
|
|
2098
2700
|
const addItem = useCartStore((s) => s.addItem);
|
|
2099
2701
|
const [qty, setQty] = useState(1);
|
|
@@ -2129,21 +2731,56 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2129
2731
|
const soldOut = product.inventoryCount <= 0;
|
|
2130
2732
|
const lowStock = !soldOut && product.inventoryCount <= 5;
|
|
2131
2733
|
const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
|
|
2734
|
+
const components = getStorefrontComponents(config);
|
|
2735
|
+
const ProductDetailComponent = components.ProductDetail;
|
|
2736
|
+
const addToCart = () => addItem(product, qty, selectedOptions);
|
|
2737
|
+
const openEnquiry = () => {
|
|
2738
|
+
document.getElementById("storefront-product-enquiry")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
2739
|
+
};
|
|
2132
2740
|
const discountPct = product.compareAtPrice && product.compareAtPrice > product.price ? Math.round((1 - product.price / product.compareAtPrice) * 100) : 0;
|
|
2133
|
-
|
|
2741
|
+
if (ProductDetailComponent) {
|
|
2742
|
+
return /* @__PURE__ */ jsx(
|
|
2743
|
+
ProductDetailComponent,
|
|
2744
|
+
{
|
|
2745
|
+
product,
|
|
2746
|
+
config,
|
|
2747
|
+
commerceMode: config.commerceMode,
|
|
2748
|
+
primaryImage: image,
|
|
2749
|
+
optionGroups,
|
|
2750
|
+
selectedOptions,
|
|
2751
|
+
setSelectedOptions,
|
|
2752
|
+
quantity: qty,
|
|
2753
|
+
setQuantity: setQty,
|
|
2754
|
+
soldOut,
|
|
2755
|
+
lowStock,
|
|
2756
|
+
actions,
|
|
2757
|
+
addToCart,
|
|
2758
|
+
openEnquiry
|
|
2759
|
+
}
|
|
2760
|
+
);
|
|
2761
|
+
}
|
|
2762
|
+
return /* @__PURE__ */ jsxs("main", { ...storefrontComponentContracts.productDetail.root, className: "mx-auto max-w-5xl px-4 py-8 sm:px-6", children: [
|
|
2134
2763
|
/* @__PURE__ */ jsxs(Link, { to: "/", className: "mb-6 inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [
|
|
2135
2764
|
/* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }),
|
|
2136
2765
|
" Continuar comprando"
|
|
2137
2766
|
] }),
|
|
2138
2767
|
/* @__PURE__ */ jsxs("div", { className: "grid animate-fade-up gap-10 md:grid-cols-2", children: [
|
|
2139
|
-
/* @__PURE__ */ jsx(
|
|
2140
|
-
"
|
|
2768
|
+
/* @__PURE__ */ jsx(
|
|
2769
|
+
"div",
|
|
2141
2770
|
{
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2771
|
+
...storefrontComponentContracts.productDetail.gallery,
|
|
2772
|
+
className: "group overflow-hidden border bg-muted",
|
|
2773
|
+
style: { borderRadius: "var(--sf-radius-card)" },
|
|
2774
|
+
children: image && /* @__PURE__ */ jsx(
|
|
2775
|
+
SmoothImage,
|
|
2776
|
+
{
|
|
2777
|
+
src: image.url,
|
|
2778
|
+
alt: image.altText ?? product.name,
|
|
2779
|
+
className: "aspect-square w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
|
2780
|
+
}
|
|
2781
|
+
)
|
|
2145
2782
|
}
|
|
2146
|
-
)
|
|
2783
|
+
),
|
|
2147
2784
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col py-2 lg:sticky lg:top-24 lg:self-start", children: [
|
|
2148
2785
|
/* @__PURE__ */ jsxs("nav", { className: "mb-3 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
2149
2786
|
/* @__PURE__ */ jsx(Link, { to: "/", className: "transition-colors hover:text-foreground", children: "In\xEDcio" }),
|
|
@@ -2155,7 +2792,7 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2155
2792
|
/* @__PURE__ */ jsx("span", { className: "text-foreground", children: product.name })
|
|
2156
2793
|
] }),
|
|
2157
2794
|
/* @__PURE__ */ jsx("h1", { "data-testid": TID.pdpName, className: "sf-heading text-3xl font-bold tracking-tight", children: product.name }),
|
|
2158
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-3", children: [
|
|
2795
|
+
config.commerceMode === "checkout" && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-3", children: [
|
|
2159
2796
|
/* @__PURE__ */ jsx("div", { className: "text-xl", children: /* @__PURE__ */ jsx(
|
|
2160
2797
|
Price,
|
|
2161
2798
|
{
|
|
@@ -2172,7 +2809,7 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2172
2809
|
] })
|
|
2173
2810
|
] }),
|
|
2174
2811
|
/* @__PURE__ */ jsx("p", { "data-testid": TID.pdpDescription, className: "mt-5 leading-relaxed text-muted-foreground", children: product.description }),
|
|
2175
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm", children: [
|
|
2812
|
+
config.commerceMode === "checkout" && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm", children: [
|
|
2176
2813
|
soldOut ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 font-medium text-muted-foreground", children: [
|
|
2177
2814
|
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-muted-foreground" }),
|
|
2178
2815
|
" Sem estoque"
|
|
@@ -2198,7 +2835,7 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2198
2835
|
onChange: setSelectedOptions
|
|
2199
2836
|
}
|
|
2200
2837
|
),
|
|
2201
|
-
/* @__PURE__ */
|
|
2838
|
+
/* @__PURE__ */ jsx("div", { ...storefrontComponentContracts.productDetail.actions, className: "mt-8", children: config.commerceMode === "checkout" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
2202
2839
|
/* @__PURE__ */ jsx(
|
|
2203
2840
|
QuantityInput,
|
|
2204
2841
|
{
|
|
@@ -2214,14 +2851,32 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2214
2851
|
type: "button",
|
|
2215
2852
|
"data-testid": TID.pdpAddToCart,
|
|
2216
2853
|
disabled: soldOut,
|
|
2217
|
-
onClick:
|
|
2854
|
+
onClick: addToCart,
|
|
2218
2855
|
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",
|
|
2219
2856
|
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2220
2857
|
children: soldOut ? "Esgotado" : "Adicionar ao carrinho"
|
|
2221
2858
|
}
|
|
2222
2859
|
)
|
|
2223
|
-
] })
|
|
2224
|
-
|
|
2860
|
+
] }) : config.commerceMode === "enquiry" ? /* @__PURE__ */ jsx(
|
|
2861
|
+
"button",
|
|
2862
|
+
{
|
|
2863
|
+
type: "button",
|
|
2864
|
+
...storefrontComponentContracts.productDetail.enquiryButton,
|
|
2865
|
+
onClick: openEnquiry,
|
|
2866
|
+
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",
|
|
2867
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2868
|
+
children: config.enquiry.label
|
|
2869
|
+
}
|
|
2870
|
+
) : /* @__PURE__ */ jsx(
|
|
2871
|
+
Link,
|
|
2872
|
+
{
|
|
2873
|
+
to: config.catalogPath,
|
|
2874
|
+
className: "inline-flex border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
|
|
2875
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2876
|
+
children: "Voltar ao cat\xE1logo"
|
|
2877
|
+
}
|
|
2878
|
+
) }),
|
|
2879
|
+
config.commerceMode === "checkout" && /* @__PURE__ */ jsxs("div", { className: "mt-6 grid grid-cols-3 gap-3 border-t pt-6 text-center", children: [
|
|
2225
2880
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
|
|
2226
2881
|
/* @__PURE__ */ jsx(Truck, { className: "h-5 w-5 text-primary" }),
|
|
2227
2882
|
" Envio r\xE1pido"
|
|
@@ -2237,6 +2892,11 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2237
2892
|
] })
|
|
2238
2893
|
] })
|
|
2239
2894
|
] }),
|
|
2895
|
+
config.commerceMode === "enquiry" && /* @__PURE__ */ jsx("section", { id: "storefront-product-enquiry", className: "mt-12 border-t pt-10", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-3xl", children: [
|
|
2896
|
+
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-2xl font-semibold", children: "Need more information?" }),
|
|
2897
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: "Send an enquiry about this product." }),
|
|
2898
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsx(ProductEnquiryForm, { product }) })
|
|
2899
|
+
] }) }),
|
|
2240
2900
|
/* @__PURE__ */ jsx(ProductSpecs, { product }),
|
|
2241
2901
|
/* @__PURE__ */ jsx(RelatedProducts, { product })
|
|
2242
2902
|
] });
|
|
@@ -2792,7 +3452,7 @@ function CheckoutPage() {
|
|
|
2792
3452
|
/* @__PURE__ */ jsx("h2", { className: "sr-only", children: "Resumo do pedido" }),
|
|
2793
3453
|
/* @__PURE__ */ jsx("ul", { className: "space-y-4", children: cart.lines.map((line) => /* @__PURE__ */ jsxs("li", { className: "flex gap-3 text-sm", children: [
|
|
2794
3454
|
/* @__PURE__ */ jsxs("div", { className: "relative h-16 w-16 flex-none overflow-hidden rounded-lg border bg-background", children: [
|
|
2795
|
-
line.imageUrl && /* @__PURE__ */ jsx(
|
|
3455
|
+
line.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: line.imageUrl, alt: line.name, className: "h-full w-full object-cover" }),
|
|
2796
3456
|
/* @__PURE__ */ jsx("span", { className: "absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-muted-foreground px-1.5 text-[10px] font-bold text-background", children: line.quantity })
|
|
2797
3457
|
] }),
|
|
2798
3458
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
@@ -3214,7 +3874,7 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
3214
3874
|
/* @__PURE__ */ jsx(OrderTrackingTimeline, { order })
|
|
3215
3875
|
] }),
|
|
3216
3876
|
/* @__PURE__ */ jsx("ul", { className: "mt-8 space-y-3 text-left", children: order.items.map((item) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 text-sm", children: [
|
|
3217
|
-
item.imageUrl && /* @__PURE__ */ jsx(
|
|
3877
|
+
item.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: item.imageUrl, alt: item.name, className: "h-12 w-12 rounded-lg border object-cover" }),
|
|
3218
3878
|
/* @__PURE__ */ jsxs("span", { className: "flex-1", children: [
|
|
3219
3879
|
item.name,
|
|
3220
3880
|
" ",
|
|
@@ -3422,7 +4082,7 @@ function OrdersPanel() {
|
|
|
3422
4082
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: new Date(order.createdAt).toLocaleDateString(config.locale) }),
|
|
3423
4083
|
/* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(OrderTrackingTimeline, { order, compact: true }) }),
|
|
3424
4084
|
/* @__PURE__ */ jsx("ul", { className: "mt-3 flex flex-wrap gap-2", children: order.items.map((item) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2 rounded-lg border px-2 py-1 text-xs", children: [
|
|
3425
|
-
item.imageUrl && /* @__PURE__ */ jsx(
|
|
4085
|
+
item.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: item.imageUrl, alt: item.name, className: "h-6 w-6 rounded object-cover" }),
|
|
3426
4086
|
item.name,
|
|
3427
4087
|
" \xD7 ",
|
|
3428
4088
|
item.quantity
|
|
@@ -3602,7 +4262,7 @@ function PolicyPage({ kind }) {
|
|
|
3602
4262
|
] }) })
|
|
3603
4263
|
] });
|
|
3604
4264
|
}
|
|
3605
|
-
var StorefrontErrorBoundary = class extends
|
|
4265
|
+
var StorefrontErrorBoundary = class extends React6.Component {
|
|
3606
4266
|
constructor() {
|
|
3607
4267
|
super(...arguments);
|
|
3608
4268
|
this.state = { error: null };
|
|
@@ -3703,8 +4363,8 @@ function RouteSwitch({ path }) {
|
|
|
3703
4363
|
if (product?.slug) return /* @__PURE__ */ jsx(ProductDetailPage, { slug: product.slug });
|
|
3704
4364
|
const order = matchPath("/order/:id", path);
|
|
3705
4365
|
if (order?.id) return /* @__PURE__ */ jsx(OrderConfirmationPage, { orderId: order.id });
|
|
3706
|
-
if (matchPath("/checkout", path)) return /* @__PURE__ */ jsx(CheckoutPage, {});
|
|
3707
|
-
if (matchPath("/account", path)) return /* @__PURE__ */ jsx(MyPurchasesPage, {});
|
|
4366
|
+
if (matchPath("/checkout", path) && config.features.checkout) return /* @__PURE__ */ jsx(CheckoutPage, {});
|
|
4367
|
+
if (matchPath("/account", path) && config.features.accounts) return /* @__PURE__ */ jsx(MyPurchasesPage, {});
|
|
3708
4368
|
if (matchPath("/catalog", path)) return /* @__PURE__ */ jsx(CatalogPage, {});
|
|
3709
4369
|
if (matchPath("/privacy", path)) return /* @__PURE__ */ jsx(PolicyPage, { kind: "privacy" });
|
|
3710
4370
|
if (matchPath("/terms", path)) return /* @__PURE__ */ jsx(PolicyPage, { kind: "terms" });
|
|
@@ -3746,7 +4406,12 @@ function buildMockSeed(config) {
|
|
|
3746
4406
|
};
|
|
3747
4407
|
}
|
|
3748
4408
|
function initStorefrontRuntime(config) {
|
|
3749
|
-
|
|
4409
|
+
if (resolveConfig(config).features.accounts) {
|
|
4410
|
+
initCustomerAuth(resolveAuthAdapter(config.auth, {
|
|
4411
|
+
supabaseUrl: config.supabaseUrl,
|
|
4412
|
+
supabaseAnonKey: config.supabaseAnonKey
|
|
4413
|
+
}));
|
|
4414
|
+
}
|
|
3750
4415
|
if (config.provider) {
|
|
3751
4416
|
setShopProvider(config.provider);
|
|
3752
4417
|
} else {
|
|
@@ -3762,14 +4427,20 @@ function StorefrontShell() {
|
|
|
3762
4427
|
const config = useStorefrontConfig();
|
|
3763
4428
|
const path = useHashPath();
|
|
3764
4429
|
const focused = isFocusedRoute(config, path);
|
|
3765
|
-
|
|
4430
|
+
const components = getStorefrontComponents(config);
|
|
4431
|
+
const chromeProps = { config, commerceMode: config.commerceMode };
|
|
4432
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3766
4433
|
config.theme && /* @__PURE__ */ jsx(StorefrontThemeStyle, { theme: config.theme }),
|
|
3767
|
-
!focused && /* @__PURE__ */ jsx(StorefrontHeader, {}),
|
|
4434
|
+
!focused && (components.Header ? /* @__PURE__ */ jsx(components.Header, { ...chromeProps }) : /* @__PURE__ */ jsx(StorefrontHeader, {})),
|
|
3768
4435
|
/* @__PURE__ */ jsx(StorefrontErrorBoundary, { children: /* @__PURE__ */ jsx(RouteSwitch, { path }) }, path),
|
|
3769
|
-
!focused && /* @__PURE__ */ jsx(CartDrawer, {}),
|
|
3770
|
-
!focused && /* @__PURE__ */ jsx(StorefrontFooter, {}),
|
|
4436
|
+
!focused && config.features.cart && /* @__PURE__ */ jsx(CartDrawer, {}),
|
|
4437
|
+
!focused && (components.Footer ? /* @__PURE__ */ jsx(components.Footer, { ...chromeProps }) : /* @__PURE__ */ jsx(StorefrontFooter, {})),
|
|
3771
4438
|
/* @__PURE__ */ jsx(Toaster, {})
|
|
3772
4439
|
] });
|
|
4440
|
+
if (components.Shell) {
|
|
4441
|
+
return /* @__PURE__ */ jsx(components.Shell, { ...chromeProps, children: body });
|
|
4442
|
+
}
|
|
4443
|
+
return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-background text-foreground", children: body });
|
|
3773
4444
|
}
|
|
3774
4445
|
function createStorefrontApp(config) {
|
|
3775
4446
|
const resolved = resolveConfig(config);
|
|
@@ -3780,6 +4451,37 @@ function createStorefrontApp(config) {
|
|
|
3780
4451
|
StorefrontApp.displayName = "StorefrontApp";
|
|
3781
4452
|
return StorefrontApp;
|
|
3782
4453
|
}
|
|
4454
|
+
function createStorefront(options) {
|
|
4455
|
+
const routes = [...options.pages ?? [], ...options.routes ?? []];
|
|
4456
|
+
const config = {
|
|
4457
|
+
...options.config,
|
|
4458
|
+
provider: options.provider ?? options.config.provider,
|
|
4459
|
+
components: {
|
|
4460
|
+
...options.config.components,
|
|
4461
|
+
...options.components
|
|
4462
|
+
},
|
|
4463
|
+
routes: routes.length > 0 ? routes : options.config.routes,
|
|
4464
|
+
home: options.sections ? { ...options.config.home ?? { sections: [] }, sections: options.sections } : options.config.home
|
|
4465
|
+
};
|
|
4466
|
+
return createStorefrontApp(config);
|
|
4467
|
+
}
|
|
4468
|
+
|
|
4469
|
+
// src/define.ts
|
|
4470
|
+
function defineStorefrontConfig(config) {
|
|
4471
|
+
return config;
|
|
4472
|
+
}
|
|
4473
|
+
function defineStorefrontComponents(components) {
|
|
4474
|
+
return components;
|
|
4475
|
+
}
|
|
4476
|
+
function defineStorefrontRoutes(routes) {
|
|
4477
|
+
return routes;
|
|
4478
|
+
}
|
|
4479
|
+
function defineStorefrontSections(sections) {
|
|
4480
|
+
return sections;
|
|
4481
|
+
}
|
|
4482
|
+
function defineStorefrontApp(options) {
|
|
4483
|
+
return options;
|
|
4484
|
+
}
|
|
3783
4485
|
var runtimeConfigRegistry = /* @__PURE__ */ new Map();
|
|
3784
4486
|
function slug(name) {
|
|
3785
4487
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "app";
|
|
@@ -3838,7 +4540,7 @@ function manifestToStorefrontConfig(manifest, surfaceName) {
|
|
|
3838
4540
|
provider: manifest.backend?.provider,
|
|
3839
4541
|
url: manifest.backend?.url
|
|
3840
4542
|
},
|
|
3841
|
-
|
|
4543
|
+
components: runtimeConfig?.components,
|
|
3842
4544
|
routes: runtimeConfig?.routes,
|
|
3843
4545
|
provider: runtimeConfig?.provider,
|
|
3844
4546
|
auth: runtimeConfig?.auth,
|
|
@@ -3846,9 +4548,9 @@ function manifestToStorefrontConfig(manifest, surfaceName) {
|
|
|
3846
4548
|
};
|
|
3847
4549
|
}
|
|
3848
4550
|
function StorefrontScaffold({ manifest, surface }) {
|
|
3849
|
-
const config =
|
|
3850
|
-
const resolved =
|
|
3851
|
-
const inited =
|
|
4551
|
+
const config = React6.useMemo(() => manifestToStorefrontConfig(manifest, surface), [manifest, surface]);
|
|
4552
|
+
const resolved = React6.useMemo(() => resolveConfig(config), [config]);
|
|
4553
|
+
const inited = React6.useRef(false);
|
|
3852
4554
|
if (!inited.current) {
|
|
3853
4555
|
initStorefrontRuntime(config);
|
|
3854
4556
|
inited.current = true;
|
|
@@ -4081,6 +4783,6 @@ var storefrontTemplates = {
|
|
|
4081
4783
|
atelier: atelierTemplate
|
|
4082
4784
|
};
|
|
4083
4785
|
|
|
4084
|
-
export { BenefitsRow, CartDrawer, CatalogPage, CategoryShowcase, CheckoutPage, FiltersPanel, HeroSection, HomePage, Link, ManifestoBlock, MyPurchasesPage, NewsletterBand, OrderConfirmationPage, OrderTrackingTimeline, Price, ProductCard, ProductDetailPage, ProductGrid, ProductOptionSelector, ProductRail, PromoBanner, QuantityInput, Reveal, StorefrontFooter, StorefrontHeader, StorefrontScaffold, StorefrontShell, StorefrontThemeStyle, TID, Testimonials, atelierTemplate, bannerPlaceholder, createStorefrontApp, defineStorefront, establishCustomerSession, formatMoney, formatProductOptionSelection, getCustomerAuthAdapter, getProductOptionGroups, initStorefrontRuntime, mareTemplate, matchPath, navigateTo, normalizeProductOptionSelection, placeStorefrontOrder, prefersReducedMotion,
|
|
4786
|
+
export { BenefitsRow, CartDrawer, CatalogPage, CategoryShowcase, CheckoutPage, FiltersPanel, HeroSection, HomePage, Link, ManifestoBlock, MediaCarousel, MyPurchasesPage, NewsletterBand, OrderConfirmationPage, OrderTrackingTimeline, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGrid, ProductOptionSelector, ProductRail, ProductSlider, PromoBanner, QuantityInput, Reveal, SmoothImage, StorefrontFooter, StorefrontHeader, StorefrontScaffold, StorefrontSections, StorefrontShell, StorefrontThemeStyle, TID, Testimonials, atelierTemplate, bannerPlaceholder, createStorefront, createStorefrontApp, defineStorefront, defineStorefrontApp, defineStorefrontComponents, defineStorefrontConfig, defineStorefrontRoutes, defineStorefrontSections, establishCustomerSession, formatMoney, formatProductOptionSelection, getCustomerAuthAdapter, getProductOptionGroups, initStorefrontRuntime, mareTemplate, matchPath, navigateTo, normalizeProductOptionSelection, placeStorefrontOrder, prefersReducedMotion, productCardComponentContract, productOptionSelectionKey, registerStorefrontBlocks, resolveAuthAdapter, roundCents, sectionsToBlocks, selectCount, selectDiscountTotal, selectShipping, selectSubtotal, selectTotal, sertaoTemplate, signInByEmail, signOutCustomer, signUpCustomer, storefrontComponentContracts, storefrontTemplates, themeToCss, useCart, useCartStore, useCatalog, useCatalogStore, useCategories, useDiscountValidator, useEnquiry, useHashPath, useInView, useMyOrders, usePopOnChange, useProduct, useProducts, useScrolled, useSessionStore, useStorefront, useStorefrontActions, useStorefrontConfig, useStorefrontConfigOptional, voltTemplate };
|
|
4085
4787
|
//# sourceMappingURL=index.js.map
|
|
4086
4788
|
//# sourceMappingURL=index.js.map
|