@fayz-ai/storefront 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- 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 +1113 -277
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +21 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1006 -188
- 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/presets.d.ts +5 -0
- package/dist/presets.d.ts.map +1 -1
- package/dist/sections.d.ts +45 -2
- package/dist/sections.d.ts.map +1 -1
- package/dist/stores/cart.store.d.ts +5 -0
- package/dist/stores/cart.store.d.ts.map +1 -1
- package/dist/testids.d.ts +21 -0
- package/dist/testids.d.ts.map +1 -1
- package/dist/theme.d.ts +21 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/workflows/checkout.d.ts.map +1 -1
- package/package.json +10 -6
- 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 +74 -42
- 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 +80 -10
- package/src/define.ts +34 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useStorefront.ts +58 -0
- package/src/index.ts +62 -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/presets.ts +66 -0
- package/src/scaffold.tsx +1 -1
- package/src/sections.ts +49 -2
- package/src/stores/cart.store.ts +16 -1
- package/src/testids.ts +21 -0
- package/src/theme.ts +36 -2
- package/src/workflows/checkout.ts +5 -2
- 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,93 +204,7 @@ function matchPath(pattern, path) {
|
|
|
162
204
|
return params;
|
|
163
205
|
}
|
|
164
206
|
function Link({ to, children, ...rest }) {
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
var RADIUS_MAP = {
|
|
168
|
-
none: { button: "0px", card: "0px", input: "0px" },
|
|
169
|
-
soft: { button: "6px", card: "10px", input: "6px" },
|
|
170
|
-
round: { button: "10px", card: "16px", input: "10px" },
|
|
171
|
-
pill: { button: "999px", card: "20px", input: "999px" }
|
|
172
|
-
};
|
|
173
|
-
var COLOR_VAR_MAP = {
|
|
174
|
-
background: "--background",
|
|
175
|
-
foreground: "--foreground",
|
|
176
|
-
primary: "--primary",
|
|
177
|
-
primaryForeground: "--primary-foreground",
|
|
178
|
-
card: "--card",
|
|
179
|
-
cardForeground: "--card-foreground",
|
|
180
|
-
muted: "--muted",
|
|
181
|
-
mutedForeground: "--muted-foreground",
|
|
182
|
-
border: "--border",
|
|
183
|
-
headerBackground: "--sf-header-bg",
|
|
184
|
-
headerForeground: "--sf-header-fg",
|
|
185
|
-
announcementBackground: "--sf-announcement-bg",
|
|
186
|
-
announcementForeground: "--sf-announcement-fg"
|
|
187
|
-
};
|
|
188
|
-
function themeToCss(theme) {
|
|
189
|
-
const lines = [];
|
|
190
|
-
for (const [key, cssVar] of Object.entries(COLOR_VAR_MAP)) {
|
|
191
|
-
const value = theme.colors?.[key];
|
|
192
|
-
if (value) lines.push(`${cssVar}: ${value};`);
|
|
193
|
-
}
|
|
194
|
-
if (!theme.colors?.headerBackground && theme.colors?.background) {
|
|
195
|
-
lines.push(`--sf-header-bg: ${theme.colors.background};`);
|
|
196
|
-
}
|
|
197
|
-
if (!theme.colors?.headerForeground && theme.colors?.foreground) {
|
|
198
|
-
lines.push(`--sf-header-fg: ${theme.colors.foreground};`);
|
|
199
|
-
}
|
|
200
|
-
if (theme.font) {
|
|
201
|
-
const fallback = theme.font.fallback ?? "sans-serif";
|
|
202
|
-
lines.push(`--font-family: '${theme.font.body}', ${fallback};`);
|
|
203
|
-
lines.push(`--sf-font-heading: '${theme.font.heading}', ${fallback};`);
|
|
204
|
-
}
|
|
205
|
-
const radius = RADIUS_MAP[theme.radius ?? "soft"];
|
|
206
|
-
lines.push(`--button-radius: ${radius.button};`);
|
|
207
|
-
lines.push(`--card-radius: ${radius.card};`);
|
|
208
|
-
lines.push(`--input-radius: ${radius.input};`);
|
|
209
|
-
lines.push(`--sf-radius-button: ${radius.button};`);
|
|
210
|
-
lines.push(`--sf-radius-card: ${radius.card};`);
|
|
211
|
-
lines.push(`--sf-radius-input: ${radius.input};`);
|
|
212
|
-
let css = `:root {
|
|
213
|
-
${lines.join("\n ")}
|
|
214
|
-
}
|
|
215
|
-
`;
|
|
216
|
-
css += `body { font-family: var(--font-family); }
|
|
217
|
-
`;
|
|
218
|
-
css += `h1, h2, h3, h4, .sf-heading { font-family: var(--sf-font-heading, var(--font-family)); }
|
|
219
|
-
`;
|
|
220
|
-
if (theme.uppercaseButtons) {
|
|
221
|
-
css += `.sf-cta { text-transform: uppercase; letter-spacing: 0.08em; }
|
|
222
|
-
`;
|
|
223
|
-
}
|
|
224
|
-
if (theme.header?.uppercaseNav) {
|
|
225
|
-
css += `.sf-nav-link { text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.8rem; }
|
|
226
|
-
`;
|
|
227
|
-
}
|
|
228
|
-
return css;
|
|
229
|
-
}
|
|
230
|
-
function googleFontsHref(theme) {
|
|
231
|
-
if (!theme.font) return null;
|
|
232
|
-
const families = theme.font.googleFonts ?? [theme.font.heading, theme.font.body];
|
|
233
|
-
const unique = [...new Set(families)];
|
|
234
|
-
const params = unique.map((f) => `family=${encodeURIComponent(f).replace(/%20/g, "+")}:wght@300;400;500;600;700`).join("&");
|
|
235
|
-
return `https://fonts.googleapis.com/css2?${params}&display=swap`;
|
|
236
|
-
}
|
|
237
|
-
function StorefrontThemeStyle({ theme }) {
|
|
238
|
-
const fontsHref = googleFontsHref(theme);
|
|
239
|
-
useEffect(() => {
|
|
240
|
-
if (!fontsHref) return;
|
|
241
|
-
const id = "sf-theme-fonts";
|
|
242
|
-
let link = document.getElementById(id);
|
|
243
|
-
if (!link) {
|
|
244
|
-
link = document.createElement("link");
|
|
245
|
-
link.id = id;
|
|
246
|
-
link.rel = "stylesheet";
|
|
247
|
-
document.head.appendChild(link);
|
|
248
|
-
}
|
|
249
|
-
link.href = fontsHref;
|
|
250
|
-
}, [fontsHref]);
|
|
251
|
-
return React18.createElement("style", { "data-sf-theme": theme.name }, themeToCss(theme));
|
|
207
|
+
return React6.createElement("a", { href: `#${to}`, ...rest }, children);
|
|
252
208
|
}
|
|
253
209
|
|
|
254
210
|
// src/format.ts
|
|
@@ -302,7 +258,7 @@ function productOptionSelectionKey(selection) {
|
|
|
302
258
|
// src/stores/cart.store.ts
|
|
303
259
|
var useCartStore = create()(
|
|
304
260
|
persist(
|
|
305
|
-
(set) => ({
|
|
261
|
+
(set, get) => ({
|
|
306
262
|
lines: [],
|
|
307
263
|
discountCode: null,
|
|
308
264
|
discountPercent: 0,
|
|
@@ -354,7 +310,16 @@ var useCartStore = create()(
|
|
|
354
310
|
clearDiscount: () => set({ discountCode: null, discountPercent: 0 }),
|
|
355
311
|
clear: () => set({ lines: [], discountCode: null, discountPercent: 0 }),
|
|
356
312
|
openDrawer: () => set({ isOpen: true }),
|
|
357
|
-
closeDrawer: () => set({ isOpen: false, justAddedLineId: null })
|
|
313
|
+
closeDrawer: () => set({ isOpen: false, justAddedLineId: null }),
|
|
314
|
+
reconcile: async (resolve) => {
|
|
315
|
+
const lines = get().lines;
|
|
316
|
+
if (lines.length === 0) return;
|
|
317
|
+
const keep = await Promise.all(
|
|
318
|
+
lines.map((line) => resolve(line).catch(() => true))
|
|
319
|
+
);
|
|
320
|
+
const kept = lines.filter((_, i) => keep[i]);
|
|
321
|
+
if (kept.length !== lines.length) set({ lines: kept });
|
|
322
|
+
}
|
|
358
323
|
}),
|
|
359
324
|
{
|
|
360
325
|
name: "fayz.storefront.cart.v1",
|
|
@@ -376,6 +341,102 @@ var selectShipping = (s, cfg) => {
|
|
|
376
341
|
return cfg.shipping.flatRate;
|
|
377
342
|
};
|
|
378
343
|
var selectTotal = (s, cfg) => roundCents(selectSubtotal(s) - selectDiscountTotal(s) + selectShipping(s, cfg));
|
|
344
|
+
var RADIUS_MAP = {
|
|
345
|
+
none: { button: "0px", card: "0px", input: "0px" },
|
|
346
|
+
soft: { button: "6px", card: "10px", input: "6px" },
|
|
347
|
+
round: { button: "10px", card: "16px", input: "10px" },
|
|
348
|
+
pill: { button: "999px", card: "20px", input: "999px" }
|
|
349
|
+
};
|
|
350
|
+
var COLOR_VAR_MAP = {
|
|
351
|
+
background: "--background",
|
|
352
|
+
foreground: "--foreground",
|
|
353
|
+
primary: "--primary",
|
|
354
|
+
primaryForeground: "--primary-foreground",
|
|
355
|
+
card: "--card",
|
|
356
|
+
cardForeground: "--card-foreground",
|
|
357
|
+
muted: "--muted",
|
|
358
|
+
mutedForeground: "--muted-foreground",
|
|
359
|
+
border: "--border",
|
|
360
|
+
headerBackground: "--sf-header-bg",
|
|
361
|
+
headerForeground: "--sf-header-fg",
|
|
362
|
+
announcementBackground: "--sf-announcement-bg",
|
|
363
|
+
announcementForeground: "--sf-announcement-fg",
|
|
364
|
+
accent: "--sf-accent",
|
|
365
|
+
accentForeground: "--sf-accent-foreground"
|
|
366
|
+
};
|
|
367
|
+
function themeToCss(theme) {
|
|
368
|
+
const lines = [];
|
|
369
|
+
for (const [key, cssVar] of Object.entries(COLOR_VAR_MAP)) {
|
|
370
|
+
const value = theme.colors?.[key];
|
|
371
|
+
if (value) lines.push(`${cssVar}: ${value};`);
|
|
372
|
+
}
|
|
373
|
+
if (!theme.colors?.headerBackground && theme.colors?.background) {
|
|
374
|
+
lines.push(`--sf-header-bg: ${theme.colors.background};`);
|
|
375
|
+
}
|
|
376
|
+
if (!theme.colors?.headerForeground && theme.colors?.foreground) {
|
|
377
|
+
lines.push(`--sf-header-fg: ${theme.colors.foreground};`);
|
|
378
|
+
}
|
|
379
|
+
if (theme.font) {
|
|
380
|
+
const fallback = theme.font.fallback ?? "sans-serif";
|
|
381
|
+
lines.push(`--font-family: '${theme.font.body}', ${fallback};`);
|
|
382
|
+
lines.push(`--sf-font-heading: '${theme.font.heading}', ${fallback};`);
|
|
383
|
+
if (theme.font.display) lines.push(`--sf-font-display: '${theme.font.display}', ${fallback};`);
|
|
384
|
+
if (theme.font.serif) lines.push(`--sf-font-serif: '${theme.font.serif}', serif;`);
|
|
385
|
+
}
|
|
386
|
+
for (const [key, value] of Object.entries(theme.tokens ?? {})) {
|
|
387
|
+
const name = key.startsWith("--") ? key : `--${key}`;
|
|
388
|
+
lines.push(`${name}: ${value};`);
|
|
389
|
+
}
|
|
390
|
+
const radius = RADIUS_MAP[theme.radius ?? "soft"];
|
|
391
|
+
lines.push(`--button-radius: ${radius.button};`);
|
|
392
|
+
lines.push(`--card-radius: ${radius.card};`);
|
|
393
|
+
lines.push(`--input-radius: ${radius.input};`);
|
|
394
|
+
lines.push(`--sf-radius-button: ${radius.button};`);
|
|
395
|
+
lines.push(`--sf-radius-card: ${radius.card};`);
|
|
396
|
+
lines.push(`--sf-radius-input: ${radius.input};`);
|
|
397
|
+
let css = `:root {
|
|
398
|
+
${lines.join("\n ")}
|
|
399
|
+
}
|
|
400
|
+
`;
|
|
401
|
+
css += `body { font-family: var(--font-family); }
|
|
402
|
+
`;
|
|
403
|
+
css += `h1, h2, h3, h4, .sf-heading { font-family: var(--sf-font-heading, var(--font-family)); }
|
|
404
|
+
`;
|
|
405
|
+
if (theme.uppercaseButtons) {
|
|
406
|
+
css += `.sf-cta { text-transform: uppercase; letter-spacing: 0.08em; }
|
|
407
|
+
`;
|
|
408
|
+
}
|
|
409
|
+
if (theme.header?.uppercaseNav) {
|
|
410
|
+
css += `.sf-nav-link { text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.8rem; }
|
|
411
|
+
`;
|
|
412
|
+
}
|
|
413
|
+
return css;
|
|
414
|
+
}
|
|
415
|
+
function googleFontsHref(theme) {
|
|
416
|
+
if (!theme.font) return null;
|
|
417
|
+
const families = theme.font.googleFonts ?? [theme.font.heading, theme.font.body, theme.font.display, theme.font.serif].filter(
|
|
418
|
+
(f) => Boolean(f)
|
|
419
|
+
);
|
|
420
|
+
const unique = [...new Set(families)];
|
|
421
|
+
const params = unique.map((f) => `family=${encodeURIComponent(f).replace(/%20/g, "+")}:wght@300;400;500;600;700`).join("&");
|
|
422
|
+
return `https://fonts.googleapis.com/css2?${params}&display=swap`;
|
|
423
|
+
}
|
|
424
|
+
function StorefrontThemeStyle({ theme }) {
|
|
425
|
+
const fontsHref = googleFontsHref(theme);
|
|
426
|
+
useEffect(() => {
|
|
427
|
+
if (!fontsHref) return;
|
|
428
|
+
const id = "sf-theme-fonts";
|
|
429
|
+
let link = document.getElementById(id);
|
|
430
|
+
if (!link) {
|
|
431
|
+
link = document.createElement("link");
|
|
432
|
+
link.id = id;
|
|
433
|
+
link.rel = "stylesheet";
|
|
434
|
+
document.head.appendChild(link);
|
|
435
|
+
}
|
|
436
|
+
link.href = fontsHref;
|
|
437
|
+
}, [fontsHref]);
|
|
438
|
+
return React6.createElement("style", { "data-sf-theme": theme.name }, themeToCss(theme));
|
|
439
|
+
}
|
|
379
440
|
var initial = {
|
|
380
441
|
search: "",
|
|
381
442
|
categoryId: null,
|
|
@@ -484,6 +545,7 @@ function usePopOnChange(value) {
|
|
|
484
545
|
var TID = {
|
|
485
546
|
// header
|
|
486
547
|
headerSearch: "header-search",
|
|
548
|
+
headerSearchToggle: "header-search-toggle",
|
|
487
549
|
cartButton: "cart-button",
|
|
488
550
|
cartCount: "cart-count",
|
|
489
551
|
accountLink: "account-link",
|
|
@@ -496,6 +558,13 @@ var TID = {
|
|
|
496
558
|
hero: "home-hero",
|
|
497
559
|
heroPrev: "hero-prev",
|
|
498
560
|
heroNext: "hero-next",
|
|
561
|
+
mediaCarousel: "media-carousel",
|
|
562
|
+
mediaCarouselPrev: "media-carousel-prev",
|
|
563
|
+
mediaCarouselNext: "media-carousel-next",
|
|
564
|
+
productSlider: "product-slider",
|
|
565
|
+
productSliderViewport: "product-slider-viewport",
|
|
566
|
+
productSliderPrev: "product-slider-prev",
|
|
567
|
+
productSliderNext: "product-slider-next",
|
|
499
568
|
categoryShowcase: "category-showcase",
|
|
500
569
|
productRail: "product-rail",
|
|
501
570
|
benefits: "benefits-row",
|
|
@@ -530,6 +599,19 @@ var TID = {
|
|
|
530
599
|
productOptionValue: (id, value) => `product-option-${id.toLowerCase().replace(/\s+/g, "-")}-${value.toLowerCase().replace(/\s+/g, "-")}`,
|
|
531
600
|
pdpQtyInput: "pdp-qty-input",
|
|
532
601
|
pdpAddToCart: "pdp-add-to-cart",
|
|
602
|
+
pdpRoot: "pdp-root",
|
|
603
|
+
pdpGallery: "pdp-gallery",
|
|
604
|
+
pdpActions: "pdp-actions",
|
|
605
|
+
pdpEnquiryButton: "pdp-enquiry-button",
|
|
606
|
+
enquiryForm: "enquiry-form",
|
|
607
|
+
enquiryName: "enquiry-name",
|
|
608
|
+
enquiryEmail: "enquiry-email",
|
|
609
|
+
enquiryPhone: "enquiry-phone",
|
|
610
|
+
enquirySubject: "enquiry-subject",
|
|
611
|
+
enquiryMessage: "enquiry-message",
|
|
612
|
+
enquirySubmit: "enquiry-submit",
|
|
613
|
+
enquirySuccess: "enquiry-success",
|
|
614
|
+
enquiryError: "enquiry-error",
|
|
533
615
|
// cart
|
|
534
616
|
cartDrawer: "cart-drawer",
|
|
535
617
|
cartLine: "cart-line",
|
|
@@ -652,12 +734,26 @@ function UtilityBar() {
|
|
|
652
734
|
/* @__PURE__ */ jsx("span", { className: "ml-auto flex items-center gap-4", children: social?.map((s) => /* @__PURE__ */ jsx("a", { href: s.href, target: "_blank", rel: "noreferrer", className: "hover:underline", children: s.label }, s.href)) })
|
|
653
735
|
] }) });
|
|
654
736
|
}
|
|
655
|
-
function SearchInput({ className }) {
|
|
737
|
+
function SearchInput({ className, iconOnly = false }) {
|
|
656
738
|
const config = useStorefrontConfig();
|
|
657
739
|
const search = useCatalogStore((s) => s.search);
|
|
658
740
|
const setSearch = useCatalogStore((s) => s.setSearch);
|
|
659
741
|
const path = useHashPath();
|
|
660
|
-
|
|
742
|
+
const [open, setOpen] = useState(false);
|
|
743
|
+
if (iconOnly && !open) {
|
|
744
|
+
return /* @__PURE__ */ jsx(
|
|
745
|
+
"button",
|
|
746
|
+
{
|
|
747
|
+
type: "button",
|
|
748
|
+
"aria-label": "Buscar produtos",
|
|
749
|
+
"data-testid": TID.headerSearchToggle,
|
|
750
|
+
onClick: () => setOpen(true),
|
|
751
|
+
className: "rounded-full p-2.5 transition-opacity hover:opacity-70",
|
|
752
|
+
children: /* @__PURE__ */ jsx(Search, { className: "h-5 w-5" })
|
|
753
|
+
}
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
return /* @__PURE__ */ jsxs("div", { className: `relative ${iconOnly ? "w-full max-w-xs" : className ?? ""}`, children: [
|
|
661
757
|
/* @__PURE__ */ jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 opacity-60" }),
|
|
662
758
|
/* @__PURE__ */ jsx(
|
|
663
759
|
"input",
|
|
@@ -666,10 +762,17 @@ function SearchInput({ className }) {
|
|
|
666
762
|
type: "search",
|
|
667
763
|
placeholder: "Buscar produtos...",
|
|
668
764
|
value: search,
|
|
765
|
+
autoFocus: iconOnly,
|
|
669
766
|
onChange: (e) => {
|
|
670
767
|
setSearch(e.target.value);
|
|
671
768
|
if (path !== config.catalogPath) navigateTo(config.catalogPath);
|
|
672
769
|
},
|
|
770
|
+
onKeyDown: (e) => {
|
|
771
|
+
if (e.key === "Escape" && iconOnly) setOpen(false);
|
|
772
|
+
},
|
|
773
|
+
onBlur: () => {
|
|
774
|
+
if (iconOnly && !search) setOpen(false);
|
|
775
|
+
},
|
|
673
776
|
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",
|
|
674
777
|
style: {
|
|
675
778
|
borderRadius: "var(--sf-radius-input)",
|
|
@@ -757,7 +860,7 @@ function HeaderActions() {
|
|
|
757
860
|
}
|
|
758
861
|
)
|
|
759
862
|
] }),
|
|
760
|
-
/* @__PURE__ */ jsxs(
|
|
863
|
+
config.features.cart && /* @__PURE__ */ jsxs(
|
|
761
864
|
"button",
|
|
762
865
|
{
|
|
763
866
|
type: "button",
|
|
@@ -784,12 +887,13 @@ function NavLinks({ className }) {
|
|
|
784
887
|
const config = useStorefrontConfig();
|
|
785
888
|
const { categories } = useCategories();
|
|
786
889
|
const setCategoryId = useCatalogStore((s) => s.setCategoryId);
|
|
890
|
+
const showCategories = config.theme?.header?.showCategories !== false;
|
|
787
891
|
const goCategory = (categoryId) => {
|
|
788
892
|
useCatalogStore.getState().reset();
|
|
789
893
|
setCategoryId(categoryId);
|
|
790
894
|
navigateTo(config.catalogPath);
|
|
791
895
|
};
|
|
792
|
-
return /* @__PURE__ */ jsxs("nav", { className: `flex items-center gap-6 ${className ?? ""}`, children: [
|
|
896
|
+
return /* @__PURE__ */ jsxs("nav", { className: `flex min-w-max items-center gap-6 ${className ?? ""}`, children: [
|
|
793
897
|
config.nav.map((link) => /* @__PURE__ */ jsx(
|
|
794
898
|
Link,
|
|
795
899
|
{
|
|
@@ -800,7 +904,7 @@ function NavLinks({ className }) {
|
|
|
800
904
|
},
|
|
801
905
|
link.to
|
|
802
906
|
)),
|
|
803
|
-
categories.slice(0, 6).map((c) => /* @__PURE__ */ jsxs(
|
|
907
|
+
showCategories && categories.slice(0, 6).map((c) => /* @__PURE__ */ jsxs(
|
|
804
908
|
"button",
|
|
805
909
|
{
|
|
806
910
|
type: "button",
|
|
@@ -820,6 +924,8 @@ function StorefrontHeader() {
|
|
|
820
924
|
const config = useStorefrontConfig();
|
|
821
925
|
const variant = config.theme?.header?.variant ?? "classic";
|
|
822
926
|
const scrolled = useScrolled();
|
|
927
|
+
const showSearch = config.theme?.header?.showSearch !== false;
|
|
928
|
+
const iconSearch = config.theme?.header?.searchStyle === "icon";
|
|
823
929
|
const logo = /* @__PURE__ */ jsx(Link, { to: "/", className: "sf-heading shrink-0 text-xl font-bold tracking-tight", children: config.logo ?? config.name });
|
|
824
930
|
return /* @__PURE__ */ jsxs(
|
|
825
931
|
"header",
|
|
@@ -833,21 +939,21 @@ function StorefrontHeader() {
|
|
|
833
939
|
// Rio/Flex pattern: centered logo row, nav row below
|
|
834
940
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
835
941
|
/* @__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" }),
|
|
942
|
+
showSearch ? /* @__PURE__ */ jsx(SearchInput, { iconOnly: iconSearch, className: "hidden w-full max-w-xs sm:block" }) : /* @__PURE__ */ jsx("div", {}),
|
|
837
943
|
/* @__PURE__ */ jsx("div", { className: "text-center", children: logo }),
|
|
838
944
|
/* @__PURE__ */ jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsx(HeaderActions, {}) })
|
|
839
945
|
] }),
|
|
840
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
946
|
+
/* @__PURE__ */ jsx("div", { className: "flex overflow-x-auto border-t py-2.5 sm:justify-center", style: headerDivider, children: /* @__PURE__ */ jsx(NavLinks, {}) })
|
|
841
947
|
] })
|
|
842
948
|
) : variant === "search" ? (
|
|
843
949
|
// Brasília pattern: prominent search left, logo center, actions right; nav row below
|
|
844
950
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
845
951
|
/* @__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" }),
|
|
952
|
+
showSearch ? /* @__PURE__ */ jsx(SearchInput, { iconOnly: iconSearch, className: "hidden w-full max-w-sm sm:block" }) : /* @__PURE__ */ jsx("div", {}),
|
|
847
953
|
/* @__PURE__ */ jsx("div", { className: "text-center", children: logo }),
|
|
848
954
|
/* @__PURE__ */ jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsx(HeaderActions, {}) })
|
|
849
955
|
] }),
|
|
850
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
956
|
+
/* @__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
957
|
] })
|
|
852
958
|
) : variant === "minimal" ? (
|
|
853
959
|
// Uyuni pattern: nav left, centered logo, actions right — single compact row
|
|
@@ -855,17 +961,19 @@ function StorefrontHeader() {
|
|
|
855
961
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-5", children: /* @__PURE__ */ jsx(NavLinks, { className: "hidden sm:flex" }) }),
|
|
856
962
|
/* @__PURE__ */ jsx("div", { className: "text-center", children: logo }),
|
|
857
963
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3", children: [
|
|
858
|
-
/* @__PURE__ */ jsx(SearchInput, { className: "hidden w-44 lg:block" }),
|
|
964
|
+
showSearch && /* @__PURE__ */ jsx(SearchInput, { iconOnly: iconSearch, className: "hidden w-44 lg:block" }),
|
|
859
965
|
/* @__PURE__ */ jsx(HeaderActions, {})
|
|
860
966
|
] })
|
|
861
967
|
] })
|
|
862
968
|
) : (
|
|
863
|
-
// classic: logo left,
|
|
969
|
+
// classic: logo left, nav, then a right-aligned search + actions cluster
|
|
864
970
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto flex h-16 max-w-7xl items-center gap-6 px-4 sm:px-6", children: [
|
|
865
971
|
logo,
|
|
866
972
|
/* @__PURE__ */ jsx(NavLinks, { className: "hidden md:flex" }),
|
|
867
|
-
/* @__PURE__ */
|
|
868
|
-
|
|
973
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2 sm:gap-3", children: [
|
|
974
|
+
showSearch && /* @__PURE__ */ jsx(SearchInput, { iconOnly: iconSearch, className: iconSearch ? "" : "hidden w-full max-w-sm sm:block" }),
|
|
975
|
+
/* @__PURE__ */ jsx(HeaderActions, {})
|
|
976
|
+
] })
|
|
869
977
|
] })
|
|
870
978
|
)
|
|
871
979
|
]
|
|
@@ -877,6 +985,17 @@ function StorefrontFooter() {
|
|
|
877
985
|
const { categories } = useCategories();
|
|
878
986
|
const visibleCategories = categories.slice(0, 8);
|
|
879
987
|
const hiddenCategoryCount = Math.max(categories.length - visibleCategories.length, 0);
|
|
988
|
+
const labels = {
|
|
989
|
+
categories: "Departamentos",
|
|
990
|
+
navigation: "Navega\xE7\xE3o",
|
|
991
|
+
contact: "Contato",
|
|
992
|
+
viewAll: "Ver todos",
|
|
993
|
+
purchases: "Minhas compras",
|
|
994
|
+
privacy: "Privacidade",
|
|
995
|
+
terms: "Termos",
|
|
996
|
+
returns: "Trocas e devolu\xE7\xF5es",
|
|
997
|
+
...config.footer?.labels
|
|
998
|
+
};
|
|
880
999
|
const goCategory = (categoryId) => {
|
|
881
1000
|
useCatalogStore.getState().reset();
|
|
882
1001
|
useCatalogStore.getState().setCategoryId(categoryId);
|
|
@@ -889,7 +1008,7 @@ function StorefrontFooter() {
|
|
|
889
1008
|
config.footer?.about && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm leading-relaxed text-muted-foreground", children: config.footer.about })
|
|
890
1009
|
] }),
|
|
891
1010
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
892
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children:
|
|
1011
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.categories }),
|
|
893
1012
|
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm", children: [
|
|
894
1013
|
visibleCategories.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
895
1014
|
"button",
|
|
@@ -901,21 +1020,22 @@ function StorefrontFooter() {
|
|
|
901
1020
|
}
|
|
902
1021
|
) }, c.id)),
|
|
903
1022
|
hiddenCategoryCount > 0 && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, { to: config.catalogPath, className: "font-medium underline-offset-2 transition-opacity hover:underline", children: [
|
|
904
|
-
|
|
1023
|
+
labels.viewAll,
|
|
1024
|
+
" (",
|
|
905
1025
|
categories.length,
|
|
906
1026
|
")"
|
|
907
1027
|
] }) })
|
|
908
1028
|
] })
|
|
909
1029
|
] }),
|
|
910
1030
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
911
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children:
|
|
1031
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.navigation }),
|
|
912
1032
|
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm", children: [
|
|
913
1033
|
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:
|
|
1034
|
+
config.features.accounts && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, { to: "/account", className: "transition-opacity hover:opacity-70", children: labels.purchases }) })
|
|
915
1035
|
] })
|
|
916
1036
|
] }),
|
|
917
1037
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
918
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children:
|
|
1038
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.contact }),
|
|
919
1039
|
/* @__PURE__ */ jsxs("ul", { className: "space-y-2 text-sm text-muted-foreground", children: [
|
|
920
1040
|
config.footer?.contact?.phone && /* @__PURE__ */ jsx("li", { children: config.footer.contact.phone }),
|
|
921
1041
|
config.footer?.contact?.email && /* @__PURE__ */ jsx("li", { children: config.footer.contact.email }),
|
|
@@ -926,17 +1046,15 @@ function StorefrontFooter() {
|
|
|
926
1046
|
] }),
|
|
927
1047
|
/* @__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
1048
|
/* @__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:
|
|
1049
|
+
/* @__PURE__ */ jsx(Link, { to: "/privacy", className: "transition-colors hover:text-foreground", children: labels.privacy }),
|
|
1050
|
+
/* @__PURE__ */ jsx(Link, { to: "/terms", className: "transition-colors hover:text-foreground", children: labels.terms }),
|
|
1051
|
+
/* @__PURE__ */ jsx(Link, { to: "/refunds", className: "transition-colors hover:text-foreground", children: labels.returns })
|
|
932
1052
|
] }),
|
|
933
|
-
/* @__PURE__ */
|
|
1053
|
+
/* @__PURE__ */ jsx("p", { className: "text-center", children: config.footer?.credit ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
934
1054
|
"\xA9 ",
|
|
935
1055
|
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
|
-
] })
|
|
1056
|
+
" \u2014 powered by Fayz"
|
|
1057
|
+
] }) })
|
|
940
1058
|
] }) })
|
|
941
1059
|
] });
|
|
942
1060
|
}
|
|
@@ -1029,6 +1147,53 @@ function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTe
|
|
|
1029
1147
|
)
|
|
1030
1148
|
] });
|
|
1031
1149
|
}
|
|
1150
|
+
function SmoothImage({
|
|
1151
|
+
imageLoading,
|
|
1152
|
+
loading = "lazy",
|
|
1153
|
+
decoding = "async",
|
|
1154
|
+
onLoad,
|
|
1155
|
+
style,
|
|
1156
|
+
...props
|
|
1157
|
+
}) {
|
|
1158
|
+
const config = useStorefrontConfigOptional();
|
|
1159
|
+
const resolved = {
|
|
1160
|
+
...config?.imageLoading,
|
|
1161
|
+
...imageLoading
|
|
1162
|
+
};
|
|
1163
|
+
const mode = resolved.mode ?? "fade";
|
|
1164
|
+
const durationMs = resolved.durationMs ?? 420;
|
|
1165
|
+
const easing = resolved.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)";
|
|
1166
|
+
const blur = resolved.blur ?? true;
|
|
1167
|
+
const ref = React6.useRef(null);
|
|
1168
|
+
const [loaded, setLoaded] = React6.useState(mode === "none");
|
|
1169
|
+
React6.useEffect(() => {
|
|
1170
|
+
if (mode === "none") {
|
|
1171
|
+
setLoaded(true);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
if (ref.current?.complete && ref.current.naturalWidth > 0) setLoaded(true);
|
|
1175
|
+
}, [mode, props.src]);
|
|
1176
|
+
const revealStyle = mode === "none" ? {} : {
|
|
1177
|
+
opacity: loaded ? 1 : 0,
|
|
1178
|
+
filter: blur && !loaded ? "blur(10px)" : "blur(0px)",
|
|
1179
|
+
transition: `opacity ${durationMs}ms ${easing}, filter ${durationMs}ms ${easing}, transform ${durationMs}ms ${easing}`
|
|
1180
|
+
};
|
|
1181
|
+
return /* @__PURE__ */ jsx(
|
|
1182
|
+
"img",
|
|
1183
|
+
{
|
|
1184
|
+
...props,
|
|
1185
|
+
ref,
|
|
1186
|
+
loading,
|
|
1187
|
+
decoding,
|
|
1188
|
+
"data-sf-image-loaded": loaded ? "true" : "false",
|
|
1189
|
+
style: { ...style, ...revealStyle },
|
|
1190
|
+
onLoad: (event) => {
|
|
1191
|
+
setLoaded(true);
|
|
1192
|
+
onLoad?.(event);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1032
1197
|
function CartDrawer() {
|
|
1033
1198
|
const config = useStorefrontConfig();
|
|
1034
1199
|
const cart = useCartStore();
|
|
@@ -1128,7 +1293,7 @@ function CartDrawer() {
|
|
|
1128
1293
|
"data-line-id": line.lineId ?? line.productId,
|
|
1129
1294
|
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
1295
|
children: [
|
|
1131
|
-
line.imageUrl && /* @__PURE__ */ jsx(
|
|
1296
|
+
line.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: line.imageUrl, alt: line.name, className: "h-20 w-20 shrink-0 rounded-lg border object-cover" }),
|
|
1132
1297
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
|
|
1133
1298
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
1134
1299
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium leading-snug", children: line.name }),
|
|
@@ -1298,36 +1463,91 @@ function Price({ value, compareAt, testId, compareTestId, className }) {
|
|
|
1298
1463
|
] });
|
|
1299
1464
|
}
|
|
1300
1465
|
|
|
1301
|
-
// src/
|
|
1302
|
-
var
|
|
1466
|
+
// src/component-selectors.ts
|
|
1467
|
+
var productCardComponentContract = {
|
|
1303
1468
|
root: { "data-testid": TID.productCard },
|
|
1304
1469
|
name: { "data-testid": TID.productCardName },
|
|
1305
1470
|
priceTestId: TID.productCardPrice,
|
|
1306
1471
|
addButton: { "data-testid": TID.productCardAdd }
|
|
1307
1472
|
};
|
|
1308
|
-
var
|
|
1309
|
-
productCard:
|
|
1473
|
+
var storefrontComponentContracts = {
|
|
1474
|
+
productCard: productCardComponentContract,
|
|
1475
|
+
productDetail: {
|
|
1476
|
+
root: { "data-testid": TID.pdpRoot },
|
|
1477
|
+
gallery: { "data-testid": TID.pdpGallery },
|
|
1478
|
+
name: { "data-testid": TID.pdpName },
|
|
1479
|
+
description: { "data-testid": TID.pdpDescription },
|
|
1480
|
+
actions: { "data-testid": TID.pdpActions },
|
|
1481
|
+
addButton: { "data-testid": TID.pdpAddToCart },
|
|
1482
|
+
enquiryButton: { "data-testid": TID.pdpEnquiryButton },
|
|
1483
|
+
enquiryForm: { "data-testid": TID.enquiryForm }
|
|
1484
|
+
}
|
|
1310
1485
|
};
|
|
1311
|
-
function
|
|
1486
|
+
function useStorefront() {
|
|
1487
|
+
return useStorefrontConfig();
|
|
1488
|
+
}
|
|
1489
|
+
function useCatalog() {
|
|
1312
1490
|
const config = useStorefrontConfig();
|
|
1313
|
-
const
|
|
1491
|
+
const catalog = useCatalogStore();
|
|
1492
|
+
return {
|
|
1493
|
+
...catalog,
|
|
1494
|
+
catalogPath: config.catalogPath,
|
|
1495
|
+
config
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
function useCart() {
|
|
1499
|
+
return useCartStore();
|
|
1500
|
+
}
|
|
1501
|
+
function useEnquiry() {
|
|
1502
|
+
const config = useStorefrontConfig();
|
|
1503
|
+
const createProductEnquiry = async (input) => {
|
|
1504
|
+
const provider = getShopProvider();
|
|
1505
|
+
if (!provider.createProductEnquiry) {
|
|
1506
|
+
throw new Error("The active shop provider does not support product enquiries.");
|
|
1507
|
+
}
|
|
1508
|
+
return provider.createProductEnquiry(input);
|
|
1509
|
+
};
|
|
1510
|
+
return {
|
|
1511
|
+
enquiry: config.enquiry,
|
|
1512
|
+
createProductEnquiry
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
function useStorefrontActions() {
|
|
1516
|
+
const addItem = useCartStore((state) => state.addItem);
|
|
1517
|
+
const openDrawer = useCartStore((state) => state.openDrawer);
|
|
1518
|
+
const closeDrawer = useCartStore((state) => state.closeDrawer);
|
|
1519
|
+
const { createProductEnquiry } = useEnquiry();
|
|
1520
|
+
return {
|
|
1521
|
+
navigateTo,
|
|
1522
|
+
addToCart: (product, quantity, options) => addItem(product, quantity, options),
|
|
1523
|
+
openCart: openDrawer,
|
|
1524
|
+
closeCart: closeDrawer,
|
|
1525
|
+
createProductEnquiry
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
function ProductCard({ product, config: providedConfig, actions: providedActions }) {
|
|
1529
|
+
const runtimeConfig = useStorefrontConfig();
|
|
1530
|
+
const runtimeActions = useStorefrontActions();
|
|
1531
|
+
const config = providedConfig ?? runtimeConfig;
|
|
1532
|
+
const actions = providedActions ?? runtimeActions;
|
|
1314
1533
|
const hasOptions = getProductOptionGroups(product).length > 0;
|
|
1315
1534
|
const soldOut = product.inventoryCount <= 0;
|
|
1316
1535
|
const onSale = product.compareAtPrice != null && product.compareAtPrice > product.price;
|
|
1317
1536
|
const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
|
|
1318
1537
|
const cardStyle = config.theme?.productCard?.style ?? "card";
|
|
1319
1538
|
const aspect = config.theme?.productCard?.imageAspect === "portrait" ? "aspect-[3/4]" : "aspect-square";
|
|
1539
|
+
const purchaseMode = config.commerceMode === "checkout";
|
|
1320
1540
|
return /* @__PURE__ */ jsxs(
|
|
1321
1541
|
"div",
|
|
1322
1542
|
{
|
|
1323
|
-
...
|
|
1543
|
+
...productCardComponentContract.root,
|
|
1324
1544
|
"data-slug": product.slug,
|
|
1325
1545
|
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
1546
|
style: { borderRadius: "var(--sf-radius-card)" },
|
|
1327
1547
|
children: [
|
|
1328
1548
|
/* @__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
1549
|
image && /* @__PURE__ */ jsx(
|
|
1330
|
-
|
|
1550
|
+
SmoothImage,
|
|
1331
1551
|
{
|
|
1332
1552
|
src: image.url,
|
|
1333
1553
|
alt: image.altText ?? product.name,
|
|
@@ -1360,33 +1580,37 @@ function ProductCard({ product }) {
|
|
|
1360
1580
|
Link,
|
|
1361
1581
|
{
|
|
1362
1582
|
to: `/product/${product.slug}`,
|
|
1363
|
-
...
|
|
1583
|
+
...productCardComponentContract.name,
|
|
1364
1584
|
className: "font-medium leading-snug hover:underline",
|
|
1365
1585
|
children: product.name
|
|
1366
1586
|
}
|
|
1367
1587
|
),
|
|
1368
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-auto flex items-center justify-between pt-2", children: [
|
|
1369
|
-
/* @__PURE__ */ jsx(
|
|
1588
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-auto flex items-center justify-between gap-3 pt-2", children: [
|
|
1589
|
+
purchaseMode ? /* @__PURE__ */ jsx(
|
|
1370
1590
|
Price,
|
|
1371
1591
|
{
|
|
1372
1592
|
value: product.price,
|
|
1373
1593
|
compareAt: product.compareAtPrice,
|
|
1374
|
-
testId:
|
|
1594
|
+
testId: productCardComponentContract.priceTestId
|
|
1375
1595
|
}
|
|
1376
|
-
),
|
|
1596
|
+
) : /* @__PURE__ */ jsx("span", { "data-testid": productCardComponentContract.priceTestId, className: "text-sm text-muted-foreground", children: config.commerceMode === "enquiry" ? config.enquiry.label : "Ver detalhes" }),
|
|
1377
1597
|
/* @__PURE__ */ jsx(
|
|
1378
1598
|
"button",
|
|
1379
1599
|
{
|
|
1380
1600
|
type: "button",
|
|
1381
|
-
...
|
|
1382
|
-
disabled: soldOut,
|
|
1383
|
-
"aria-label": hasOptions ? `Escolher op\xE7\xF5es de ${product.name}` : `Adicionar ${product.name} ao carrinho`,
|
|
1601
|
+
...productCardComponentContract.addButton,
|
|
1602
|
+
disabled: purchaseMode && soldOut,
|
|
1603
|
+
"aria-label": purchaseMode ? hasOptions ? `Escolher op\xE7\xF5es de ${product.name}` : `Adicionar ${product.name} ao carrinho` : `${config.enquiry.label} ${product.name}`,
|
|
1384
1604
|
onClick: () => {
|
|
1605
|
+
if (!purchaseMode) {
|
|
1606
|
+
navigateTo(`/product/${product.slug}`);
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1385
1609
|
if (hasOptions) {
|
|
1386
1610
|
navigateTo(`/product/${product.slug}`);
|
|
1387
1611
|
return;
|
|
1388
1612
|
}
|
|
1389
|
-
|
|
1613
|
+
actions.addToCart(product);
|
|
1390
1614
|
},
|
|
1391
1615
|
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
1616
|
children: /* @__PURE__ */ jsx(ShoppingBag, { className: "h-4 w-4" })
|
|
@@ -1400,14 +1624,25 @@ function ProductCard({ product }) {
|
|
|
1400
1624
|
}
|
|
1401
1625
|
function ProductGrid({ products, loading }) {
|
|
1402
1626
|
const config = useStorefrontConfig();
|
|
1403
|
-
const
|
|
1627
|
+
const actions = useStorefrontActions();
|
|
1628
|
+
const components = getStorefrontComponents(config);
|
|
1629
|
+
const ProductCardComponent = components.ProductCard ?? ProductCard;
|
|
1404
1630
|
if (loading) {
|
|
1405
1631
|
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
1632
|
}
|
|
1407
1633
|
if (products.length === 0) {
|
|
1408
1634
|
return /* @__PURE__ */ jsx("div", { "data-testid": TID.productGrid, className: "py-20 text-center text-muted-foreground", children: "Nenhum produto encontrado." });
|
|
1409
1635
|
}
|
|
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(
|
|
1636
|
+
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(
|
|
1637
|
+
ProductCardComponent,
|
|
1638
|
+
{
|
|
1639
|
+
product: p,
|
|
1640
|
+
config,
|
|
1641
|
+
commerceMode: config.commerceMode,
|
|
1642
|
+
actions
|
|
1643
|
+
},
|
|
1644
|
+
p.id
|
|
1645
|
+
)) });
|
|
1411
1646
|
}
|
|
1412
1647
|
function FiltersPanel() {
|
|
1413
1648
|
const { categories } = useCategories();
|
|
@@ -1645,8 +1880,15 @@ function HeroSection({
|
|
|
1645
1880
|
height = "tall"
|
|
1646
1881
|
}) {
|
|
1647
1882
|
const [index, setIndex] = useState(0);
|
|
1883
|
+
const [loadedSlides, setLoadedSlides] = useState({});
|
|
1884
|
+
const config = useStorefrontConfig();
|
|
1648
1885
|
const interacted = useRef(false);
|
|
1649
1886
|
const hClass = height === "tall" ? "h-[72vh] min-h-[440px]" : "h-[48vh] min-h-[340px]";
|
|
1887
|
+
const revealImages = config.imageLoading.mode !== "none";
|
|
1888
|
+
const revealStyle = (loaded) => revealImages ? {
|
|
1889
|
+
filter: config.imageLoading.blur && !loaded ? "blur(10px)" : "blur(0px)",
|
|
1890
|
+
transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`
|
|
1891
|
+
} : void 0;
|
|
1650
1892
|
useEffect(() => {
|
|
1651
1893
|
if (variant !== "slider" || slides.length < 2 || prefersReducedMotion()) return;
|
|
1652
1894
|
const timer = setInterval(() => {
|
|
@@ -1667,7 +1909,7 @@ function HeroSection({
|
|
|
1667
1909
|
className: "group relative block h-[42vh] min-h-[320px] overflow-hidden",
|
|
1668
1910
|
children: [
|
|
1669
1911
|
/* @__PURE__ */ jsx(
|
|
1670
|
-
|
|
1912
|
+
SmoothImage,
|
|
1671
1913
|
{
|
|
1672
1914
|
src: slideImage(slide, 900, 700),
|
|
1673
1915
|
alt: slide.title,
|
|
@@ -1699,7 +1941,11 @@ function HeroSection({
|
|
|
1699
1941
|
src: slideImage(slide),
|
|
1700
1942
|
alt: slide.title,
|
|
1701
1943
|
"aria-hidden": i !== index,
|
|
1702
|
-
|
|
1944
|
+
loading: i === 0 ? "eager" : "lazy",
|
|
1945
|
+
decoding: "async",
|
|
1946
|
+
onLoad: () => setLoadedSlides((current) => ({ ...current, [i]: true })),
|
|
1947
|
+
style: revealStyle(Boolean(loadedSlides[i])),
|
|
1948
|
+
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
1949
|
},
|
|
1704
1950
|
i
|
|
1705
1951
|
)),
|
|
@@ -1761,7 +2007,7 @@ function CategoryShowcase({ style, title }) {
|
|
|
1761
2007
|
title && /* @__PURE__ */ jsx(Reveal, { children: /* @__PURE__ */ jsx("h2", { className: "sf-heading mb-8 text-center text-3xl font-bold", children: title }) }),
|
|
1762
2008
|
/* @__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
2009
|
/* @__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
|
-
|
|
2010
|
+
SmoothImage,
|
|
1765
2011
|
{
|
|
1766
2012
|
src: c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 200, 200),
|
|
1767
2013
|
alt: c.name,
|
|
@@ -1776,7 +2022,7 @@ function CategoryShowcase({ style, title }) {
|
|
|
1776
2022
|
title && /* @__PURE__ */ jsx(Reveal, { children: /* @__PURE__ */ jsx("h2", { className: "sf-heading mb-8 text-center text-3xl font-bold", children: title }) }),
|
|
1777
2023
|
/* @__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
2024
|
/* @__PURE__ */ jsx(
|
|
1779
|
-
|
|
2025
|
+
SmoothImage,
|
|
1780
2026
|
{
|
|
1781
2027
|
src: c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 480, 600),
|
|
1782
2028
|
alt: c.name,
|
|
@@ -1798,7 +2044,9 @@ function ProductRail({
|
|
|
1798
2044
|
limit = 4
|
|
1799
2045
|
}) {
|
|
1800
2046
|
const config = useStorefrontConfig();
|
|
1801
|
-
const
|
|
2047
|
+
const actions = useStorefrontActions();
|
|
2048
|
+
const components = getStorefrontComponents(config);
|
|
2049
|
+
const ProductCardComponent = components.ProductCard ?? ProductCard;
|
|
1802
2050
|
const { products, loading } = useProducts(
|
|
1803
2051
|
filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
|
|
1804
2052
|
);
|
|
@@ -1814,7 +2062,15 @@ function ProductRail({
|
|
|
1814
2062
|
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-3xl font-bold tracking-tight", children: title }),
|
|
1815
2063
|
subtitle && /* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: subtitle })
|
|
1816
2064
|
] }),
|
|
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(
|
|
2065
|
+
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(
|
|
2066
|
+
ProductCardComponent,
|
|
2067
|
+
{
|
|
2068
|
+
product: p,
|
|
2069
|
+
config,
|
|
2070
|
+
commerceMode: config.commerceMode,
|
|
2071
|
+
actions
|
|
2072
|
+
}
|
|
2073
|
+
) }, p.id)) }),
|
|
1818
2074
|
/* @__PURE__ */ jsx(Reveal, { className: "mt-10 text-center", delay: 200, children: /* @__PURE__ */ jsx(
|
|
1819
2075
|
Link,
|
|
1820
2076
|
{
|
|
@@ -1826,6 +2082,201 @@ function ProductRail({
|
|
|
1826
2082
|
) })
|
|
1827
2083
|
] });
|
|
1828
2084
|
}
|
|
2085
|
+
var heightClass = {
|
|
2086
|
+
medium: "h-[58vh] min-h-[420px]",
|
|
2087
|
+
tall: "h-[74vh] min-h-[520px]",
|
|
2088
|
+
screen: "min-h-[calc(100vh-64px)]"
|
|
2089
|
+
};
|
|
2090
|
+
var alignClass = {
|
|
2091
|
+
start: "items-start text-left",
|
|
2092
|
+
center: "items-center text-center",
|
|
2093
|
+
end: "items-end text-right"
|
|
2094
|
+
};
|
|
2095
|
+
function MediaCarousel({
|
|
2096
|
+
items,
|
|
2097
|
+
height = "tall",
|
|
2098
|
+
autoplayMs = 7e3,
|
|
2099
|
+
overlay = "dark",
|
|
2100
|
+
className = ""
|
|
2101
|
+
}) {
|
|
2102
|
+
const [index, setIndex] = useState(0);
|
|
2103
|
+
const [loadedItems, setLoadedItems] = useState({});
|
|
2104
|
+
const config = useStorefrontConfig();
|
|
2105
|
+
const interacted = useRef(false);
|
|
2106
|
+
const revealImages = config.imageLoading.mode !== "none";
|
|
2107
|
+
const revealStyle = (loaded) => revealImages ? {
|
|
2108
|
+
filter: config.imageLoading.blur && !loaded ? "blur(10px)" : "blur(0px)",
|
|
2109
|
+
transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`
|
|
2110
|
+
} : void 0;
|
|
2111
|
+
useEffect(() => {
|
|
2112
|
+
if (items.length < 2 || autoplayMs <= 0 || prefersReducedMotion()) return;
|
|
2113
|
+
const timer = setInterval(() => {
|
|
2114
|
+
if (!interacted.current) setIndex((i) => (i + 1) % items.length);
|
|
2115
|
+
}, autoplayMs);
|
|
2116
|
+
return () => clearInterval(timer);
|
|
2117
|
+
}, [autoplayMs, items.length]);
|
|
2118
|
+
if (items.length === 0) return null;
|
|
2119
|
+
const active = items[Math.min(index, items.length - 1)];
|
|
2120
|
+
const goTo = (next) => {
|
|
2121
|
+
interacted.current = true;
|
|
2122
|
+
setIndex((next + items.length) % items.length);
|
|
2123
|
+
};
|
|
2124
|
+
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" : "";
|
|
2125
|
+
return /* @__PURE__ */ jsxs("section", { "data-testid": TID.mediaCarousel, className: `relative isolate overflow-hidden ${heightClass[height]} ${className}`, children: [
|
|
2126
|
+
items.map((item, i) => /* @__PURE__ */ jsx(
|
|
2127
|
+
"img",
|
|
2128
|
+
{
|
|
2129
|
+
src: item.image,
|
|
2130
|
+
alt: item.imageAlt ?? item.title,
|
|
2131
|
+
"aria-hidden": i !== index,
|
|
2132
|
+
loading: i === 0 ? "eager" : "lazy",
|
|
2133
|
+
decoding: "async",
|
|
2134
|
+
onLoad: () => setLoadedItems((current) => ({ ...current, [i]: true })),
|
|
2135
|
+
style: revealStyle(Boolean(loadedItems[i])),
|
|
2136
|
+
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"}`
|
|
2137
|
+
},
|
|
2138
|
+
`${item.title}-${i}`
|
|
2139
|
+
)),
|
|
2140
|
+
/* @__PURE__ */ jsx("div", { className: `absolute inset-0 -z-10 ${overlayClass}` }),
|
|
2141
|
+
/* @__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: [
|
|
2142
|
+
active.eyebrow && /* @__PURE__ */ jsx("p", { className: "mb-4 text-xs font-semibold uppercase tracking-[0.24em] text-white/80", children: active.eyebrow }),
|
|
2143
|
+
/* @__PURE__ */ jsx("h1", { className: "sf-heading text-5xl font-normal leading-[0.95] sm:text-7xl lg:text-8xl", children: active.title }),
|
|
2144
|
+
active.subtitle && /* @__PURE__ */ jsx("p", { className: "mt-5 max-w-xl text-base leading-8 text-white/88 sm:text-lg", children: active.subtitle }),
|
|
2145
|
+
active.cta && /* @__PURE__ */ jsx(
|
|
2146
|
+
Link,
|
|
2147
|
+
{
|
|
2148
|
+
to: active.href ?? "/catalog",
|
|
2149
|
+
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",
|
|
2150
|
+
children: active.cta
|
|
2151
|
+
}
|
|
2152
|
+
)
|
|
2153
|
+
] }) }),
|
|
2154
|
+
items.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2155
|
+
/* @__PURE__ */ jsx(
|
|
2156
|
+
"button",
|
|
2157
|
+
{
|
|
2158
|
+
type: "button",
|
|
2159
|
+
"aria-label": "Previous slide",
|
|
2160
|
+
"data-testid": TID.mediaCarouselPrev,
|
|
2161
|
+
onClick: () => goTo(index - 1),
|
|
2162
|
+
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",
|
|
2163
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
|
|
2164
|
+
}
|
|
2165
|
+
),
|
|
2166
|
+
/* @__PURE__ */ jsx(
|
|
2167
|
+
"button",
|
|
2168
|
+
{
|
|
2169
|
+
type: "button",
|
|
2170
|
+
"aria-label": "Next slide",
|
|
2171
|
+
"data-testid": TID.mediaCarouselNext,
|
|
2172
|
+
onClick: () => goTo(index + 1),
|
|
2173
|
+
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",
|
|
2174
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
2175
|
+
}
|
|
2176
|
+
),
|
|
2177
|
+
/* @__PURE__ */ jsx("div", { className: "absolute bottom-5 left-1/2 flex -translate-x-1/2 gap-2", children: items.map((item, i) => /* @__PURE__ */ jsx(
|
|
2178
|
+
"button",
|
|
2179
|
+
{
|
|
2180
|
+
type: "button",
|
|
2181
|
+
"aria-label": `Go to slide ${i + 1}`,
|
|
2182
|
+
onClick: () => goTo(i),
|
|
2183
|
+
className: `h-2 rounded-full transition-all ${i === index ? "w-8 bg-white" : "w-2 bg-white/55 hover:bg-white"}`
|
|
2184
|
+
},
|
|
2185
|
+
`${item.title}-dot-${i}`
|
|
2186
|
+
)) })
|
|
2187
|
+
] })
|
|
2188
|
+
] });
|
|
2189
|
+
}
|
|
2190
|
+
function ProductSlider({
|
|
2191
|
+
title,
|
|
2192
|
+
subtitle,
|
|
2193
|
+
eyebrow,
|
|
2194
|
+
filter = "all",
|
|
2195
|
+
categoryName,
|
|
2196
|
+
collection,
|
|
2197
|
+
limit = 10,
|
|
2198
|
+
cta = "View all",
|
|
2199
|
+
href,
|
|
2200
|
+
className = ""
|
|
2201
|
+
}) {
|
|
2202
|
+
const config = useStorefrontConfig();
|
|
2203
|
+
const actions = useStorefrontActions();
|
|
2204
|
+
const viewportRef = useRef(null);
|
|
2205
|
+
const components = getStorefrontComponents(config);
|
|
2206
|
+
const ProductCardComponent = components.ProductCard ?? ProductCard;
|
|
2207
|
+
const { products, loading } = useProducts(
|
|
2208
|
+
filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
|
|
2209
|
+
);
|
|
2210
|
+
const visible = useMemo(() => {
|
|
2211
|
+
let list = products;
|
|
2212
|
+
if (filter === "sale") list = list.filter((p) => p.compareAtPrice != null && p.compareAtPrice > p.price);
|
|
2213
|
+
if (categoryName) list = list.filter((p) => p.categoryName === categoryName);
|
|
2214
|
+
if (collection) list = list.filter((p) => p.metadata?.collection === collection);
|
|
2215
|
+
return list.slice(0, limit);
|
|
2216
|
+
}, [categoryName, collection, filter, limit, products]);
|
|
2217
|
+
const scroll = (direction) => {
|
|
2218
|
+
viewportRef.current?.scrollBy({ left: direction * Math.max(320, viewportRef.current.clientWidth * 0.75), behavior: "smooth" });
|
|
2219
|
+
};
|
|
2220
|
+
if (!loading && visible.length === 0) return null;
|
|
2221
|
+
return /* @__PURE__ */ jsxs("section", { "data-testid": TID.productSlider, className: `mx-auto max-w-7xl px-4 py-14 sm:px-6 ${className}`, children: [
|
|
2222
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-8 flex flex-col gap-5 sm:flex-row sm:items-end sm:justify-between", children: [
|
|
2223
|
+
/* @__PURE__ */ jsxs("div", { className: "max-w-2xl", children: [
|
|
2224
|
+
eyebrow && /* @__PURE__ */ jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-primary", children: eyebrow }),
|
|
2225
|
+
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-3xl font-normal leading-tight tracking-normal sm:text-5xl", children: title }),
|
|
2226
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "mt-3 leading-7 text-muted-foreground", children: subtitle })
|
|
2227
|
+
] }),
|
|
2228
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2229
|
+
/* @__PURE__ */ jsx(
|
|
2230
|
+
"button",
|
|
2231
|
+
{
|
|
2232
|
+
type: "button",
|
|
2233
|
+
"aria-label": "Previous products",
|
|
2234
|
+
"data-testid": TID.productSliderPrev,
|
|
2235
|
+
onClick: () => scroll(-1),
|
|
2236
|
+
className: "inline-flex h-10 w-10 items-center justify-center border border-border bg-background transition hover:bg-foreground hover:text-background",
|
|
2237
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
|
|
2238
|
+
}
|
|
2239
|
+
),
|
|
2240
|
+
/* @__PURE__ */ jsx(
|
|
2241
|
+
"button",
|
|
2242
|
+
{
|
|
2243
|
+
type: "button",
|
|
2244
|
+
"aria-label": "Next products",
|
|
2245
|
+
"data-testid": TID.productSliderNext,
|
|
2246
|
+
onClick: () => scroll(1),
|
|
2247
|
+
className: "inline-flex h-10 w-10 items-center justify-center border border-border bg-background transition hover:bg-foreground hover:text-background",
|
|
2248
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
2249
|
+
}
|
|
2250
|
+
)
|
|
2251
|
+
] })
|
|
2252
|
+
] }),
|
|
2253
|
+
/* @__PURE__ */ jsx(
|
|
2254
|
+
"div",
|
|
2255
|
+
{
|
|
2256
|
+
ref: viewportRef,
|
|
2257
|
+
"data-testid": TID.productSliderViewport,
|
|
2258
|
+
className: "flex snap-x gap-5 overflow-x-auto pb-5 [scrollbar-width:thin]",
|
|
2259
|
+
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(
|
|
2260
|
+
ProductCardComponent,
|
|
2261
|
+
{
|
|
2262
|
+
product,
|
|
2263
|
+
config,
|
|
2264
|
+
commerceMode: config.commerceMode,
|
|
2265
|
+
actions
|
|
2266
|
+
}
|
|
2267
|
+
) }, product.id))
|
|
2268
|
+
}
|
|
2269
|
+
),
|
|
2270
|
+
cta && /* @__PURE__ */ jsx(
|
|
2271
|
+
Link,
|
|
2272
|
+
{
|
|
2273
|
+
to: href ?? config.catalogPath,
|
|
2274
|
+
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",
|
|
2275
|
+
children: cta
|
|
2276
|
+
}
|
|
2277
|
+
)
|
|
2278
|
+
] });
|
|
2279
|
+
}
|
|
1829
2280
|
function Icon({ name, className }) {
|
|
1830
2281
|
const C = LucideIcons[name];
|
|
1831
2282
|
return C ? /* @__PURE__ */ jsx(C, { className }) : null;
|
|
@@ -1850,7 +2301,7 @@ function PromoBanner({
|
|
|
1850
2301
|
}) {
|
|
1851
2302
|
return /* @__PURE__ */ jsxs("section", { "data-testid": TID.promoBanner, className: "group relative my-12 overflow-hidden", children: [
|
|
1852
2303
|
/* @__PURE__ */ jsx(
|
|
1853
|
-
|
|
2304
|
+
SmoothImage,
|
|
1854
2305
|
{
|
|
1855
2306
|
src: image ?? bannerPlaceholder(title, hue, hue + 50, 1600, 420),
|
|
1856
2307
|
alt: title,
|
|
@@ -1953,8 +2404,10 @@ function registerStorefrontBlocks() {
|
|
|
1953
2404
|
if (registered) return;
|
|
1954
2405
|
registered = true;
|
|
1955
2406
|
registerBlock("hero", HeroSection, { source: "sdk", label: "Hero" });
|
|
2407
|
+
registerBlock("mediaCarousel", MediaCarousel, { source: "sdk", label: "Media carousel" });
|
|
1956
2408
|
registerBlock("categories", CategoryShowcase, { source: "sdk", label: "Categories" });
|
|
1957
2409
|
registerBlock("products", ProductRail, { source: "sdk", label: "Product rail" });
|
|
2410
|
+
registerBlock("productSlider", ProductSlider, { source: "sdk", label: "Product slider" });
|
|
1958
2411
|
registerBlock("benefits", BenefitsRow, { source: "sdk", label: "Benefits row" });
|
|
1959
2412
|
registerBlock("banner", PromoBanner, { source: "sdk", label: "Promo banner" });
|
|
1960
2413
|
registerBlock("manifesto", ManifestoBlock, { source: "sdk", label: "Manifesto" });
|
|
@@ -1968,9 +2421,12 @@ function sectionsToBlocks(sections) {
|
|
|
1968
2421
|
return { type, id: `${type}-${i}`, props };
|
|
1969
2422
|
});
|
|
1970
2423
|
}
|
|
1971
|
-
function
|
|
2424
|
+
function StorefrontSections({ sections }) {
|
|
1972
2425
|
return /* @__PURE__ */ jsx("main", { children: renderBlocks(sectionsToBlocks(sections)) });
|
|
1973
2426
|
}
|
|
2427
|
+
function HomePage({ sections }) {
|
|
2428
|
+
return /* @__PURE__ */ jsx(StorefrontSections, { sections });
|
|
2429
|
+
}
|
|
1974
2430
|
function useProduct(slug2) {
|
|
1975
2431
|
const [product, setProduct] = useState(null);
|
|
1976
2432
|
const [loading, setLoading] = useState(true);
|
|
@@ -2082,18 +2538,208 @@ function ProductSpecs({ product }) {
|
|
|
2082
2538
|
}
|
|
2083
2539
|
function RelatedProducts({ product }) {
|
|
2084
2540
|
const config = useStorefrontConfig();
|
|
2085
|
-
const
|
|
2541
|
+
const actions = useStorefrontActions();
|
|
2542
|
+
const components = getStorefrontComponents(config);
|
|
2543
|
+
const Card = components.ProductCard ?? ProductCard;
|
|
2086
2544
|
const opts = product.categoryId ? { categoryId: product.categoryId, status: "active", limit: 5 } : { status: "active", limit: 5 };
|
|
2087
2545
|
const { products, loading } = useProducts(opts);
|
|
2088
2546
|
const related = products.filter((p) => p.id !== product.id).slice(0, 4);
|
|
2089
2547
|
if (loading || related.length === 0) return null;
|
|
2090
2548
|
return /* @__PURE__ */ jsxs("section", { className: "mt-16", children: [
|
|
2091
2549
|
/* @__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(
|
|
2550
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6 grid grid-cols-2 gap-5 md:grid-cols-4", children: related.map((p) => /* @__PURE__ */ jsx(
|
|
2551
|
+
Card,
|
|
2552
|
+
{
|
|
2553
|
+
product: p,
|
|
2554
|
+
config,
|
|
2555
|
+
commerceMode: config.commerceMode,
|
|
2556
|
+
actions
|
|
2557
|
+
},
|
|
2558
|
+
p.id
|
|
2559
|
+
)) })
|
|
2093
2560
|
] });
|
|
2094
2561
|
}
|
|
2562
|
+
function defaultSubject(prefix, product) {
|
|
2563
|
+
return `${prefix}: ${product.name}`;
|
|
2564
|
+
}
|
|
2565
|
+
function buildWhatsAppHref(url, product) {
|
|
2566
|
+
const text = encodeURIComponent(`Hello, I would like more information about ${product.name}.`);
|
|
2567
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
2568
|
+
return `${url}${sep}text=${text}`;
|
|
2569
|
+
}
|
|
2570
|
+
function ProductEnquiryForm({ product, onSuccess }) {
|
|
2571
|
+
const config = useStorefrontConfig();
|
|
2572
|
+
const [customerName, setCustomerName] = useState("");
|
|
2573
|
+
const [customerEmail, setCustomerEmail] = useState("");
|
|
2574
|
+
const [customerPhone, setCustomerPhone] = useState("");
|
|
2575
|
+
const [subject, setSubject] = useState(defaultSubject(config.enquiry.subjectPrefix, product));
|
|
2576
|
+
const [message, setMessage] = useState(`I would like more information about ${product.name}.`);
|
|
2577
|
+
const [status, setStatus] = useState("idle");
|
|
2578
|
+
const [error, setError] = useState(null);
|
|
2579
|
+
const submit = async (event) => {
|
|
2580
|
+
event.preventDefault();
|
|
2581
|
+
setStatus("submitting");
|
|
2582
|
+
setError(null);
|
|
2583
|
+
try {
|
|
2584
|
+
const provider = getShopProvider();
|
|
2585
|
+
if (provider.createProductEnquiry) {
|
|
2586
|
+
await provider.createProductEnquiry({
|
|
2587
|
+
productId: product.id,
|
|
2588
|
+
productName: product.name,
|
|
2589
|
+
productSlug: product.slug,
|
|
2590
|
+
customerName,
|
|
2591
|
+
customerEmail,
|
|
2592
|
+
customerPhone: customerPhone || void 0,
|
|
2593
|
+
subject,
|
|
2594
|
+
message,
|
|
2595
|
+
sourceUrl: typeof window === "undefined" ? void 0 : window.location.href,
|
|
2596
|
+
metadata: { sku: product.sku, categoryName: product.categoryName }
|
|
2597
|
+
});
|
|
2598
|
+
} else if (config.enquiry.email) {
|
|
2599
|
+
window.location.href = `mailto:${config.enquiry.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(message)}`;
|
|
2600
|
+
}
|
|
2601
|
+
setStatus("success");
|
|
2602
|
+
onSuccess?.();
|
|
2603
|
+
} catch (e) {
|
|
2604
|
+
setStatus("error");
|
|
2605
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
2606
|
+
}
|
|
2607
|
+
};
|
|
2608
|
+
return /* @__PURE__ */ jsxs(
|
|
2609
|
+
"form",
|
|
2610
|
+
{
|
|
2611
|
+
...storefrontComponentContracts.productDetail.enquiryForm,
|
|
2612
|
+
onSubmit: submit,
|
|
2613
|
+
className: "space-y-4",
|
|
2614
|
+
children: [
|
|
2615
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
|
|
2616
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5 text-sm font-medium", children: [
|
|
2617
|
+
/* @__PURE__ */ jsx("span", { children: "Your name" }),
|
|
2618
|
+
/* @__PURE__ */ jsx(
|
|
2619
|
+
"input",
|
|
2620
|
+
{
|
|
2621
|
+
"data-testid": TID.enquiryName,
|
|
2622
|
+
required: true,
|
|
2623
|
+
value: customerName,
|
|
2624
|
+
onChange: (e) => setCustomerName(e.target.value),
|
|
2625
|
+
className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2626
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2627
|
+
}
|
|
2628
|
+
)
|
|
2629
|
+
] }),
|
|
2630
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1.5 text-sm font-medium", children: [
|
|
2631
|
+
/* @__PURE__ */ jsx("span", { children: "Your email" }),
|
|
2632
|
+
/* @__PURE__ */ jsx(
|
|
2633
|
+
"input",
|
|
2634
|
+
{
|
|
2635
|
+
"data-testid": TID.enquiryEmail,
|
|
2636
|
+
required: true,
|
|
2637
|
+
type: "email",
|
|
2638
|
+
value: customerEmail,
|
|
2639
|
+
onChange: (e) => setCustomerEmail(e.target.value),
|
|
2640
|
+
className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2641
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2642
|
+
}
|
|
2643
|
+
)
|
|
2644
|
+
] })
|
|
2645
|
+
] }),
|
|
2646
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
|
|
2647
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
2648
|
+
"Phone",
|
|
2649
|
+
config.enquiry.requirePhone ? "" : " (optional)"
|
|
2650
|
+
] }),
|
|
2651
|
+
/* @__PURE__ */ jsx(
|
|
2652
|
+
"input",
|
|
2653
|
+
{
|
|
2654
|
+
"data-testid": TID.enquiryPhone,
|
|
2655
|
+
required: config.enquiry.requirePhone,
|
|
2656
|
+
value: customerPhone,
|
|
2657
|
+
onChange: (e) => setCustomerPhone(e.target.value),
|
|
2658
|
+
className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2659
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2660
|
+
}
|
|
2661
|
+
)
|
|
2662
|
+
] }),
|
|
2663
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
|
|
2664
|
+
/* @__PURE__ */ jsx("span", { children: "Subject" }),
|
|
2665
|
+
/* @__PURE__ */ jsx(
|
|
2666
|
+
"input",
|
|
2667
|
+
{
|
|
2668
|
+
"data-testid": TID.enquirySubject,
|
|
2669
|
+
required: true,
|
|
2670
|
+
value: subject,
|
|
2671
|
+
onChange: (e) => setSubject(e.target.value),
|
|
2672
|
+
className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2673
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2674
|
+
}
|
|
2675
|
+
)
|
|
2676
|
+
] }),
|
|
2677
|
+
/* @__PURE__ */ jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
|
|
2678
|
+
/* @__PURE__ */ jsx("span", { children: "Message" }),
|
|
2679
|
+
/* @__PURE__ */ jsx(
|
|
2680
|
+
"textarea",
|
|
2681
|
+
{
|
|
2682
|
+
"data-testid": TID.enquiryMessage,
|
|
2683
|
+
required: true,
|
|
2684
|
+
rows: 5,
|
|
2685
|
+
value: message,
|
|
2686
|
+
onChange: (e) => setMessage(e.target.value),
|
|
2687
|
+
className: "w-full resize-none border bg-background px-3 py-2 outline-none focus:border-primary",
|
|
2688
|
+
style: { borderRadius: "var(--sf-radius-input)" }
|
|
2689
|
+
}
|
|
2690
|
+
)
|
|
2691
|
+
] }),
|
|
2692
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
2693
|
+
/* @__PURE__ */ jsxs(
|
|
2694
|
+
"button",
|
|
2695
|
+
{
|
|
2696
|
+
"data-testid": TID.enquirySubmit,
|
|
2697
|
+
type: "submit",
|
|
2698
|
+
disabled: status === "submitting",
|
|
2699
|
+
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",
|
|
2700
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2701
|
+
children: [
|
|
2702
|
+
/* @__PURE__ */ jsx(Send, { className: "h-4 w-4" }),
|
|
2703
|
+
status === "submitting" ? "Sending..." : "Send enquiry"
|
|
2704
|
+
]
|
|
2705
|
+
}
|
|
2706
|
+
),
|
|
2707
|
+
config.enquiry.whatsappUrl && /* @__PURE__ */ jsxs(
|
|
2708
|
+
"a",
|
|
2709
|
+
{
|
|
2710
|
+
href: buildWhatsAppHref(config.enquiry.whatsappUrl, product),
|
|
2711
|
+
target: "_blank",
|
|
2712
|
+
rel: "noreferrer",
|
|
2713
|
+
className: "inline-flex items-center gap-2 border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
|
|
2714
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2715
|
+
children: [
|
|
2716
|
+
/* @__PURE__ */ jsx(MessageCircle, { className: "h-4 w-4" }),
|
|
2717
|
+
"WhatsApp"
|
|
2718
|
+
]
|
|
2719
|
+
}
|
|
2720
|
+
),
|
|
2721
|
+
config.enquiry.email && /* @__PURE__ */ jsxs(
|
|
2722
|
+
"a",
|
|
2723
|
+
{
|
|
2724
|
+
href: `mailto:${config.enquiry.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(message)}`,
|
|
2725
|
+
className: "inline-flex items-center gap-2 border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
|
|
2726
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2727
|
+
children: [
|
|
2728
|
+
/* @__PURE__ */ jsx(Mail, { className: "h-4 w-4" }),
|
|
2729
|
+
"Email"
|
|
2730
|
+
]
|
|
2731
|
+
}
|
|
2732
|
+
)
|
|
2733
|
+
] }),
|
|
2734
|
+
status === "success" && /* @__PURE__ */ jsx("p", { "data-testid": TID.enquirySuccess, className: "text-sm font-medium text-emerald-600", children: config.enquiry.successMessage }),
|
|
2735
|
+
error && /* @__PURE__ */ jsx("p", { "data-testid": TID.enquiryError, className: "text-sm font-medium text-red-600", children: error })
|
|
2736
|
+
]
|
|
2737
|
+
}
|
|
2738
|
+
);
|
|
2739
|
+
}
|
|
2095
2740
|
function ProductDetailPage({ slug: slug2 }) {
|
|
2096
2741
|
const config = useStorefrontConfig();
|
|
2742
|
+
const actions = useStorefrontActions();
|
|
2097
2743
|
const { product, loading } = useProduct(slug2);
|
|
2098
2744
|
const addItem = useCartStore((s) => s.addItem);
|
|
2099
2745
|
const [qty, setQty] = useState(1);
|
|
@@ -2129,21 +2775,56 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2129
2775
|
const soldOut = product.inventoryCount <= 0;
|
|
2130
2776
|
const lowStock = !soldOut && product.inventoryCount <= 5;
|
|
2131
2777
|
const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
|
|
2778
|
+
const components = getStorefrontComponents(config);
|
|
2779
|
+
const ProductDetailComponent = components.ProductDetail;
|
|
2780
|
+
const addToCart = () => addItem(product, qty, selectedOptions);
|
|
2781
|
+
const openEnquiry = () => {
|
|
2782
|
+
document.getElementById("storefront-product-enquiry")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
2783
|
+
};
|
|
2132
2784
|
const discountPct = product.compareAtPrice && product.compareAtPrice > product.price ? Math.round((1 - product.price / product.compareAtPrice) * 100) : 0;
|
|
2133
|
-
|
|
2785
|
+
if (ProductDetailComponent) {
|
|
2786
|
+
return /* @__PURE__ */ jsx(
|
|
2787
|
+
ProductDetailComponent,
|
|
2788
|
+
{
|
|
2789
|
+
product,
|
|
2790
|
+
config,
|
|
2791
|
+
commerceMode: config.commerceMode,
|
|
2792
|
+
primaryImage: image,
|
|
2793
|
+
optionGroups,
|
|
2794
|
+
selectedOptions,
|
|
2795
|
+
setSelectedOptions,
|
|
2796
|
+
quantity: qty,
|
|
2797
|
+
setQuantity: setQty,
|
|
2798
|
+
soldOut,
|
|
2799
|
+
lowStock,
|
|
2800
|
+
actions,
|
|
2801
|
+
addToCart,
|
|
2802
|
+
openEnquiry
|
|
2803
|
+
}
|
|
2804
|
+
);
|
|
2805
|
+
}
|
|
2806
|
+
return /* @__PURE__ */ jsxs("main", { ...storefrontComponentContracts.productDetail.root, className: "mx-auto max-w-5xl px-4 py-8 sm:px-6", children: [
|
|
2134
2807
|
/* @__PURE__ */ jsxs(Link, { to: "/", className: "mb-6 inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [
|
|
2135
2808
|
/* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }),
|
|
2136
2809
|
" Continuar comprando"
|
|
2137
2810
|
] }),
|
|
2138
2811
|
/* @__PURE__ */ jsxs("div", { className: "grid animate-fade-up gap-10 md:grid-cols-2", children: [
|
|
2139
|
-
/* @__PURE__ */ jsx(
|
|
2140
|
-
"
|
|
2812
|
+
/* @__PURE__ */ jsx(
|
|
2813
|
+
"div",
|
|
2141
2814
|
{
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2815
|
+
...storefrontComponentContracts.productDetail.gallery,
|
|
2816
|
+
className: "group overflow-hidden border bg-muted",
|
|
2817
|
+
style: { borderRadius: "var(--sf-radius-card)" },
|
|
2818
|
+
children: image && /* @__PURE__ */ jsx(
|
|
2819
|
+
SmoothImage,
|
|
2820
|
+
{
|
|
2821
|
+
src: image.url,
|
|
2822
|
+
alt: image.altText ?? product.name,
|
|
2823
|
+
className: "aspect-square w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
|
2824
|
+
}
|
|
2825
|
+
)
|
|
2145
2826
|
}
|
|
2146
|
-
)
|
|
2827
|
+
),
|
|
2147
2828
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col py-2 lg:sticky lg:top-24 lg:self-start", children: [
|
|
2148
2829
|
/* @__PURE__ */ jsxs("nav", { className: "mb-3 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
2149
2830
|
/* @__PURE__ */ jsx(Link, { to: "/", className: "transition-colors hover:text-foreground", children: "In\xEDcio" }),
|
|
@@ -2155,7 +2836,7 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2155
2836
|
/* @__PURE__ */ jsx("span", { className: "text-foreground", children: product.name })
|
|
2156
2837
|
] }),
|
|
2157
2838
|
/* @__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: [
|
|
2839
|
+
config.commerceMode === "checkout" && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center gap-3", children: [
|
|
2159
2840
|
/* @__PURE__ */ jsx("div", { className: "text-xl", children: /* @__PURE__ */ jsx(
|
|
2160
2841
|
Price,
|
|
2161
2842
|
{
|
|
@@ -2172,7 +2853,7 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2172
2853
|
] })
|
|
2173
2854
|
] }),
|
|
2174
2855
|
/* @__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: [
|
|
2856
|
+
config.commerceMode === "checkout" && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm", children: [
|
|
2176
2857
|
soldOut ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 font-medium text-muted-foreground", children: [
|
|
2177
2858
|
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-muted-foreground" }),
|
|
2178
2859
|
" Sem estoque"
|
|
@@ -2198,7 +2879,7 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2198
2879
|
onChange: setSelectedOptions
|
|
2199
2880
|
}
|
|
2200
2881
|
),
|
|
2201
|
-
/* @__PURE__ */
|
|
2882
|
+
/* @__PURE__ */ jsx("div", { ...storefrontComponentContracts.productDetail.actions, className: "mt-8", children: config.commerceMode === "checkout" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
2202
2883
|
/* @__PURE__ */ jsx(
|
|
2203
2884
|
QuantityInput,
|
|
2204
2885
|
{
|
|
@@ -2214,14 +2895,32 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2214
2895
|
type: "button",
|
|
2215
2896
|
"data-testid": TID.pdpAddToCart,
|
|
2216
2897
|
disabled: soldOut,
|
|
2217
|
-
onClick:
|
|
2898
|
+
onClick: addToCart,
|
|
2218
2899
|
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
2900
|
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2220
2901
|
children: soldOut ? "Esgotado" : "Adicionar ao carrinho"
|
|
2221
2902
|
}
|
|
2222
2903
|
)
|
|
2223
|
-
] })
|
|
2224
|
-
|
|
2904
|
+
] }) : config.commerceMode === "enquiry" ? /* @__PURE__ */ jsx(
|
|
2905
|
+
"button",
|
|
2906
|
+
{
|
|
2907
|
+
type: "button",
|
|
2908
|
+
...storefrontComponentContracts.productDetail.enquiryButton,
|
|
2909
|
+
onClick: openEnquiry,
|
|
2910
|
+
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",
|
|
2911
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2912
|
+
children: config.enquiry.label
|
|
2913
|
+
}
|
|
2914
|
+
) : /* @__PURE__ */ jsx(
|
|
2915
|
+
Link,
|
|
2916
|
+
{
|
|
2917
|
+
to: config.catalogPath,
|
|
2918
|
+
className: "inline-flex border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
|
|
2919
|
+
style: { borderRadius: "var(--sf-radius-button)" },
|
|
2920
|
+
children: "Voltar ao cat\xE1logo"
|
|
2921
|
+
}
|
|
2922
|
+
) }),
|
|
2923
|
+
config.commerceMode === "checkout" && /* @__PURE__ */ jsxs("div", { className: "mt-6 grid grid-cols-3 gap-3 border-t pt-6 text-center", children: [
|
|
2225
2924
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
|
|
2226
2925
|
/* @__PURE__ */ jsx(Truck, { className: "h-5 w-5 text-primary" }),
|
|
2227
2926
|
" Envio r\xE1pido"
|
|
@@ -2237,6 +2936,11 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2237
2936
|
] })
|
|
2238
2937
|
] })
|
|
2239
2938
|
] }),
|
|
2939
|
+
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: [
|
|
2940
|
+
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-2xl font-semibold", children: "Need more information?" }),
|
|
2941
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: "Send an enquiry about this product." }),
|
|
2942
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsx(ProductEnquiryForm, { product }) })
|
|
2943
|
+
] }) }),
|
|
2240
2944
|
/* @__PURE__ */ jsx(ProductSpecs, { product }),
|
|
2241
2945
|
/* @__PURE__ */ jsx(RelatedProducts, { product })
|
|
2242
2946
|
] });
|
|
@@ -2277,7 +2981,8 @@ async function placeStorefrontOrder({
|
|
|
2277
2981
|
}))
|
|
2278
2982
|
});
|
|
2279
2983
|
if (markPaid) {
|
|
2280
|
-
await provider.
|
|
2984
|
+
if (provider.confirmPayment) await provider.confirmPayment(order.id);
|
|
2985
|
+
else await provider.updateOrder(order.id, { financialStatus: "paid" });
|
|
2281
2986
|
}
|
|
2282
2987
|
return { order, customerId: customerId ?? order.customerId ?? "" };
|
|
2283
2988
|
}
|
|
@@ -2792,7 +3497,7 @@ function CheckoutPage() {
|
|
|
2792
3497
|
/* @__PURE__ */ jsx("h2", { className: "sr-only", children: "Resumo do pedido" }),
|
|
2793
3498
|
/* @__PURE__ */ jsx("ul", { className: "space-y-4", children: cart.lines.map((line) => /* @__PURE__ */ jsxs("li", { className: "flex gap-3 text-sm", children: [
|
|
2794
3499
|
/* @__PURE__ */ jsxs("div", { className: "relative h-16 w-16 flex-none overflow-hidden rounded-lg border bg-background", children: [
|
|
2795
|
-
line.imageUrl && /* @__PURE__ */ jsx(
|
|
3500
|
+
line.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: line.imageUrl, alt: line.name, className: "h-full w-full object-cover" }),
|
|
2796
3501
|
/* @__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
3502
|
] }),
|
|
2798
3503
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
@@ -3214,7 +3919,7 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
3214
3919
|
/* @__PURE__ */ jsx(OrderTrackingTimeline, { order })
|
|
3215
3920
|
] }),
|
|
3216
3921
|
/* @__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(
|
|
3922
|
+
item.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: item.imageUrl, alt: item.name, className: "h-12 w-12 rounded-lg border object-cover" }),
|
|
3218
3923
|
/* @__PURE__ */ jsxs("span", { className: "flex-1", children: [
|
|
3219
3924
|
item.name,
|
|
3220
3925
|
" ",
|
|
@@ -3422,7 +4127,7 @@ function OrdersPanel() {
|
|
|
3422
4127
|
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: new Date(order.createdAt).toLocaleDateString(config.locale) }),
|
|
3423
4128
|
/* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(OrderTrackingTimeline, { order, compact: true }) }),
|
|
3424
4129
|
/* @__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(
|
|
4130
|
+
item.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: item.imageUrl, alt: item.name, className: "h-6 w-6 rounded object-cover" }),
|
|
3426
4131
|
item.name,
|
|
3427
4132
|
" \xD7 ",
|
|
3428
4133
|
item.quantity
|
|
@@ -3602,7 +4307,7 @@ function PolicyPage({ kind }) {
|
|
|
3602
4307
|
] }) })
|
|
3603
4308
|
] });
|
|
3604
4309
|
}
|
|
3605
|
-
var StorefrontErrorBoundary = class extends
|
|
4310
|
+
var StorefrontErrorBoundary = class extends React6.Component {
|
|
3606
4311
|
constructor() {
|
|
3607
4312
|
super(...arguments);
|
|
3608
4313
|
this.state = { error: null };
|
|
@@ -3703,8 +4408,8 @@ function RouteSwitch({ path }) {
|
|
|
3703
4408
|
if (product?.slug) return /* @__PURE__ */ jsx(ProductDetailPage, { slug: product.slug });
|
|
3704
4409
|
const order = matchPath("/order/:id", path);
|
|
3705
4410
|
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, {});
|
|
4411
|
+
if (matchPath("/checkout", path) && config.features.checkout) return /* @__PURE__ */ jsx(CheckoutPage, {});
|
|
4412
|
+
if (matchPath("/account", path) && config.features.accounts) return /* @__PURE__ */ jsx(MyPurchasesPage, {});
|
|
3708
4413
|
if (matchPath("/catalog", path)) return /* @__PURE__ */ jsx(CatalogPage, {});
|
|
3709
4414
|
if (matchPath("/privacy", path)) return /* @__PURE__ */ jsx(PolicyPage, { kind: "privacy" });
|
|
3710
4415
|
if (matchPath("/terms", path)) return /* @__PURE__ */ jsx(PolicyPage, { kind: "terms" });
|
|
@@ -3746,12 +4451,26 @@ function buildMockSeed(config) {
|
|
|
3746
4451
|
};
|
|
3747
4452
|
}
|
|
3748
4453
|
function initStorefrontRuntime(config) {
|
|
3749
|
-
|
|
4454
|
+
if (resolveConfig(config).features.accounts) {
|
|
4455
|
+
initCustomerAuth(resolveAuthAdapter(config.auth, {
|
|
4456
|
+
supabaseUrl: config.supabaseUrl,
|
|
4457
|
+
supabaseAnonKey: config.supabaseAnonKey
|
|
4458
|
+
}));
|
|
4459
|
+
}
|
|
3750
4460
|
if (config.provider) {
|
|
3751
4461
|
setShopProvider(config.provider);
|
|
3752
4462
|
} else {
|
|
3753
4463
|
setShopProvider(createMockShopProvider(buildMockSeed(config)));
|
|
3754
4464
|
}
|
|
4465
|
+
const reconcileCart = () => {
|
|
4466
|
+
const provider = getShopProvider();
|
|
4467
|
+
void useCartStore.getState().reconcile(async (line) => {
|
|
4468
|
+
const product = await provider.getProduct(line.productId);
|
|
4469
|
+
return product != null;
|
|
4470
|
+
});
|
|
4471
|
+
};
|
|
4472
|
+
if (useCartStore.persist.hasHydrated()) reconcileCart();
|
|
4473
|
+
else useCartStore.persist.onFinishHydration(reconcileCart);
|
|
3755
4474
|
if (config.supabaseUrl || config.supabaseAnonKey) {
|
|
3756
4475
|
console.warn(
|
|
3757
4476
|
"@fayz-ai/shop: supabaseUrl/supabaseAnonKey are legacy fields. Pass an explicit provider/adapter or use the Fayz SDK broker path."
|
|
@@ -3762,14 +4481,20 @@ function StorefrontShell() {
|
|
|
3762
4481
|
const config = useStorefrontConfig();
|
|
3763
4482
|
const path = useHashPath();
|
|
3764
4483
|
const focused = isFocusedRoute(config, path);
|
|
3765
|
-
|
|
4484
|
+
const components = getStorefrontComponents(config);
|
|
4485
|
+
const chromeProps = { config, commerceMode: config.commerceMode };
|
|
4486
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3766
4487
|
config.theme && /* @__PURE__ */ jsx(StorefrontThemeStyle, { theme: config.theme }),
|
|
3767
|
-
!focused && /* @__PURE__ */ jsx(StorefrontHeader, {}),
|
|
4488
|
+
!focused && (components.Header ? /* @__PURE__ */ jsx(components.Header, { ...chromeProps }) : /* @__PURE__ */ jsx(StorefrontHeader, {})),
|
|
3768
4489
|
/* @__PURE__ */ jsx(StorefrontErrorBoundary, { children: /* @__PURE__ */ jsx(RouteSwitch, { path }) }, path),
|
|
3769
|
-
!focused && /* @__PURE__ */ jsx(CartDrawer, {}),
|
|
3770
|
-
!focused && /* @__PURE__ */ jsx(StorefrontFooter, {}),
|
|
4490
|
+
!focused && config.features.cart && /* @__PURE__ */ jsx(CartDrawer, {}),
|
|
4491
|
+
!focused && (components.Footer ? /* @__PURE__ */ jsx(components.Footer, { ...chromeProps }) : /* @__PURE__ */ jsx(StorefrontFooter, {})),
|
|
3771
4492
|
/* @__PURE__ */ jsx(Toaster, {})
|
|
3772
4493
|
] });
|
|
4494
|
+
if (components.Shell) {
|
|
4495
|
+
return /* @__PURE__ */ jsx(components.Shell, { ...chromeProps, children: body });
|
|
4496
|
+
}
|
|
4497
|
+
return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-background text-foreground", children: body });
|
|
3773
4498
|
}
|
|
3774
4499
|
function createStorefrontApp(config) {
|
|
3775
4500
|
const resolved = resolveConfig(config);
|
|
@@ -3780,6 +4505,37 @@ function createStorefrontApp(config) {
|
|
|
3780
4505
|
StorefrontApp.displayName = "StorefrontApp";
|
|
3781
4506
|
return StorefrontApp;
|
|
3782
4507
|
}
|
|
4508
|
+
function createStorefront(options) {
|
|
4509
|
+
const routes = [...options.pages ?? [], ...options.routes ?? []];
|
|
4510
|
+
const config = {
|
|
4511
|
+
...options.config,
|
|
4512
|
+
provider: options.provider ?? options.config.provider,
|
|
4513
|
+
components: {
|
|
4514
|
+
...options.config.components,
|
|
4515
|
+
...options.components
|
|
4516
|
+
},
|
|
4517
|
+
routes: routes.length > 0 ? routes : options.config.routes,
|
|
4518
|
+
home: options.sections ? { ...options.config.home ?? { sections: [] }, sections: options.sections } : options.config.home
|
|
4519
|
+
};
|
|
4520
|
+
return createStorefrontApp(config);
|
|
4521
|
+
}
|
|
4522
|
+
|
|
4523
|
+
// src/define.ts
|
|
4524
|
+
function defineStorefrontConfig(config) {
|
|
4525
|
+
return config;
|
|
4526
|
+
}
|
|
4527
|
+
function defineStorefrontComponents(components) {
|
|
4528
|
+
return components;
|
|
4529
|
+
}
|
|
4530
|
+
function defineStorefrontRoutes(routes) {
|
|
4531
|
+
return routes;
|
|
4532
|
+
}
|
|
4533
|
+
function defineStorefrontSections(sections) {
|
|
4534
|
+
return sections;
|
|
4535
|
+
}
|
|
4536
|
+
function defineStorefrontApp(options) {
|
|
4537
|
+
return options;
|
|
4538
|
+
}
|
|
3783
4539
|
var runtimeConfigRegistry = /* @__PURE__ */ new Map();
|
|
3784
4540
|
function slug(name) {
|
|
3785
4541
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "app";
|
|
@@ -3838,7 +4594,7 @@ function manifestToStorefrontConfig(manifest, surfaceName) {
|
|
|
3838
4594
|
provider: manifest.backend?.provider,
|
|
3839
4595
|
url: manifest.backend?.url
|
|
3840
4596
|
},
|
|
3841
|
-
|
|
4597
|
+
components: runtimeConfig?.components,
|
|
3842
4598
|
routes: runtimeConfig?.routes,
|
|
3843
4599
|
provider: runtimeConfig?.provider,
|
|
3844
4600
|
auth: runtimeConfig?.auth,
|
|
@@ -3846,9 +4602,9 @@ function manifestToStorefrontConfig(manifest, surfaceName) {
|
|
|
3846
4602
|
};
|
|
3847
4603
|
}
|
|
3848
4604
|
function StorefrontScaffold({ manifest, surface }) {
|
|
3849
|
-
const config =
|
|
3850
|
-
const resolved =
|
|
3851
|
-
const inited =
|
|
4605
|
+
const config = React6.useMemo(() => manifestToStorefrontConfig(manifest, surface), [manifest, surface]);
|
|
4606
|
+
const resolved = React6.useMemo(() => resolveConfig(config), [config]);
|
|
4607
|
+
const inited = React6.useRef(false);
|
|
3852
4608
|
if (!inited.current) {
|
|
3853
4609
|
initStorefrontRuntime(config);
|
|
3854
4610
|
inited.current = true;
|
|
@@ -4074,13 +4830,75 @@ var atelierTemplate = {
|
|
|
4074
4830
|
]
|
|
4075
4831
|
})
|
|
4076
4832
|
};
|
|
4833
|
+
var foodTemplate = {
|
|
4834
|
+
id: "food",
|
|
4835
|
+
inspiration: "Delivery / restaurant storefront",
|
|
4836
|
+
announcement: "ENTREGA GR\xC1TIS ACIMA DE R$ 150 \xB7 PE\xC7A J\xC1",
|
|
4837
|
+
theme: {
|
|
4838
|
+
name: "food",
|
|
4839
|
+
colors: {
|
|
4840
|
+
background: "30 33% 97%",
|
|
4841
|
+
foreground: "20 14% 15%",
|
|
4842
|
+
primary: "14 84% 50%",
|
|
4843
|
+
primaryForeground: "0 0% 100%",
|
|
4844
|
+
card: "30 33% 99%",
|
|
4845
|
+
cardForeground: "20 14% 15%",
|
|
4846
|
+
muted: "28 20% 92%",
|
|
4847
|
+
mutedForeground: "22 10% 42%",
|
|
4848
|
+
border: "28 18% 85%",
|
|
4849
|
+
headerBackground: "20 14% 11%",
|
|
4850
|
+
headerForeground: "30 24% 96%",
|
|
4851
|
+
announcementBackground: "14 84% 50%",
|
|
4852
|
+
announcementForeground: "0 0% 100%"
|
|
4853
|
+
},
|
|
4854
|
+
font: { heading: "Poppins", body: "Inter" },
|
|
4855
|
+
radius: "round",
|
|
4856
|
+
header: { variant: "classic" },
|
|
4857
|
+
productCard: { style: "card", imageAspect: "square" }
|
|
4858
|
+
},
|
|
4859
|
+
home: (storeName, images) => ({
|
|
4860
|
+
sections: [
|
|
4861
|
+
{
|
|
4862
|
+
type: "hero",
|
|
4863
|
+
variant: "banner",
|
|
4864
|
+
height: "tall",
|
|
4865
|
+
slides: [
|
|
4866
|
+
{ title: "Sabor de verdade, na sua porta", subtitle: `${storeName} \u2014 feito na hora, do jeito que voc\xEA ama.`, cta: "Ver card\xE1pio", href: "/catalog", hue: 20, image: images?.hero?.[0] }
|
|
4867
|
+
]
|
|
4868
|
+
},
|
|
4869
|
+
{
|
|
4870
|
+
type: "benefits",
|
|
4871
|
+
items: [
|
|
4872
|
+
{ icon: "Flame", title: "Feito na hora", text: "Preparado com capricho" },
|
|
4873
|
+
{ icon: "Truck", title: "Entrega r\xE1pida", text: "Frete gr\xE1tis acima de R$ 150" },
|
|
4874
|
+
{ icon: "CreditCard", title: "Pagamento f\xE1cil", text: "Pix, cart\xE3o e mais" }
|
|
4875
|
+
]
|
|
4876
|
+
},
|
|
4877
|
+
{ type: "categories", style: "tiles", title: "Nosso card\xE1pio" },
|
|
4878
|
+
{ type: "products", title: "Mais pedidos", eyebrow: "favoritos da casa", filter: "all", limit: 8 },
|
|
4879
|
+
{ type: "banner", title: "Combo do dia", eyebrow: "oferta", subtitle: "Sele\xE7\xE3o especial com desconto", cta: "Aproveitar", hue: 14, image: images?.banners?.[0] },
|
|
4880
|
+
{ type: "products", title: "Novidades no card\xE1pio", filter: "new", limit: 4 },
|
|
4881
|
+
{
|
|
4882
|
+
type: "testimonials",
|
|
4883
|
+
title: "O que dizem por a\xED",
|
|
4884
|
+
items: [
|
|
4885
|
+
{ quote: "Chegou quentinho e absurdo de bom. Virei cliente!", author: "Rafael M." },
|
|
4886
|
+
{ quote: "Sabor de restaurante em casa, sem complica\xE7\xE3o.", author: "Beatriz L." },
|
|
4887
|
+
{ quote: "Entrega r\xE1pida e por\xE7\xE3o generosa. Recomendo demais.", author: "Diego S." }
|
|
4888
|
+
]
|
|
4889
|
+
},
|
|
4890
|
+
{ type: "newsletter", title: "Receba nossas ofertas", subtitle: "Promo\xE7\xF5es e novidades do card\xE1pio, direto no seu e-mail." }
|
|
4891
|
+
]
|
|
4892
|
+
})
|
|
4893
|
+
};
|
|
4077
4894
|
var storefrontTemplates = {
|
|
4078
4895
|
mare: mareTemplate,
|
|
4079
4896
|
sertao: sertaoTemplate,
|
|
4080
4897
|
volt: voltTemplate,
|
|
4081
|
-
atelier: atelierTemplate
|
|
4898
|
+
atelier: atelierTemplate,
|
|
4899
|
+
food: foodTemplate
|
|
4082
4900
|
};
|
|
4083
4901
|
|
|
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,
|
|
4902
|
+
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, foodTemplate, 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
4903
|
//# sourceMappingURL=index.js.map
|
|
4086
4904
|
//# sourceMappingURL=index.js.map
|