@fayz-ai/storefront 0.8.0 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.d.ts.map +1 -1
- package/dist/components/CartDrawer.d.ts.map +1 -1
- package/dist/components/DeliveryEstimator.d.ts +17 -0
- package/dist/components/DeliveryEstimator.d.ts.map +1 -0
- package/dist/components/ProductGallery.d.ts +22 -0
- package/dist/components/ProductGallery.d.ts.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.cjs +674 -275
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +576 -177
- package/dist/index.js.map +1 -1
- package/dist/pages/CheckoutPage.d.ts.map +1 -1
- package/dist/pages/ProductDetailPage.d.ts.map +1 -1
- package/dist/stores/cart.store.d.ts +16 -0
- package/dist/stores/cart.store.d.ts.map +1 -1
- package/dist/stores/delivery.store.d.ts +64 -0
- package/dist/stores/delivery.store.d.ts.map +1 -0
- package/dist/testids.d.ts +11 -0
- package/dist/testids.d.ts.map +1 -1
- package/dist/workflows/checkout.d.ts +5 -2
- package/dist/workflows/checkout.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/auth.ts +57 -1
- package/src/components/CartDrawer.tsx +23 -1
- package/src/components/DeliveryEstimator.tsx +157 -0
- package/src/components/ProductGallery.tsx +174 -0
- package/src/config.ts +15 -3
- package/src/pages/CheckoutPage.tsx +240 -134
- package/src/pages/ProductDetailPage.tsx +17 -13
- package/src/stores/cart.store.ts +21 -0
- package/src/stores/delivery.store.ts +152 -0
- package/src/testids.ts +12 -0
- package/src/workflows/checkout.ts +30 -5
package/dist/index.cjs
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
var runtime = require('@fayz-ai/shop/runtime');
|
|
4
4
|
var mock = require('@fayz-ai/shop/mock');
|
|
5
5
|
var pluginAuth = require('@fayz-ai/plugin-auth');
|
|
6
|
+
var shop = require('@fayz-ai/sdk/shop');
|
|
6
7
|
var zustand = require('zustand');
|
|
7
8
|
var middleware = require('zustand/middleware');
|
|
8
|
-
var
|
|
9
|
+
var React7 = require('react');
|
|
9
10
|
var core = require('@fayz-ai/core');
|
|
10
11
|
var LucideIcons = require('lucide-react');
|
|
11
12
|
var jsxRuntime = require('react/jsx-runtime');
|
|
12
|
-
var shop = require('@fayz-ai/shop');
|
|
13
|
+
var shop$1 = require('@fayz-ai/shop');
|
|
13
14
|
|
|
14
15
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
16
|
|
|
@@ -31,7 +32,7 @@ function _interopNamespace(e) {
|
|
|
31
32
|
return Object.freeze(n);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
var
|
|
35
|
+
var React7__default = /*#__PURE__*/_interopDefault(React7);
|
|
35
36
|
var LucideIcons__namespace = /*#__PURE__*/_interopNamespace(LucideIcons);
|
|
36
37
|
|
|
37
38
|
// src/createStorefrontApp.tsx
|
|
@@ -76,10 +77,23 @@ function resolveAuthAdapter(configured, fallback) {
|
|
|
76
77
|
}
|
|
77
78
|
}).adapter;
|
|
78
79
|
}
|
|
80
|
+
var _accessToken = null;
|
|
81
|
+
var _hadAuthUser = false;
|
|
82
|
+
async function syncAccessToken(adapter2) {
|
|
83
|
+
try {
|
|
84
|
+
const existing = await adapter2.getSession();
|
|
85
|
+
_accessToken = existing?.session.accessToken ?? null;
|
|
86
|
+
} catch {
|
|
87
|
+
_accessToken = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
79
90
|
function initCustomerAuth(adapter2) {
|
|
80
91
|
_adapter = adapter2;
|
|
92
|
+
shop.setShopAccessTokenResolver(() => _accessToken);
|
|
81
93
|
adapter2.getSession().then((existing) => {
|
|
94
|
+
_accessToken = existing?.session.accessToken ?? null;
|
|
82
95
|
if (!existing) return;
|
|
96
|
+
_hadAuthUser = true;
|
|
83
97
|
const store = useSessionStore.getState();
|
|
84
98
|
if (store.email !== existing.user.email) {
|
|
85
99
|
void linkCustomer(existing.user);
|
|
@@ -87,7 +101,16 @@ function initCustomerAuth(adapter2) {
|
|
|
87
101
|
}).catch(() => {
|
|
88
102
|
});
|
|
89
103
|
adapter2.onAuthStateChange((user) => {
|
|
90
|
-
if (!user)
|
|
104
|
+
if (!user) {
|
|
105
|
+
_accessToken = null;
|
|
106
|
+
if (_hadAuthUser) {
|
|
107
|
+
_hadAuthUser = false;
|
|
108
|
+
useSessionStore.getState().signOut();
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
_hadAuthUser = true;
|
|
113
|
+
void syncAccessToken(adapter2);
|
|
91
114
|
});
|
|
92
115
|
}
|
|
93
116
|
function getCustomerAuthAdapter() {
|
|
@@ -123,6 +146,8 @@ async function establishCustomerSession(email, opts) {
|
|
|
123
146
|
if (opts?.password) {
|
|
124
147
|
const adapter2 = getCustomerAuthAdapter();
|
|
125
148
|
const session = await adapter2.signIn(normalized, opts.password);
|
|
149
|
+
_accessToken = session.accessToken ?? null;
|
|
150
|
+
_hadAuthUser = true;
|
|
126
151
|
return linkCustomer(session.user.email ? session.user : { ...session.user, email: normalized }, opts.name);
|
|
127
152
|
}
|
|
128
153
|
return linkCustomer({ email: normalized }, opts?.name);
|
|
@@ -134,12 +159,16 @@ async function signUpCustomer(email, password, name) {
|
|
|
134
159
|
const adapter2 = getCustomerAuthAdapter();
|
|
135
160
|
const normalized = email.trim().toLowerCase();
|
|
136
161
|
const session = await adapter2.signUp(normalized, password, name);
|
|
162
|
+
_accessToken = session.accessToken ?? null;
|
|
163
|
+
_hadAuthUser = true;
|
|
137
164
|
return linkCustomer(session.user.email ? session.user : { ...session.user, email: normalized }, name);
|
|
138
165
|
}
|
|
139
166
|
async function signOutCustomer() {
|
|
140
167
|
try {
|
|
141
168
|
await getCustomerAuthAdapter().signOut();
|
|
142
169
|
} finally {
|
|
170
|
+
_accessToken = null;
|
|
171
|
+
_hadAuthUser = false;
|
|
143
172
|
useSessionStore.getState().signOut();
|
|
144
173
|
}
|
|
145
174
|
}
|
|
@@ -152,7 +181,10 @@ function resolveConfig(config) {
|
|
|
152
181
|
currency: config.currency ?? "BRL",
|
|
153
182
|
locale: config.locale ?? "pt-BR",
|
|
154
183
|
shipping: { flatRate: config.shipping?.flatRate ?? 0, freeAbove: config.shipping?.freeAbove },
|
|
155
|
-
payments: {
|
|
184
|
+
payments: {
|
|
185
|
+
mode: config.payments?.mode ?? "mock",
|
|
186
|
+
methods: config.payments?.methods?.length ? config.payments.methods : ["pix", "credit_card", "cash"]
|
|
187
|
+
},
|
|
156
188
|
commerceMode,
|
|
157
189
|
enquiry: {
|
|
158
190
|
label: config.enquiry?.label ?? "Contact me",
|
|
@@ -184,15 +216,15 @@ function resolveConfig(config) {
|
|
|
184
216
|
] : [{ label: "Loja", to: "/" }])
|
|
185
217
|
};
|
|
186
218
|
}
|
|
187
|
-
var StorefrontConfigContext =
|
|
219
|
+
var StorefrontConfigContext = React7.createContext(null);
|
|
188
220
|
var StorefrontConfigProvider = StorefrontConfigContext.Provider;
|
|
189
221
|
function useStorefrontConfig() {
|
|
190
|
-
const ctx =
|
|
222
|
+
const ctx = React7.useContext(StorefrontConfigContext);
|
|
191
223
|
if (!ctx) throw new Error("useStorefrontConfig must be used inside createStorefrontApp");
|
|
192
224
|
return ctx;
|
|
193
225
|
}
|
|
194
226
|
function useStorefrontConfigOptional() {
|
|
195
|
-
return
|
|
227
|
+
return React7.useContext(StorefrontConfigContext);
|
|
196
228
|
}
|
|
197
229
|
function getStorefrontComponents(config) {
|
|
198
230
|
return config.components ?? {};
|
|
@@ -202,8 +234,8 @@ function navigateTo(to) {
|
|
|
202
234
|
adapter.navigate(to);
|
|
203
235
|
}
|
|
204
236
|
function useHashPath() {
|
|
205
|
-
const [path, setPath] =
|
|
206
|
-
|
|
237
|
+
const [path, setPath] = React7.useState(() => adapter.getCurrentPath() || "/");
|
|
238
|
+
React7.useEffect(() => {
|
|
207
239
|
const unsub = adapter.onPathChange((p) => {
|
|
208
240
|
setPath(p || "/");
|
|
209
241
|
window.scrollTo(0, 0);
|
|
@@ -227,7 +259,7 @@ function matchPath(pattern, path) {
|
|
|
227
259
|
return params;
|
|
228
260
|
}
|
|
229
261
|
function Link({ to, children, ...rest }) {
|
|
230
|
-
return
|
|
262
|
+
return React7__default.default.createElement("a", { href: `#${to}`, ...rest }, children);
|
|
231
263
|
}
|
|
232
264
|
|
|
233
265
|
// src/format.ts
|
|
@@ -237,6 +269,87 @@ function formatMoney(value, currency = "BRL", locale = "pt-BR") {
|
|
|
237
269
|
function roundCents(value) {
|
|
238
270
|
return Math.round(value * 100) / 100;
|
|
239
271
|
}
|
|
272
|
+
var EMPTY = {
|
|
273
|
+
postalCode: "",
|
|
274
|
+
address: null,
|
|
275
|
+
options: [],
|
|
276
|
+
selectedZoneId: null,
|
|
277
|
+
status: "idle",
|
|
278
|
+
error: null
|
|
279
|
+
};
|
|
280
|
+
var useDeliveryStore = zustand.create()(
|
|
281
|
+
middleware.persist(
|
|
282
|
+
(set, get) => ({
|
|
283
|
+
...EMPTY,
|
|
284
|
+
async resolve(postalCode, subtotal) {
|
|
285
|
+
const code = core.normalizePostalCode(postalCode);
|
|
286
|
+
if (code.length !== 8) {
|
|
287
|
+
set({ ...EMPTY, postalCode, status: "error", error: "Digite os 8 d\xEDgitos do CEP." });
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
set({ postalCode: code, status: "loading", error: null });
|
|
291
|
+
let options = [];
|
|
292
|
+
try {
|
|
293
|
+
options = await runtime.getShopProvider().quoteShipping?.(code, subtotal) ?? [];
|
|
294
|
+
} catch {
|
|
295
|
+
set({
|
|
296
|
+
...EMPTY,
|
|
297
|
+
postalCode: code,
|
|
298
|
+
status: "error",
|
|
299
|
+
error: "N\xE3o foi poss\xEDvel calcular o frete agora. Tente de novo."
|
|
300
|
+
});
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let address = null;
|
|
304
|
+
let notFound = false;
|
|
305
|
+
try {
|
|
306
|
+
address = await core.lookupPostalCode(code);
|
|
307
|
+
notFound = address === null;
|
|
308
|
+
} catch {
|
|
309
|
+
address = null;
|
|
310
|
+
}
|
|
311
|
+
set({
|
|
312
|
+
postalCode: code,
|
|
313
|
+
address,
|
|
314
|
+
options,
|
|
315
|
+
selectedZoneId: options[0]?.zoneId ?? null,
|
|
316
|
+
// A store with no zones configured quotes nothing, and that must NOT
|
|
317
|
+
// read as "we don't deliver here" — it falls back to the store-wide
|
|
318
|
+
// flat rate, which the cart already knows how to show.
|
|
319
|
+
status: options.length > 0 ? "served" : "unserved",
|
|
320
|
+
// Reported alongside the price, not instead of it: the CEP may be
|
|
321
|
+
// mistyped even though its range is covered.
|
|
322
|
+
error: notFound ? "CEP n\xE3o encontrado. Confira os n\xFAmeros." : null
|
|
323
|
+
});
|
|
324
|
+
},
|
|
325
|
+
selectZone(zoneId) {
|
|
326
|
+
if (get().options.some((option) => option.zoneId === zoneId)) set({ selectedZoneId: zoneId });
|
|
327
|
+
},
|
|
328
|
+
clear() {
|
|
329
|
+
set({ ...EMPTY });
|
|
330
|
+
}
|
|
331
|
+
}),
|
|
332
|
+
{
|
|
333
|
+
name: "fayz.storefront.delivery.v1",
|
|
334
|
+
// Deliberately NOT persisting `options`: a rehydrated
|
|
335
|
+
// price would be shown as current without anything having quoted it. The
|
|
336
|
+
// address survives, the money is asked for again.
|
|
337
|
+
partialize: (state) => ({ postalCode: state.postalCode, address: state.address }),
|
|
338
|
+
onRehydrateStorage: () => (state) => {
|
|
339
|
+
if (state?.address) state.status = "idle";
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
)
|
|
343
|
+
);
|
|
344
|
+
function resolveOptionRate(option, subtotal) {
|
|
345
|
+
if (option.freeAbove != null && subtotal >= option.freeAbove) return 0;
|
|
346
|
+
return option.baseRate;
|
|
347
|
+
}
|
|
348
|
+
var selectQuotedShipping = (state, subtotal) => {
|
|
349
|
+
if (state.status !== "served") return null;
|
|
350
|
+
const option = state.options.find((o) => o.zoneId === state.selectedZoneId) ?? state.options[0];
|
|
351
|
+
return option ? resolveOptionRate(option, subtotal) : null;
|
|
352
|
+
};
|
|
240
353
|
|
|
241
354
|
// src/product-options.ts
|
|
242
355
|
function cleanValues(value) {
|
|
@@ -360,6 +473,8 @@ var selectDiscountTotal = (s) => roundCents(selectSubtotal(s) * (s.discountPerce
|
|
|
360
473
|
var selectShipping = (s, cfg) => {
|
|
361
474
|
if (s.lines.length === 0) return 0;
|
|
362
475
|
const subtotal = selectSubtotal(s);
|
|
476
|
+
const quoted = selectQuotedShipping(useDeliveryStore.getState(), subtotal);
|
|
477
|
+
if (quoted != null) return quoted;
|
|
363
478
|
if (cfg.shipping.freeAbove != null && subtotal >= cfg.shipping.freeAbove) return 0;
|
|
364
479
|
return cfg.shipping.flatRate;
|
|
365
480
|
};
|
|
@@ -446,7 +561,7 @@ function googleFontsHref(theme) {
|
|
|
446
561
|
}
|
|
447
562
|
function StorefrontThemeStyle({ theme }) {
|
|
448
563
|
const fontsHref = googleFontsHref(theme);
|
|
449
|
-
|
|
564
|
+
React7.useEffect(() => {
|
|
450
565
|
if (!fontsHref) return;
|
|
451
566
|
const id = "sf-theme-fonts";
|
|
452
567
|
let link = document.getElementById(id);
|
|
@@ -458,7 +573,7 @@ function StorefrontThemeStyle({ theme }) {
|
|
|
458
573
|
}
|
|
459
574
|
link.href = fontsHref;
|
|
460
575
|
}, [fontsHref]);
|
|
461
|
-
return
|
|
576
|
+
return React7__default.default.createElement("style", { "data-sf-theme": theme.name }, themeToCss(theme));
|
|
462
577
|
}
|
|
463
578
|
var initial = {
|
|
464
579
|
search: "",
|
|
@@ -479,9 +594,9 @@ var useCatalogStore = zustand.create()((set) => ({
|
|
|
479
594
|
reset: () => set(initial)
|
|
480
595
|
}));
|
|
481
596
|
function useCategories() {
|
|
482
|
-
const [categories, setCategories] =
|
|
483
|
-
const [loading, setLoading] =
|
|
484
|
-
|
|
597
|
+
const [categories, setCategories] = React7.useState([]);
|
|
598
|
+
const [loading, setLoading] = React7.useState(true);
|
|
599
|
+
React7.useEffect(() => {
|
|
485
600
|
let cancelled = false;
|
|
486
601
|
runtime.getShopProvider().listCategories().then((data) => {
|
|
487
602
|
if (!cancelled) setCategories(data);
|
|
@@ -500,9 +615,9 @@ function prefersReducedMotion() {
|
|
|
500
615
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
501
616
|
}
|
|
502
617
|
function useInView(margin = "0px 0px -8% 0px") {
|
|
503
|
-
const ref =
|
|
504
|
-
const [inView, setInView] =
|
|
505
|
-
|
|
618
|
+
const ref = React7.useRef(null);
|
|
619
|
+
const [inView, setInView] = React7.useState(() => prefersReducedMotion());
|
|
620
|
+
React7.useEffect(() => {
|
|
506
621
|
if (inView) return;
|
|
507
622
|
const el = ref.current;
|
|
508
623
|
if (!el || typeof IntersectionObserver === "undefined") {
|
|
@@ -537,8 +652,8 @@ function Reveal({ delay = 0, children, className, style, ...rest }) {
|
|
|
537
652
|
);
|
|
538
653
|
}
|
|
539
654
|
function useScrolled(threshold = 8) {
|
|
540
|
-
const [scrolled, setScrolled] =
|
|
541
|
-
|
|
655
|
+
const [scrolled, setScrolled] = React7.useState(false);
|
|
656
|
+
React7.useEffect(() => {
|
|
542
657
|
const onScroll = () => setScrolled(window.scrollY > threshold);
|
|
543
658
|
onScroll();
|
|
544
659
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
@@ -547,9 +662,9 @@ function useScrolled(threshold = 8) {
|
|
|
547
662
|
return scrolled;
|
|
548
663
|
}
|
|
549
664
|
function usePopOnChange(value) {
|
|
550
|
-
const [pop, setPop] =
|
|
551
|
-
const prev =
|
|
552
|
-
|
|
665
|
+
const [pop, setPop] = React7.useState(false);
|
|
666
|
+
const prev = React7.useRef(value);
|
|
667
|
+
React7.useEffect(() => {
|
|
553
668
|
if (prev.current !== value && value > 0) {
|
|
554
669
|
setPop(true);
|
|
555
670
|
const t = setTimeout(() => setPop(false), 400);
|
|
@@ -558,7 +673,7 @@ function usePopOnChange(value) {
|
|
|
558
673
|
prev.current = value;
|
|
559
674
|
return void 0;
|
|
560
675
|
}, [value]);
|
|
561
|
-
|
|
676
|
+
React7.useEffect(() => {
|
|
562
677
|
prev.current = value;
|
|
563
678
|
});
|
|
564
679
|
return pop;
|
|
@@ -624,6 +739,9 @@ var TID = {
|
|
|
624
739
|
pdpAddToCart: "pdp-add-to-cart",
|
|
625
740
|
pdpRoot: "pdp-root",
|
|
626
741
|
pdpGallery: "pdp-gallery",
|
|
742
|
+
pdpGalleryImage: "pdp-gallery-image",
|
|
743
|
+
pdpGalleryThumbs: "pdp-gallery-thumbs",
|
|
744
|
+
pdpGalleryThumb: "pdp-gallery-thumb",
|
|
627
745
|
pdpActions: "pdp-actions",
|
|
628
746
|
pdpEnquiryButton: "pdp-enquiry-button",
|
|
629
747
|
enquiryForm: "enquiry-form",
|
|
@@ -652,6 +770,15 @@ var TID = {
|
|
|
652
770
|
cartTotal: "cart-total",
|
|
653
771
|
goCheckout: "go-checkout",
|
|
654
772
|
cartEmpty: "cart-empty",
|
|
773
|
+
// delivery estimate (product page + cart)
|
|
774
|
+
deliveryEstimator: "delivery-estimator",
|
|
775
|
+
deliveryCepInput: "delivery-cep-input",
|
|
776
|
+
deliveryCepSubmit: "delivery-cep-submit",
|
|
777
|
+
deliveryOption: "delivery-option",
|
|
778
|
+
deliveryUnserved: "delivery-unserved",
|
|
779
|
+
deliveryError: "delivery-error",
|
|
780
|
+
deliveryAddress: "delivery-address",
|
|
781
|
+
deliveryClear: "delivery-clear",
|
|
655
782
|
// checkout
|
|
656
783
|
checkoutEmail: "checkout-email",
|
|
657
784
|
checkoutName: "checkout-name",
|
|
@@ -698,7 +825,7 @@ var headerDivider = {
|
|
|
698
825
|
var ANNOUNCEMENT_KEY = "fayz.storefront.announcement.dismissed";
|
|
699
826
|
function AnnouncementBar() {
|
|
700
827
|
const config = useStorefrontConfig();
|
|
701
|
-
const [dismissed, setDismissed] =
|
|
828
|
+
const [dismissed, setDismissed] = React7.useState(() => {
|
|
702
829
|
try {
|
|
703
830
|
return localStorage.getItem(ANNOUNCEMENT_KEY) === config.announcement;
|
|
704
831
|
} catch {
|
|
@@ -762,7 +889,7 @@ function SearchInput({ className, iconOnly = false }) {
|
|
|
762
889
|
const search = useCatalogStore((s) => s.search);
|
|
763
890
|
const setSearch = useCatalogStore((s) => s.setSearch);
|
|
764
891
|
const path = useHashPath();
|
|
765
|
-
const [open, setOpen] =
|
|
892
|
+
const [open, setOpen] = React7.useState(false);
|
|
766
893
|
if (iconOnly && !open) {
|
|
767
894
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
768
895
|
"button",
|
|
@@ -810,7 +937,7 @@ function HeaderActions() {
|
|
|
810
937
|
const count = useCartStore(selectCount);
|
|
811
938
|
const openDrawer = useCartStore((s) => s.openDrawer);
|
|
812
939
|
const session = useSessionStore();
|
|
813
|
-
const [accountOpen, setAccountOpen] =
|
|
940
|
+
const [accountOpen, setAccountOpen] = React7.useState(false);
|
|
814
941
|
const pop = usePopOnChange(count);
|
|
815
942
|
const firstName = session.name?.split(/\s+/)[0];
|
|
816
943
|
const initial2 = (firstName ?? session.email ?? "C").slice(0, 1).toUpperCase();
|
|
@@ -1089,12 +1216,12 @@ var REASON_MESSAGE = {
|
|
|
1089
1216
|
};
|
|
1090
1217
|
function useDiscountValidator() {
|
|
1091
1218
|
const config = useStorefrontConfig();
|
|
1092
|
-
return
|
|
1219
|
+
return React7.useCallback(async (code) => {
|
|
1093
1220
|
const normalized = code.trim().toUpperCase();
|
|
1094
1221
|
if (!normalized) return { valid: false, percent: 0, message: "Informe um cupom." };
|
|
1095
1222
|
const configDiscount = config.discounts?.find((d) => d.code.trim().toUpperCase() === normalized);
|
|
1096
1223
|
if (configDiscount) return { valid: true, percent: configDiscount.percent };
|
|
1097
|
-
const validation = await shop.validateDiscount({ code: normalized });
|
|
1224
|
+
const validation = await shop$1.validateDiscount({ code: normalized });
|
|
1098
1225
|
if (!validation.valid) {
|
|
1099
1226
|
return { valid: false, percent: 0, message: REASON_MESSAGE[validation.reason ?? "not_found"] ?? "Cupom inv\xE1lido ou expirado." };
|
|
1100
1227
|
}
|
|
@@ -1170,6 +1297,113 @@ function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTe
|
|
|
1170
1297
|
)
|
|
1171
1298
|
] });
|
|
1172
1299
|
}
|
|
1300
|
+
function DeliveryEstimator({ compact = false }) {
|
|
1301
|
+
const config = useStorefrontConfig();
|
|
1302
|
+
const cart = useCartStore();
|
|
1303
|
+
const delivery = useDeliveryStore();
|
|
1304
|
+
const [draft, setDraft] = React7.useState(() => core.formatPostalCode(delivery.postalCode));
|
|
1305
|
+
const subtotal = selectSubtotal(cart);
|
|
1306
|
+
const money = (value) => formatMoney(value, config.currency, config.locale);
|
|
1307
|
+
const loading = delivery.status === "loading";
|
|
1308
|
+
async function submit(event) {
|
|
1309
|
+
event.preventDefault();
|
|
1310
|
+
await delivery.resolve(draft, subtotal);
|
|
1311
|
+
}
|
|
1312
|
+
function eta(option) {
|
|
1313
|
+
const { etaMinDays: min, etaMaxDays: max } = option;
|
|
1314
|
+
if (min == null && max == null) return null;
|
|
1315
|
+
if (min === 0 && (max === 0 || max === 1)) return "hoje ou amanh\xE3";
|
|
1316
|
+
if (min != null && max != null && min !== max) return `${min} a ${max} dias \xFAteis`;
|
|
1317
|
+
return `${max ?? min} dia(s) \xFAtil(eis)`;
|
|
1318
|
+
}
|
|
1319
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1320
|
+
"section",
|
|
1321
|
+
{
|
|
1322
|
+
"data-testid": TID.deliveryEstimator,
|
|
1323
|
+
className: compact ? "rounded-lg border p-3" : "mt-6 rounded-lg border p-4",
|
|
1324
|
+
children: [
|
|
1325
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm font-semibold", children: [
|
|
1326
|
+
/* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Truck, { className: "h-4 w-4 text-primary" }),
|
|
1327
|
+
"Calcular entrega"
|
|
1328
|
+
] }),
|
|
1329
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: submit, className: "mt-3 flex gap-2", children: [
|
|
1330
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1331
|
+
"input",
|
|
1332
|
+
{
|
|
1333
|
+
"data-testid": TID.deliveryCepInput,
|
|
1334
|
+
"aria-label": "CEP",
|
|
1335
|
+
inputMode: "numeric",
|
|
1336
|
+
autoComplete: "postal-code",
|
|
1337
|
+
placeholder: "00000-000",
|
|
1338
|
+
value: draft,
|
|
1339
|
+
onChange: (event) => setDraft(core.formatPostalCode(event.target.value)),
|
|
1340
|
+
className: "min-w-0 flex-1 rounded-lg border bg-background px-3 py-2 text-sm outline-none transition focus:border-primary focus:ring-2 focus:ring-primary/10"
|
|
1341
|
+
}
|
|
1342
|
+
),
|
|
1343
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1344
|
+
"button",
|
|
1345
|
+
{
|
|
1346
|
+
type: "submit",
|
|
1347
|
+
"data-testid": TID.deliveryCepSubmit,
|
|
1348
|
+
disabled: loading,
|
|
1349
|
+
className: "shrink-0 rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
|
|
1350
|
+
children: loading ? "..." : "Calcular"
|
|
1351
|
+
}
|
|
1352
|
+
)
|
|
1353
|
+
] }),
|
|
1354
|
+
delivery.error && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.deliveryError, className: "mt-3 text-sm text-destructive", children: delivery.error }),
|
|
1355
|
+
delivery.address && /* @__PURE__ */ jsxRuntime.jsxs("p", { "data-testid": TID.deliveryAddress, className: "mt-3 flex items-start gap-1.5 text-xs text-muted-foreground", children: [
|
|
1356
|
+
/* @__PURE__ */ jsxRuntime.jsx(LucideIcons.MapPin, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
|
|
1357
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1358
|
+
[delivery.address.street, delivery.address.district].filter(Boolean).join(", "),
|
|
1359
|
+
delivery.address.street || delivery.address.district ? " \u2014 " : "",
|
|
1360
|
+
delivery.address.city,
|
|
1361
|
+
"/",
|
|
1362
|
+
delivery.address.state
|
|
1363
|
+
] })
|
|
1364
|
+
] }),
|
|
1365
|
+
delivery.status === "served" && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-3 space-y-2", children: delivery.options.map((option) => {
|
|
1366
|
+
const selected = delivery.selectedZoneId === option.zoneId;
|
|
1367
|
+
const label = eta(option);
|
|
1368
|
+
const rate = resolveOptionRate(option, subtotal);
|
|
1369
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1370
|
+
"button",
|
|
1371
|
+
{
|
|
1372
|
+
type: "button",
|
|
1373
|
+
"data-testid": TID.deliveryOption,
|
|
1374
|
+
"data-zone": option.zoneId,
|
|
1375
|
+
"data-rate": rate.toFixed(2),
|
|
1376
|
+
"aria-pressed": selected,
|
|
1377
|
+
onClick: () => delivery.selectZone(option.zoneId),
|
|
1378
|
+
className: `flex w-full items-center justify-between gap-3 rounded-lg border px-3 py-2 text-left text-sm transition ${selected ? "border-primary bg-primary/5" : "border-border hover:bg-muted/40"}`,
|
|
1379
|
+
children: [
|
|
1380
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0", children: [
|
|
1381
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate font-medium", children: option.carrier ? `${option.carrier} \xB7 ${option.name}` : option.name }),
|
|
1382
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block text-xs text-muted-foreground", children: label })
|
|
1383
|
+
] }),
|
|
1384
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-semibold", children: rate === 0 ? "Gr\xE1tis" : money(rate) })
|
|
1385
|
+
]
|
|
1386
|
+
}
|
|
1387
|
+
) }, option.zoneId);
|
|
1388
|
+
}) }),
|
|
1389
|
+
delivery.status === "unserved" && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.deliveryUnserved, className: "mt-3 text-sm text-destructive", children: "Ainda n\xE3o entregamos nesse CEP." }),
|
|
1390
|
+
delivery.postalCode && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1391
|
+
"button",
|
|
1392
|
+
{
|
|
1393
|
+
type: "button",
|
|
1394
|
+
"data-testid": TID.deliveryClear,
|
|
1395
|
+
onClick: () => {
|
|
1396
|
+
delivery.clear();
|
|
1397
|
+
setDraft("");
|
|
1398
|
+
},
|
|
1399
|
+
className: "mt-3 text-xs text-muted-foreground underline-offset-2 hover:underline",
|
|
1400
|
+
children: "Trocar CEP"
|
|
1401
|
+
}
|
|
1402
|
+
)
|
|
1403
|
+
]
|
|
1404
|
+
}
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1173
1407
|
function SmoothImage({
|
|
1174
1408
|
imageLoading,
|
|
1175
1409
|
loading = "lazy",
|
|
@@ -1187,9 +1421,9 @@ function SmoothImage({
|
|
|
1187
1421
|
const durationMs = resolved.durationMs ?? 420;
|
|
1188
1422
|
const easing = resolved.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)";
|
|
1189
1423
|
const blur = resolved.blur ?? true;
|
|
1190
|
-
const ref =
|
|
1191
|
-
const [loaded, setLoaded] =
|
|
1192
|
-
|
|
1424
|
+
const ref = React7__default.default.useRef(null);
|
|
1425
|
+
const [loaded, setLoaded] = React7__default.default.useState(mode === "none");
|
|
1426
|
+
React7__default.default.useEffect(() => {
|
|
1193
1427
|
if (mode === "none") {
|
|
1194
1428
|
setLoaded(true);
|
|
1195
1429
|
return;
|
|
@@ -1221,10 +1455,10 @@ function CartDrawer() {
|
|
|
1221
1455
|
const config = useStorefrontConfig();
|
|
1222
1456
|
const cart = useCartStore();
|
|
1223
1457
|
const validate = useDiscountValidator();
|
|
1224
|
-
const [code, setCode] =
|
|
1225
|
-
const [discountError, setDiscountError] =
|
|
1226
|
-
const [applying, setApplying] =
|
|
1227
|
-
|
|
1458
|
+
const [code, setCode] = React7.useState("");
|
|
1459
|
+
const [discountError, setDiscountError] = React7.useState(null);
|
|
1460
|
+
const [applying, setApplying] = React7.useState(false);
|
|
1461
|
+
React7.useEffect(() => {
|
|
1228
1462
|
if (!cart.isOpen) return;
|
|
1229
1463
|
const onKey = (e) => {
|
|
1230
1464
|
if (e.key === "Escape") cart.closeDrawer();
|
|
@@ -1232,6 +1466,7 @@ function CartDrawer() {
|
|
|
1232
1466
|
document.addEventListener("keydown", onKey);
|
|
1233
1467
|
return () => document.removeEventListener("keydown", onKey);
|
|
1234
1468
|
}, [cart.isOpen, cart.closeDrawer]);
|
|
1469
|
+
const delivery = useDeliveryStore();
|
|
1235
1470
|
const subtotal = selectSubtotal(cart);
|
|
1236
1471
|
const discountTotal = selectDiscountTotal(cart);
|
|
1237
1472
|
const shipping = selectShipping(cart, config);
|
|
@@ -1388,6 +1623,7 @@ function CartDrawer() {
|
|
|
1388
1623
|
] }),
|
|
1389
1624
|
discountError && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.discountError, className: "text-xs text-destructive", children: discountError })
|
|
1390
1625
|
] }),
|
|
1626
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsxRuntime.jsx(DeliveryEstimator, { compact: true }) }),
|
|
1391
1627
|
/* @__PURE__ */ jsxRuntime.jsxs("dl", { className: "space-y-1.5 text-sm", children: [
|
|
1392
1628
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
|
|
1393
1629
|
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground", children: "Subtotal" }),
|
|
@@ -1417,7 +1653,13 @@ function CartDrawer() {
|
|
|
1417
1653
|
] })
|
|
1418
1654
|
] }),
|
|
1419
1655
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
|
|
1420
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1656
|
+
/* @__PURE__ */ jsxRuntime.jsxs("dt", { className: "text-muted-foreground", children: [
|
|
1657
|
+
"Frete",
|
|
1658
|
+
delivery.status === "served" && delivery.postalCode && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1 text-xs", children: [
|
|
1659
|
+
"\xB7 ",
|
|
1660
|
+
core.formatPostalCode(delivery.postalCode)
|
|
1661
|
+
] })
|
|
1662
|
+
] }),
|
|
1421
1663
|
/* @__PURE__ */ jsxRuntime.jsx("dd", { "data-testid": TID.cartShipping, "data-price": shipping.toFixed(2), children: shipping === 0 ? "Gr\xE1tis" : money(shipping) })
|
|
1422
1664
|
] }),
|
|
1423
1665
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between border-t pt-2 text-base font-semibold", children: [
|
|
@@ -1447,12 +1689,12 @@ function CartDrawer() {
|
|
|
1447
1689
|
] });
|
|
1448
1690
|
}
|
|
1449
1691
|
function useProducts(opts) {
|
|
1450
|
-
const [products, setProducts] =
|
|
1451
|
-
const [loading, setLoading] =
|
|
1452
|
-
const [error, setError] =
|
|
1453
|
-
const [nonce, setNonce] =
|
|
1692
|
+
const [products, setProducts] = React7.useState([]);
|
|
1693
|
+
const [loading, setLoading] = React7.useState(true);
|
|
1694
|
+
const [error, setError] = React7.useState(null);
|
|
1695
|
+
const [nonce, setNonce] = React7.useState(0);
|
|
1454
1696
|
const key = JSON.stringify(opts);
|
|
1455
|
-
|
|
1697
|
+
React7.useEffect(() => {
|
|
1456
1698
|
let cancelled = false;
|
|
1457
1699
|
setLoading(true);
|
|
1458
1700
|
runtime.getShopProvider().listProducts(JSON.parse(key)).then((data) => {
|
|
@@ -1770,10 +2012,10 @@ function FiltersPanel() {
|
|
|
1770
2012
|
}
|
|
1771
2013
|
function useStorefrontHead(input) {
|
|
1772
2014
|
const { title, description } = input;
|
|
1773
|
-
|
|
2015
|
+
React7.useEffect(() => {
|
|
1774
2016
|
if (title) document.title = title;
|
|
1775
2017
|
}, [title]);
|
|
1776
|
-
|
|
2018
|
+
React7.useEffect(() => {
|
|
1777
2019
|
if (!description) return;
|
|
1778
2020
|
let tag = document.head.querySelector('meta[name="description"]');
|
|
1779
2021
|
if (!tag) {
|
|
@@ -1792,9 +2034,9 @@ var SORT_MAP = {
|
|
|
1792
2034
|
};
|
|
1793
2035
|
var SORTS = ["newest", "price-asc", "price-desc", "name"];
|
|
1794
2036
|
function useCatalogUrlSync() {
|
|
1795
|
-
const hydrated =
|
|
2037
|
+
const hydrated = React7.useRef(false);
|
|
1796
2038
|
const catalog = useCatalogStore();
|
|
1797
|
-
|
|
2039
|
+
React7.useEffect(() => {
|
|
1798
2040
|
if (hydrated.current) return;
|
|
1799
2041
|
hydrated.current = true;
|
|
1800
2042
|
const q = window.location.hash.split("?")[1];
|
|
@@ -1809,7 +2051,7 @@ function useCatalogUrlSync() {
|
|
|
1809
2051
|
if (p.has("min")) s.setPriceMin(Number(p.get("min")) || null);
|
|
1810
2052
|
if (p.has("max")) s.setPriceMax(Number(p.get("max")) || null);
|
|
1811
2053
|
}, []);
|
|
1812
|
-
|
|
2054
|
+
React7.useEffect(() => {
|
|
1813
2055
|
const p = new URLSearchParams();
|
|
1814
2056
|
if (catalog.search) p.set("search", catalog.search);
|
|
1815
2057
|
if (catalog.categoryId) p.set("category", catalog.categoryId);
|
|
@@ -1837,7 +2079,7 @@ function CatalogPage() {
|
|
|
1837
2079
|
...SORT_MAP[catalog.sort]
|
|
1838
2080
|
};
|
|
1839
2081
|
const { products, loading, error, reload } = useProducts(providerOpts);
|
|
1840
|
-
const filtered =
|
|
2082
|
+
const filtered = React7.useMemo(
|
|
1841
2083
|
() => products.filter((p) => {
|
|
1842
2084
|
if (catalog.priceMin != null && p.price < catalog.priceMin) return false;
|
|
1843
2085
|
if (catalog.priceMax != null && p.price > catalog.priceMax) return false;
|
|
@@ -1902,17 +2144,17 @@ function HeroSection({
|
|
|
1902
2144
|
slides,
|
|
1903
2145
|
height = "tall"
|
|
1904
2146
|
}) {
|
|
1905
|
-
const [index, setIndex] =
|
|
1906
|
-
const [loadedSlides, setLoadedSlides] =
|
|
2147
|
+
const [index, setIndex] = React7.useState(0);
|
|
2148
|
+
const [loadedSlides, setLoadedSlides] = React7.useState({});
|
|
1907
2149
|
const config = useStorefrontConfig();
|
|
1908
|
-
const interacted =
|
|
2150
|
+
const interacted = React7.useRef(false);
|
|
1909
2151
|
const hClass = height === "tall" ? "h-[72vh] min-h-[440px]" : "h-[48vh] min-h-[340px]";
|
|
1910
2152
|
const revealImages = config.imageLoading.mode !== "none";
|
|
1911
2153
|
const revealStyle = (loaded) => revealImages ? {
|
|
1912
2154
|
filter: config.imageLoading.blur && !loaded ? "blur(10px)" : "blur(0px)",
|
|
1913
2155
|
transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`
|
|
1914
2156
|
} : void 0;
|
|
1915
|
-
|
|
2157
|
+
React7.useEffect(() => {
|
|
1916
2158
|
if (variant !== "slider" || slides.length < 2 || prefersReducedMotion()) return;
|
|
1917
2159
|
const timer = setInterval(() => {
|
|
1918
2160
|
if (!interacted.current) setIndex((i) => (i + 1) % slides.length);
|
|
@@ -2073,7 +2315,7 @@ function ProductRail({
|
|
|
2073
2315
|
const { products, loading } = useProducts(
|
|
2074
2316
|
filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
|
|
2075
2317
|
);
|
|
2076
|
-
const visible =
|
|
2318
|
+
const visible = React7.useMemo(() => {
|
|
2077
2319
|
let list = products;
|
|
2078
2320
|
if (filter === "sale") list = list.filter((p) => p.compareAtPrice != null && p.compareAtPrice > p.price);
|
|
2079
2321
|
return list.slice(0, limit);
|
|
@@ -2122,16 +2364,16 @@ function MediaCarousel({
|
|
|
2122
2364
|
overlay = "dark",
|
|
2123
2365
|
className = ""
|
|
2124
2366
|
}) {
|
|
2125
|
-
const [index, setIndex] =
|
|
2126
|
-
const [loadedItems, setLoadedItems] =
|
|
2367
|
+
const [index, setIndex] = React7.useState(0);
|
|
2368
|
+
const [loadedItems, setLoadedItems] = React7.useState({});
|
|
2127
2369
|
const config = useStorefrontConfig();
|
|
2128
|
-
const interacted =
|
|
2370
|
+
const interacted = React7.useRef(false);
|
|
2129
2371
|
const revealImages = config.imageLoading.mode !== "none";
|
|
2130
2372
|
const revealStyle = (loaded) => revealImages ? {
|
|
2131
2373
|
filter: config.imageLoading.blur && !loaded ? "blur(10px)" : "blur(0px)",
|
|
2132
2374
|
transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`
|
|
2133
2375
|
} : void 0;
|
|
2134
|
-
|
|
2376
|
+
React7.useEffect(() => {
|
|
2135
2377
|
if (items.length < 2 || autoplayMs <= 0 || prefersReducedMotion()) return;
|
|
2136
2378
|
const timer = setInterval(() => {
|
|
2137
2379
|
if (!interacted.current) setIndex((i) => (i + 1) % items.length);
|
|
@@ -2224,13 +2466,13 @@ function ProductSlider({
|
|
|
2224
2466
|
}) {
|
|
2225
2467
|
const config = useStorefrontConfig();
|
|
2226
2468
|
const actions = useStorefrontActions();
|
|
2227
|
-
const viewportRef =
|
|
2469
|
+
const viewportRef = React7.useRef(null);
|
|
2228
2470
|
const components = getStorefrontComponents(config);
|
|
2229
2471
|
const ProductCardComponent = components.ProductCard ?? ProductCard;
|
|
2230
2472
|
const { products, loading } = useProducts(
|
|
2231
2473
|
filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
|
|
2232
2474
|
);
|
|
2233
|
-
const visible =
|
|
2475
|
+
const visible = React7.useMemo(() => {
|
|
2234
2476
|
let list = products;
|
|
2235
2477
|
if (filter === "sale") list = list.filter((p) => p.compareAtPrice != null && p.compareAtPrice > p.price);
|
|
2236
2478
|
if (categoryName) list = list.filter((p) => p.categoryName === categoryName);
|
|
@@ -2378,8 +2620,8 @@ function Testimonials({ title, items }) {
|
|
|
2378
2620
|
] }) });
|
|
2379
2621
|
}
|
|
2380
2622
|
function NewsletterBand({ title, subtitle }) {
|
|
2381
|
-
const [email, setEmail] =
|
|
2382
|
-
const [done, setDone] =
|
|
2623
|
+
const [email, setEmail] = React7.useState("");
|
|
2624
|
+
const [done, setDone] = React7.useState(false);
|
|
2383
2625
|
return /* @__PURE__ */ jsxRuntime.jsx("section", { "data-testid": TID.newsletter, className: "bg-primary py-16 text-primary-foreground", children: /* @__PURE__ */ jsxRuntime.jsxs(Reveal, { className: "mx-auto max-w-xl px-4 text-center sm:px-6", children: [
|
|
2384
2626
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-3xl font-bold", children: title ?? "Receba nossas novidades" }),
|
|
2385
2627
|
subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 opacity-90", children: subtitle }),
|
|
@@ -2451,10 +2693,10 @@ function HomePage({ sections }) {
|
|
|
2451
2693
|
return /* @__PURE__ */ jsxRuntime.jsx(StorefrontSections, { sections });
|
|
2452
2694
|
}
|
|
2453
2695
|
function useProduct(slug2) {
|
|
2454
|
-
const [product, setProduct] =
|
|
2455
|
-
const [loading, setLoading] =
|
|
2456
|
-
const [error, setError] =
|
|
2457
|
-
|
|
2696
|
+
const [product, setProduct] = React7.useState(null);
|
|
2697
|
+
const [loading, setLoading] = React7.useState(true);
|
|
2698
|
+
const [error, setError] = React7.useState(null);
|
|
2699
|
+
React7.useEffect(() => {
|
|
2458
2700
|
let cancelled = false;
|
|
2459
2701
|
setLoading(true);
|
|
2460
2702
|
runtime.getShopProvider().listProducts({ slug: slug2, status: "active", limit: 1 }).then((data) => {
|
|
@@ -2592,13 +2834,13 @@ function buildWhatsAppHref(url, product) {
|
|
|
2592
2834
|
}
|
|
2593
2835
|
function ProductEnquiryForm({ product, onSuccess }) {
|
|
2594
2836
|
const config = useStorefrontConfig();
|
|
2595
|
-
const [customerName, setCustomerName] =
|
|
2596
|
-
const [customerEmail, setCustomerEmail] =
|
|
2597
|
-
const [customerPhone, setCustomerPhone] =
|
|
2598
|
-
const [subject, setSubject] =
|
|
2599
|
-
const [message, setMessage] =
|
|
2600
|
-
const [status, setStatus] =
|
|
2601
|
-
const [error, setError] =
|
|
2837
|
+
const [customerName, setCustomerName] = React7.useState("");
|
|
2838
|
+
const [customerEmail, setCustomerEmail] = React7.useState("");
|
|
2839
|
+
const [customerPhone, setCustomerPhone] = React7.useState("");
|
|
2840
|
+
const [subject, setSubject] = React7.useState(defaultSubject(config.enquiry.subjectPrefix, product));
|
|
2841
|
+
const [message, setMessage] = React7.useState(`I would like more information about ${product.name}.`);
|
|
2842
|
+
const [status, setStatus] = React7.useState("idle");
|
|
2843
|
+
const [error, setError] = React7.useState(null);
|
|
2602
2844
|
const submit = async (event) => {
|
|
2603
2845
|
event.preventDefault();
|
|
2604
2846
|
setStatus("submitting");
|
|
@@ -2760,19 +3002,141 @@ function ProductEnquiryForm({ product, onSuccess }) {
|
|
|
2760
3002
|
}
|
|
2761
3003
|
);
|
|
2762
3004
|
}
|
|
3005
|
+
function ProductGallery({ product, images, primaryImage }) {
|
|
3006
|
+
const ordered = orderImages(images, primaryImage);
|
|
3007
|
+
const [index, setIndex] = React7.useState(0);
|
|
3008
|
+
const [zoom, setZoom] = React7.useState(null);
|
|
3009
|
+
const frameRef = React7.useRef(null);
|
|
3010
|
+
const canHover = useCanHover();
|
|
3011
|
+
React7.useEffect(() => {
|
|
3012
|
+
setIndex(0);
|
|
3013
|
+
setZoom(null);
|
|
3014
|
+
}, [product.id]);
|
|
3015
|
+
const current = ordered[Math.min(index, ordered.length - 1)];
|
|
3016
|
+
function trackPointer(event) {
|
|
3017
|
+
if (!canHover) return;
|
|
3018
|
+
const frame = frameRef.current;
|
|
3019
|
+
if (!frame) return;
|
|
3020
|
+
const rect = frame.getBoundingClientRect();
|
|
3021
|
+
setZoom({
|
|
3022
|
+
x: (event.clientX - rect.left) / rect.width * 100,
|
|
3023
|
+
y: (event.clientY - rect.top) / rect.height * 100
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
function step(delta) {
|
|
3027
|
+
if (ordered.length < 2) return;
|
|
3028
|
+
setIndex((current2) => (current2 + delta + ordered.length) % ordered.length);
|
|
3029
|
+
setZoom(null);
|
|
3030
|
+
}
|
|
3031
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 self-start", children: [
|
|
3032
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3033
|
+
"div",
|
|
3034
|
+
{
|
|
3035
|
+
...storefrontComponentContracts.productDetail.gallery,
|
|
3036
|
+
ref: frameRef,
|
|
3037
|
+
className: "group relative aspect-square w-full overflow-hidden border bg-muted",
|
|
3038
|
+
style: { borderRadius: "var(--sf-radius-card)" },
|
|
3039
|
+
onMouseMove: trackPointer,
|
|
3040
|
+
onMouseLeave: () => setZoom(null),
|
|
3041
|
+
onKeyDown: (event) => {
|
|
3042
|
+
if (event.key === "ArrowRight") step(1);
|
|
3043
|
+
if (event.key === "ArrowLeft") step(-1);
|
|
3044
|
+
},
|
|
3045
|
+
tabIndex: ordered.length > 1 ? 0 : -1,
|
|
3046
|
+
role: ordered.length > 1 ? "group" : void 0,
|
|
3047
|
+
"aria-label": ordered.length > 1 ? `Imagens de ${product.name}` : void 0,
|
|
3048
|
+
children: [
|
|
3049
|
+
current && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3050
|
+
"div",
|
|
3051
|
+
{
|
|
3052
|
+
className: "h-full w-full transition-transform duration-150 ease-out",
|
|
3053
|
+
style: zoom ? { transform: "scale(2)", transformOrigin: `${zoom.x}% ${zoom.y}%` } : { transform: "scale(1)" },
|
|
3054
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3055
|
+
SmoothImage,
|
|
3056
|
+
{
|
|
3057
|
+
src: current.url,
|
|
3058
|
+
alt: current.altText ?? product.name,
|
|
3059
|
+
"data-testid": TID.pdpGalleryImage,
|
|
3060
|
+
className: "h-full w-full object-cover"
|
|
3061
|
+
},
|
|
3062
|
+
current.id
|
|
3063
|
+
)
|
|
3064
|
+
}
|
|
3065
|
+
),
|
|
3066
|
+
ordered.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3067
|
+
/* @__PURE__ */ jsxRuntime.jsx(GalleryArrow, { side: "left", onClick: () => step(-1) }),
|
|
3068
|
+
/* @__PURE__ */ jsxRuntime.jsx(GalleryArrow, { side: "right", onClick: () => step(1) }),
|
|
3069
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pointer-events-none absolute bottom-3 right-3 rounded-full bg-black/60 px-2 py-0.5 text-[11px] font-medium text-white", children: [
|
|
3070
|
+
index + 1,
|
|
3071
|
+
"/",
|
|
3072
|
+
ordered.length
|
|
3073
|
+
] })
|
|
3074
|
+
] })
|
|
3075
|
+
]
|
|
3076
|
+
}
|
|
3077
|
+
),
|
|
3078
|
+
ordered.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("ul", { "data-testid": TID.pdpGalleryThumbs, className: "flex gap-2 overflow-x-auto pb-1", children: ordered.map((image, position) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3079
|
+
"button",
|
|
3080
|
+
{
|
|
3081
|
+
type: "button",
|
|
3082
|
+
"data-testid": TID.pdpGalleryThumb,
|
|
3083
|
+
"data-index": position,
|
|
3084
|
+
"aria-label": `Imagem ${position + 1} de ${ordered.length}`,
|
|
3085
|
+
"aria-current": position === index,
|
|
3086
|
+
onClick: () => {
|
|
3087
|
+
setIndex(position);
|
|
3088
|
+
setZoom(null);
|
|
3089
|
+
},
|
|
3090
|
+
className: `h-16 w-16 shrink-0 overflow-hidden rounded-lg border transition ${position === index ? "border-primary ring-2 ring-primary/20" : "border-border opacity-70 hover:opacity-100"}`,
|
|
3091
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: image.url, alt: "", className: "h-full w-full object-cover", loading: "lazy" })
|
|
3092
|
+
}
|
|
3093
|
+
) }, image.id)) })
|
|
3094
|
+
] });
|
|
3095
|
+
}
|
|
3096
|
+
function orderImages(images, primary) {
|
|
3097
|
+
const all = images.length > 0 ? images : primary ? [primary] : [];
|
|
3098
|
+
return [...all].sort((a, b) => {
|
|
3099
|
+
if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;
|
|
3100
|
+
return (a.sortOrder ?? 0) - (b.sortOrder ?? 0);
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
function useCanHover() {
|
|
3104
|
+
const [canHover, setCanHover] = React7.useState(false);
|
|
3105
|
+
React7.useEffect(() => {
|
|
3106
|
+
const query = window.matchMedia?.("(hover: hover) and (pointer: fine)");
|
|
3107
|
+
if (!query) return;
|
|
3108
|
+
setCanHover(query.matches);
|
|
3109
|
+
const onChange = (event) => setCanHover(event.matches);
|
|
3110
|
+
query.addEventListener?.("change", onChange);
|
|
3111
|
+
return () => query.removeEventListener?.("change", onChange);
|
|
3112
|
+
}, []);
|
|
3113
|
+
return canHover;
|
|
3114
|
+
}
|
|
3115
|
+
function GalleryArrow({ side, onClick }) {
|
|
3116
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3117
|
+
"button",
|
|
3118
|
+
{
|
|
3119
|
+
type: "button",
|
|
3120
|
+
onClick,
|
|
3121
|
+
"aria-label": side === "left" ? "Imagem anterior" : "Pr\xF3xima imagem",
|
|
3122
|
+
className: `absolute top-1/2 z-10 flex h-9 w-9 -translate-y-1/2 items-center justify-center rounded-full bg-background/80 opacity-0 shadow-md backdrop-blur transition hover:bg-background focus:opacity-100 group-hover:opacity-100 ${side === "left" ? "left-3" : "right-3"}`,
|
|
3123
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "text-lg leading-none", children: side === "left" ? "\u2039" : "\u203A" })
|
|
3124
|
+
}
|
|
3125
|
+
);
|
|
3126
|
+
}
|
|
2763
3127
|
function ProductDetailPage({ slug: slug2 }) {
|
|
2764
3128
|
const config = useStorefrontConfig();
|
|
2765
3129
|
const actions = useStorefrontActions();
|
|
2766
3130
|
const { product, loading } = useProduct(slug2);
|
|
2767
3131
|
const addItem = useCartStore((s) => s.addItem);
|
|
2768
|
-
const [qty, setQty] =
|
|
2769
|
-
const [selectedOptions, setSelectedOptions] =
|
|
3132
|
+
const [qty, setQty] = React7.useState(1);
|
|
3133
|
+
const [selectedOptions, setSelectedOptions] = React7.useState({});
|
|
2770
3134
|
const optionGroups = product ? getProductOptionGroups(product) : [];
|
|
2771
3135
|
useStorefrontHead({
|
|
2772
3136
|
title: product ? `${product.name} \u2014 ${config.name}` : config.name,
|
|
2773
3137
|
description: product?.description
|
|
2774
3138
|
});
|
|
2775
|
-
|
|
3139
|
+
React7.useEffect(() => {
|
|
2776
3140
|
if (!product) return;
|
|
2777
3141
|
const defaults = Object.fromEntries(
|
|
2778
3142
|
getProductOptionGroups(product).map((group) => [group.label, group.values[0] ?? ""])
|
|
@@ -2800,6 +3164,7 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2800
3164
|
const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
|
|
2801
3165
|
const components = getStorefrontComponents(config);
|
|
2802
3166
|
const ProductDetailComponent = components.ProductDetail;
|
|
3167
|
+
const GalleryComponent = components.ProductGallery ?? ProductGallery;
|
|
2803
3168
|
const addToCart = () => addItem(product, qty, selectedOptions);
|
|
2804
3169
|
const openEnquiry = () => {
|
|
2805
3170
|
document.getElementById("storefront-product-enquiry")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
@@ -2833,19 +3198,13 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2833
3198
|
] }),
|
|
2834
3199
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid animate-fade-up gap-10 md:grid-cols-2", children: [
|
|
2835
3200
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2836
|
-
|
|
3201
|
+
GalleryComponent,
|
|
2837
3202
|
{
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
{
|
|
2844
|
-
src: image.url,
|
|
2845
|
-
alt: image.altText ?? product.name,
|
|
2846
|
-
className: "aspect-square w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
|
2847
|
-
}
|
|
2848
|
-
)
|
|
3203
|
+
product,
|
|
3204
|
+
images: product.images,
|
|
3205
|
+
primaryImage: image,
|
|
3206
|
+
config,
|
|
3207
|
+
commerceMode: config.commerceMode
|
|
2849
3208
|
}
|
|
2850
3209
|
),
|
|
2851
3210
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col py-2 lg:sticky lg:top-24 lg:self-start", children: [
|
|
@@ -2943,6 +3302,7 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2943
3302
|
children: "Voltar ao cat\xE1logo"
|
|
2944
3303
|
}
|
|
2945
3304
|
) }),
|
|
3305
|
+
config.commerceMode === "checkout" && /* @__PURE__ */ jsxRuntime.jsx(DeliveryEstimator, {}),
|
|
2946
3306
|
config.commerceMode === "checkout" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 grid grid-cols-3 gap-3 border-t pt-6 text-center", children: [
|
|
2947
3307
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
|
|
2948
3308
|
/* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Truck, { className: "h-5 w-5 text-primary" }),
|
|
@@ -2968,9 +3328,10 @@ function ProductDetailPage({ slug: slug2 }) {
|
|
|
2968
3328
|
/* @__PURE__ */ jsxRuntime.jsx(RelatedProducts, { product })
|
|
2969
3329
|
] });
|
|
2970
3330
|
}
|
|
2971
|
-
function formatDeliveryNotes(address) {
|
|
3331
|
+
function formatDeliveryNotes(address, shippingAddress) {
|
|
3332
|
+
if (address?.notes?.trim()) return address.notes.trim();
|
|
3333
|
+
if (shippingAddress) return void 0;
|
|
2972
3334
|
if (!address) return void 0;
|
|
2973
|
-
if (address.notes?.trim()) return address.notes.trim();
|
|
2974
3335
|
const parts = [address.street, address.city, address.zip].map((part) => part?.trim()).filter(Boolean);
|
|
2975
3336
|
return parts.length > 0 ? `Entrega: ${parts.join(", ")}` : void 0;
|
|
2976
3337
|
}
|
|
@@ -2980,6 +3341,8 @@ async function placeStorefrontOrder({
|
|
|
2980
3341
|
session,
|
|
2981
3342
|
customer,
|
|
2982
3343
|
address,
|
|
3344
|
+
shippingAddress,
|
|
3345
|
+
paymentMethod,
|
|
2983
3346
|
markPaid = false
|
|
2984
3347
|
}) {
|
|
2985
3348
|
const email = customer.email.trim().toLowerCase();
|
|
@@ -2994,9 +3357,14 @@ async function placeStorefrontOrder({
|
|
|
2994
3357
|
customerId: customerId ?? void 0,
|
|
2995
3358
|
customer: { name, email },
|
|
2996
3359
|
currency: config.currency,
|
|
2997
|
-
notes: formatDeliveryNotes(address),
|
|
3360
|
+
notes: formatDeliveryNotes(address, shippingAddress),
|
|
2998
3361
|
discountCode: cart.discountCode ?? void 0,
|
|
2999
3362
|
shippingTotal: selectShipping(cart, config),
|
|
3363
|
+
// Structured address + method are what populate public.addresses and the
|
|
3364
|
+
// transactions ledger. `notes` stays for backwards compatibility with
|
|
3365
|
+
// checkouts that have not been updated yet.
|
|
3366
|
+
shippingAddress,
|
|
3367
|
+
paymentMethod,
|
|
3000
3368
|
items: cart.lines.map((line) => ({
|
|
3001
3369
|
productId: line.productId,
|
|
3002
3370
|
quantity: line.quantity,
|
|
@@ -3010,11 +3378,11 @@ async function placeStorefrontOrder({
|
|
|
3010
3378
|
return { order, customerId: customerId ?? order.customerId ?? "" };
|
|
3011
3379
|
}
|
|
3012
3380
|
function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
|
|
3013
|
-
const [email, setEmail] =
|
|
3014
|
-
const [password, setPassword] =
|
|
3015
|
-
const [busy, setBusy] =
|
|
3016
|
-
const [error, setError] =
|
|
3017
|
-
|
|
3381
|
+
const [email, setEmail] = React7.useState(defaultEmail);
|
|
3382
|
+
const [password, setPassword] = React7.useState("");
|
|
3383
|
+
const [busy, setBusy] = React7.useState(false);
|
|
3384
|
+
const [error, setError] = React7.useState(null);
|
|
3385
|
+
React7.useEffect(() => {
|
|
3018
3386
|
const onKey = (event) => {
|
|
3019
3387
|
if (event.key === "Escape") onClose();
|
|
3020
3388
|
};
|
|
@@ -3119,19 +3487,18 @@ function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
|
|
|
3119
3487
|
}
|
|
3120
3488
|
);
|
|
3121
3489
|
}
|
|
3122
|
-
var
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3490
|
+
var PAYMENT_LABELS = {
|
|
3491
|
+
pix: { label: "Pix", hint: "Voc\xEA recebe a chave para pagar ap\xF3s confirmar o pedido" },
|
|
3492
|
+
credit_card: { label: "Cart\xE3o de cr\xE9dito", hint: "Maquininha na entrega" },
|
|
3493
|
+
debit_card: { label: "Cart\xE3o de d\xE9bito", hint: "Maquininha na entrega" },
|
|
3494
|
+
boleto: { label: "Boleto", hint: "Enviado por e-mail ap\xF3s a confirma\xE7\xE3o" },
|
|
3495
|
+
cash: { label: "Dinheiro", hint: "Pagamento na entrega" },
|
|
3496
|
+
other: { label: "Combinar com a loja", hint: "A loja entra em contato para acertar o pagamento" }
|
|
3497
|
+
};
|
|
3128
3498
|
var PROCESSING_STEPS = [
|
|
3129
3499
|
{ icon: LucideIcons.Lock, label: "Validando seus dados..." },
|
|
3130
|
-
{ icon: LucideIcons.
|
|
3131
|
-
{ icon: LucideIcons.PackageCheck, label: "Confirmando seu pedido..." }
|
|
3500
|
+
{ icon: LucideIcons.PackageCheck, label: "Registrando seu pedido..." }
|
|
3132
3501
|
];
|
|
3133
|
-
var DEFAULT_ADDRESS = SAVED_ADDRESSES[0];
|
|
3134
|
-
var DEFAULT_PAYMENT = SAVED_PAYMENT_METHODS[0];
|
|
3135
3502
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3136
3503
|
function ProcessingOverlay({ step }) {
|
|
3137
3504
|
const current = PROCESSING_STEPS[Math.min(step, PROCESSING_STEPS.length - 1)];
|
|
@@ -3174,41 +3541,69 @@ function CheckoutPage() {
|
|
|
3174
3541
|
const config = useStorefrontConfig();
|
|
3175
3542
|
const cart = useCartStore();
|
|
3176
3543
|
const session = useSessionStore();
|
|
3544
|
+
const delivery = useDeliveryStore();
|
|
3177
3545
|
const validateDiscount2 = useDiscountValidator();
|
|
3178
3546
|
useStorefrontHead({ title: `Checkout \u2014 ${config.name}` });
|
|
3179
|
-
const
|
|
3180
|
-
const [
|
|
3181
|
-
const [savedAddresses, setSavedAddresses] =
|
|
3182
|
-
const [
|
|
3183
|
-
const [
|
|
3184
|
-
const [
|
|
3185
|
-
const [form, setForm] = React6.useState({
|
|
3547
|
+
const paymentMethods = config.payments.methods;
|
|
3548
|
+
const [selectedAddressId, setSelectedAddressId] = React7.useState("new");
|
|
3549
|
+
const [savedAddresses, setSavedAddresses] = React7.useState([]);
|
|
3550
|
+
const [addressMode, setAddressMode] = React7.useState("new");
|
|
3551
|
+
const [paymentMethod, setPaymentMethod] = React7.useState(paymentMethods[0] ?? "pix");
|
|
3552
|
+
const [form, setForm] = React7.useState({
|
|
3186
3553
|
email: session.email ?? "",
|
|
3187
3554
|
name: session.name ?? "",
|
|
3188
|
-
street:
|
|
3189
|
-
city:
|
|
3190
|
-
zip:
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3555
|
+
street: "",
|
|
3556
|
+
city: "",
|
|
3557
|
+
zip: "",
|
|
3558
|
+
number: "",
|
|
3559
|
+
complement: "",
|
|
3560
|
+
district: "",
|
|
3561
|
+
state: ""
|
|
3194
3562
|
});
|
|
3195
|
-
const [error, setError] =
|
|
3196
|
-
const [discountCode, setDiscountCode] =
|
|
3197
|
-
const [discountError, setDiscountError] =
|
|
3198
|
-
const [discountSuccess, setDiscountSuccess] =
|
|
3199
|
-
const [applyingDiscount, setApplyingDiscount] =
|
|
3200
|
-
const [placing, setPlacing] =
|
|
3201
|
-
const [processingStep, setProcessingStep] =
|
|
3202
|
-
const [showSignin, setShowSignin] =
|
|
3563
|
+
const [error, setError] = React7.useState(null);
|
|
3564
|
+
const [discountCode, setDiscountCode] = React7.useState("");
|
|
3565
|
+
const [discountError, setDiscountError] = React7.useState(null);
|
|
3566
|
+
const [discountSuccess, setDiscountSuccess] = React7.useState(null);
|
|
3567
|
+
const [applyingDiscount, setApplyingDiscount] = React7.useState(false);
|
|
3568
|
+
const [placing, setPlacing] = React7.useState(false);
|
|
3569
|
+
const [processingStep, setProcessingStep] = React7.useState(0);
|
|
3570
|
+
const [showSignin, setShowSignin] = React7.useState(false);
|
|
3571
|
+
const [zipLookup, setZipLookup] = React7.useState("idle");
|
|
3203
3572
|
const subtotal = selectSubtotal(cart);
|
|
3204
3573
|
const discountTotal = selectDiscountTotal(cart);
|
|
3205
3574
|
const shipping = selectShipping(cart, config);
|
|
3206
3575
|
const total = selectTotal(cart, config);
|
|
3207
3576
|
const money = (value) => formatMoney(value, config.currency, config.locale);
|
|
3208
|
-
|
|
3577
|
+
React7.useEffect(() => {
|
|
3209
3578
|
if (cart.lines.length === 0 && !placing) navigateTo(config.catalogPath);
|
|
3210
3579
|
}, [cart.lines.length, config.catalogPath, placing]);
|
|
3211
3580
|
const set = (key) => (value) => setForm((current) => ({ ...current, [key]: value }));
|
|
3581
|
+
async function setZip(value) {
|
|
3582
|
+
const masked = core.formatPostalCode(value);
|
|
3583
|
+
setForm((current) => ({ ...current, zip: masked }));
|
|
3584
|
+
if (core.normalizePostalCode(masked).length !== 8) {
|
|
3585
|
+
setZipLookup("idle");
|
|
3586
|
+
return;
|
|
3587
|
+
}
|
|
3588
|
+
setZipLookup("loading");
|
|
3589
|
+
try {
|
|
3590
|
+
const found = await core.lookupPostalCode(masked);
|
|
3591
|
+
if (!found) {
|
|
3592
|
+
setZipLookup("not-found");
|
|
3593
|
+
return;
|
|
3594
|
+
}
|
|
3595
|
+
setZipLookup("found");
|
|
3596
|
+
setForm((current) => ({
|
|
3597
|
+
...current,
|
|
3598
|
+
street: found.street || current.street,
|
|
3599
|
+
district: found.district || current.district,
|
|
3600
|
+
city: found.city,
|
|
3601
|
+
state: found.state
|
|
3602
|
+
}));
|
|
3603
|
+
} catch {
|
|
3604
|
+
setZipLookup("idle");
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3212
3607
|
async function applyDiscountCode() {
|
|
3213
3608
|
setApplyingDiscount(true);
|
|
3214
3609
|
setDiscountError(null);
|
|
@@ -3235,10 +3630,54 @@ function CheckoutPage() {
|
|
|
3235
3630
|
setDiscountSuccess(null);
|
|
3236
3631
|
setDiscountError(null);
|
|
3237
3632
|
}
|
|
3633
|
+
React7.useEffect(() => {
|
|
3634
|
+
const customerId = session.customerId;
|
|
3635
|
+
if (!customerId) {
|
|
3636
|
+
setSavedAddresses([]);
|
|
3637
|
+
setAddressMode("new");
|
|
3638
|
+
return;
|
|
3639
|
+
}
|
|
3640
|
+
let cancelled = false;
|
|
3641
|
+
const provider = runtime.getShopProvider();
|
|
3642
|
+
void provider.listCustomerAddresses?.(customerId).then((addresses) => {
|
|
3643
|
+
if (cancelled || addresses.length === 0) return;
|
|
3644
|
+
setSavedAddresses(addresses);
|
|
3645
|
+
applySavedAddress(addresses[0]);
|
|
3646
|
+
}).catch(() => {
|
|
3647
|
+
});
|
|
3648
|
+
return () => {
|
|
3649
|
+
cancelled = true;
|
|
3650
|
+
};
|
|
3651
|
+
}, [session.customerId]);
|
|
3652
|
+
React7.useEffect(() => {
|
|
3653
|
+
if (addressMode !== "new") return;
|
|
3654
|
+
const looked = delivery.address;
|
|
3655
|
+
if (!looked) return;
|
|
3656
|
+
setForm((current) => {
|
|
3657
|
+
if (current.zip.trim() || current.street.trim()) return current;
|
|
3658
|
+
return {
|
|
3659
|
+
...current,
|
|
3660
|
+
zip: core.formatPostalCode(looked.postalCode),
|
|
3661
|
+
street: looked.street,
|
|
3662
|
+
district: looked.district,
|
|
3663
|
+
city: looked.city,
|
|
3664
|
+
state: looked.state
|
|
3665
|
+
};
|
|
3666
|
+
});
|
|
3667
|
+
}, [delivery.address, addressMode]);
|
|
3238
3668
|
function applySavedAddress(address) {
|
|
3239
3669
|
setAddressMode("saved");
|
|
3240
3670
|
setSelectedAddressId(address.id);
|
|
3241
|
-
setForm((current) => ({
|
|
3671
|
+
setForm((current) => ({
|
|
3672
|
+
...current,
|
|
3673
|
+
street: address.street,
|
|
3674
|
+
city: address.city,
|
|
3675
|
+
zip: address.postalCode,
|
|
3676
|
+
number: address.number ?? "",
|
|
3677
|
+
complement: address.complement ?? "",
|
|
3678
|
+
district: address.district ?? "",
|
|
3679
|
+
state: address.state
|
|
3680
|
+
}));
|
|
3242
3681
|
}
|
|
3243
3682
|
function selectAddress(addressId) {
|
|
3244
3683
|
const address = savedAddresses.find((item) => item.id === addressId);
|
|
@@ -3247,37 +3686,7 @@ function CheckoutPage() {
|
|
|
3247
3686
|
function addNewAddress() {
|
|
3248
3687
|
setAddressMode("new");
|
|
3249
3688
|
setSelectedAddressId("new");
|
|
3250
|
-
setForm((current) => ({ ...current, street: "", city: "", zip: "" }));
|
|
3251
|
-
}
|
|
3252
|
-
function removeAddress(addressId) {
|
|
3253
|
-
const nextAddresses = savedAddresses.filter((item) => item.id !== addressId);
|
|
3254
|
-
setSavedAddresses(nextAddresses);
|
|
3255
|
-
if (selectedAddressId !== addressId) return;
|
|
3256
|
-
const fallback = nextAddresses[0];
|
|
3257
|
-
if (fallback) applySavedAddress(fallback);
|
|
3258
|
-
else addNewAddress();
|
|
3259
|
-
}
|
|
3260
|
-
function applySavedPayment(method) {
|
|
3261
|
-
setPaymentMode("saved");
|
|
3262
|
-
setSelectedPaymentId(method.id);
|
|
3263
|
-
setForm((current) => ({ ...current, card: method.card, expiry: method.expiry, cvc: method.cvc }));
|
|
3264
|
-
}
|
|
3265
|
-
function selectPayment(paymentId) {
|
|
3266
|
-
const method = savedPaymentMethods.find((item) => item.id === paymentId);
|
|
3267
|
-
if (method) applySavedPayment(method);
|
|
3268
|
-
}
|
|
3269
|
-
function addNewPayment() {
|
|
3270
|
-
setPaymentMode("new");
|
|
3271
|
-
setSelectedPaymentId("new");
|
|
3272
|
-
setForm((current) => ({ ...current, card: "", expiry: "", cvc: "" }));
|
|
3273
|
-
}
|
|
3274
|
-
function removePayment(paymentId) {
|
|
3275
|
-
const nextMethods = savedPaymentMethods.filter((item) => item.id !== paymentId);
|
|
3276
|
-
setSavedPaymentMethods(nextMethods);
|
|
3277
|
-
if (selectedPaymentId !== paymentId) return;
|
|
3278
|
-
const fallback = nextMethods[0];
|
|
3279
|
-
if (fallback) applySavedPayment(fallback);
|
|
3280
|
-
else addNewPayment();
|
|
3689
|
+
setForm((current) => ({ ...current, street: "", city: "", zip: "", number: "", complement: "", district: "", state: "" }));
|
|
3281
3690
|
}
|
|
3282
3691
|
function validate() {
|
|
3283
3692
|
setError(null);
|
|
@@ -3289,12 +3698,20 @@ function CheckoutPage() {
|
|
|
3289
3698
|
setError("Informe seu nome completo.");
|
|
3290
3699
|
return false;
|
|
3291
3700
|
}
|
|
3292
|
-
if (!form.street.trim() || !form.city.trim() || !form.zip.trim()) {
|
|
3701
|
+
if (!form.street.trim() || !form.city.trim() || !form.zip.trim() || !form.district.trim()) {
|
|
3293
3702
|
setError("Preencha ou selecione o endere\xE7o de entrega.");
|
|
3294
3703
|
return false;
|
|
3295
3704
|
}
|
|
3296
|
-
if (
|
|
3297
|
-
setError("
|
|
3705
|
+
if (!form.number.trim()) {
|
|
3706
|
+
setError("Falta o n\xFAmero do endere\xE7o.");
|
|
3707
|
+
return false;
|
|
3708
|
+
}
|
|
3709
|
+
if (form.state.trim().length !== 2) {
|
|
3710
|
+
setError("Informe a UF com duas letras (ex.: RJ).");
|
|
3711
|
+
return false;
|
|
3712
|
+
}
|
|
3713
|
+
if (delivery.status === "unserved" && delivery.postalCode === core.normalizePostalCode(form.zip)) {
|
|
3714
|
+
setError("Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.");
|
|
3298
3715
|
return false;
|
|
3299
3716
|
}
|
|
3300
3717
|
return true;
|
|
@@ -3316,9 +3733,22 @@ function CheckoutPage() {
|
|
|
3316
3733
|
session,
|
|
3317
3734
|
customer: { email: form.email, name: form.name },
|
|
3318
3735
|
address: { street: form.street, city: form.city, zip: form.zip },
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3736
|
+
shippingAddress: {
|
|
3737
|
+
postalCode: form.zip.trim(),
|
|
3738
|
+
street: form.street.trim(),
|
|
3739
|
+
number: form.number.trim() || void 0,
|
|
3740
|
+
complement: form.complement.trim() || void 0,
|
|
3741
|
+
district: form.district.trim() || void 0,
|
|
3742
|
+
city: form.city.trim(),
|
|
3743
|
+
state: form.state.trim().toUpperCase()
|
|
3744
|
+
},
|
|
3745
|
+
// The method the buyer actually chose, which opens the ledger row in
|
|
3746
|
+
// public.transactions with the right kind.
|
|
3747
|
+
paymentMethod
|
|
3748
|
+
// Never marked paid from the browser. It used to be, through an RPC that
|
|
3749
|
+
// was granted to anon — so any buyer holding their own order id could
|
|
3750
|
+
// declare it settled. The order is now born `pending` and only the
|
|
3751
|
+
// merchant (or a PSP webhook) can confirm the money arrived.
|
|
3322
3752
|
});
|
|
3323
3753
|
await minDuration;
|
|
3324
3754
|
cart.clear();
|
|
@@ -3391,35 +3821,25 @@ function CheckoutPage() {
|
|
|
3391
3821
|
/* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
|
|
3392
3822
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Endere\xE7o de entrega" }),
|
|
3393
3823
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 grid gap-3", children: [
|
|
3394
|
-
savedAddresses.map((address) => /* @__PURE__ */ jsxRuntime.
|
|
3824
|
+
savedAddresses.map((address) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3395
3825
|
"div",
|
|
3396
3826
|
{
|
|
3397
3827
|
className: `group relative rounded-lg border p-3 pr-11 text-left text-sm transition hover:bg-muted/40 ${selectedAddressId === address.id ? "border-primary bg-primary/5" : "border-border bg-background"}`,
|
|
3398
|
-
children: [
|
|
3399
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
addressMode === "saved" && selectedAddressId === address.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
|
|
3403
|
-
] }),
|
|
3404
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-muted-foreground", children: address.street }),
|
|
3405
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "block text-muted-foreground", children: [
|
|
3406
|
-
address.city,
|
|
3407
|
-
" - ",
|
|
3408
|
-
address.zip
|
|
3409
|
-
] })
|
|
3828
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => selectAddress(address.id), className: "block w-full text-left", children: [
|
|
3829
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
|
|
3830
|
+
address.label,
|
|
3831
|
+
addressMode === "saved" && selectedAddressId === address.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
|
|
3410
3832
|
] }),
|
|
3411
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3412
|
-
"
|
|
3413
|
-
{
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
)
|
|
3422
|
-
]
|
|
3833
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-1 block text-muted-foreground", children: [
|
|
3834
|
+
[address.street, address.number].filter(Boolean).join(", "),
|
|
3835
|
+
address.complement ? ` \u2014 ${address.complement}` : ""
|
|
3836
|
+
] }),
|
|
3837
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "block text-muted-foreground", children: [
|
|
3838
|
+
[address.district, address.city, address.state].filter(Boolean).join(" \xB7 "),
|
|
3839
|
+
" \u2014 ",
|
|
3840
|
+
address.postalCode
|
|
3841
|
+
] })
|
|
3842
|
+
] })
|
|
3423
3843
|
},
|
|
3424
3844
|
address.id
|
|
3425
3845
|
)),
|
|
@@ -3430,83 +3850,62 @@ function CheckoutPage() {
|
|
|
3430
3850
|
onClick: addNewAddress,
|
|
3431
3851
|
className: `rounded-lg border border-dashed p-3 text-left text-sm transition hover:bg-muted/40 ${addressMode === "new" ? "border-primary bg-primary/5" : "border-border bg-muted/20"}`,
|
|
3432
3852
|
children: [
|
|
3433
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "block font-semibold text-primary", children: "+ Adicionar novo endere\xE7o" }),
|
|
3853
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "block font-semibold text-primary", children: savedAddresses.length > 0 ? "+ Adicionar novo endere\xE7o" : "Informe o endere\xE7o de entrega" }),
|
|
3434
3854
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-muted-foreground", children: "Preencher outro endere\xE7o para esta compra" })
|
|
3435
3855
|
]
|
|
3436
3856
|
}
|
|
3437
3857
|
)
|
|
3438
3858
|
] }),
|
|
3439
3859
|
addressMode === "new" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3", children: [
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3860
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-1 sm:grid-cols-[200px_1fr] sm:items-center", children: [
|
|
3861
|
+
field("CEP", TID.checkoutZip, form.zip, setZip, {
|
|
3862
|
+
placeholder: "CEP",
|
|
3863
|
+
inputMode: "numeric",
|
|
3864
|
+
autoComplete: "postal-code"
|
|
3865
|
+
}),
|
|
3866
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-1 text-xs text-muted-foreground", children: zipLookup === "loading" ? "Buscando endere\xE7o\u2026" : zipLookup === "not-found" ? "CEP n\xE3o encontrado \u2014 preencha o endere\xE7o \xE0 m\xE3o." : "Preenchemos o endere\xE7o para voc\xEA." })
|
|
3867
|
+
] }),
|
|
3868
|
+
field("Endere\xE7o", TID.checkoutStreet, form.street, set("street"), { placeholder: "Rua, avenida\u2026" }),
|
|
3869
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
3870
|
+
field("N\xFAmero", "checkout-number", form.number, set("number"), { placeholder: "N\xFAmero" }),
|
|
3871
|
+
field("Complemento", "checkout-complement", form.complement, set("complement"), { placeholder: "Apto, bloco (opcional)" })
|
|
3872
|
+
] }),
|
|
3873
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
3874
|
+
field("Bairro", "checkout-district", form.district, set("district"), { placeholder: "Bairro" }),
|
|
3875
|
+
field("UF", "checkout-state", form.state, set("state"), { placeholder: "UF" })
|
|
3876
|
+
] }),
|
|
3877
|
+
field("Cidade", TID.checkoutCity, form.city, set("city"), { placeholder: "Cidade" })
|
|
3445
3878
|
] })
|
|
3446
3879
|
] }),
|
|
3447
3880
|
/* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
|
|
3448
3881
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Pagamento" }),
|
|
3449
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
className: `group relative rounded-lg border p-3 pr-11 text-left text-sm transition hover:bg-muted/40 ${selectedPaymentId === method.id ? "border-primary bg-primary/5" : "border-border bg-background"}`,
|
|
3454
|
-
children: [
|
|
3455
|
-
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => selectPayment(method.id), className: "block w-full text-left", children: [
|
|
3456
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
|
|
3457
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
|
|
3458
|
-
/* @__PURE__ */ jsxRuntime.jsx(LucideIcons.CreditCard, { className: "h-4 w-4" }),
|
|
3459
|
-
method.label
|
|
3460
|
-
] }),
|
|
3461
|
-
paymentMode === "saved" && selectedPaymentId === method.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
|
|
3462
|
-
] }),
|
|
3463
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-1 block text-muted-foreground", children: [
|
|
3464
|
-
"Expira em ",
|
|
3465
|
-
method.expiry
|
|
3466
|
-
] })
|
|
3467
|
-
] }),
|
|
3468
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3469
|
-
"button",
|
|
3470
|
-
{
|
|
3471
|
-
type: "button",
|
|
3472
|
-
onClick: () => removePayment(method.id),
|
|
3473
|
-
className: "absolute right-3 top-3 inline-flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground opacity-0 transition hover:bg-destructive/10 hover:text-destructive focus:opacity-100 group-hover:opacity-100",
|
|
3474
|
-
"aria-label": `Remover cart\xE3o ${method.label}`,
|
|
3475
|
-
title: "Remover cart\xE3o",
|
|
3476
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Trash2, { className: "h-4 w-4" })
|
|
3477
|
-
}
|
|
3478
|
-
)
|
|
3479
|
-
]
|
|
3480
|
-
},
|
|
3481
|
-
method.id
|
|
3482
|
-
)),
|
|
3483
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3882
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3 grid gap-3", role: "radiogroup", "aria-label": "Forma de pagamento", children: paymentMethods.map((method) => {
|
|
3883
|
+
const copy = PAYMENT_LABELS[method];
|
|
3884
|
+
const selected = paymentMethod === method;
|
|
3885
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3484
3886
|
"button",
|
|
3485
3887
|
{
|
|
3486
3888
|
type: "button",
|
|
3487
|
-
|
|
3488
|
-
|
|
3889
|
+
role: "radio",
|
|
3890
|
+
"aria-checked": selected,
|
|
3891
|
+
"data-testid": `checkout-payment-${method}`,
|
|
3892
|
+
onClick: () => setPaymentMethod(method),
|
|
3893
|
+
className: `rounded-lg border p-3 text-left text-sm transition hover:bg-muted/40 ${selected ? "border-primary bg-primary/5" : "border-border bg-background"}`,
|
|
3489
3894
|
children: [
|
|
3490
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-
|
|
3491
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3492
|
-
|
|
3895
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
|
|
3896
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
|
|
3897
|
+
/* @__PURE__ */ jsxRuntime.jsx(LucideIcons.CreditCard, { className: "h-4 w-4" }),
|
|
3898
|
+
copy.label
|
|
3899
|
+
] }),
|
|
3900
|
+
selected && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
|
|
3493
3901
|
] }),
|
|
3494
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-muted-foreground", children:
|
|
3902
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-muted-foreground", children: copy.hint })
|
|
3495
3903
|
]
|
|
3496
|
-
}
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
placeholder: "N\xFAmero do cart\xE3o",
|
|
3502
|
-
inputMode: "numeric"
|
|
3503
|
-
}),
|
|
3504
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
3505
|
-
field("Validade", TID.checkoutExpiry, form.expiry, set("expiry"), { placeholder: "MM/AA" }),
|
|
3506
|
-
field("CVC", TID.checkoutCvc, form.cvc, set("cvc"), { placeholder: "CVC", inputMode: "numeric" })
|
|
3507
|
-
] })
|
|
3508
|
-
] }),
|
|
3509
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: "Pagamento de demonstra\xE7\xE3o - nenhuma cobran\xE7a \xE9 feita." })
|
|
3904
|
+
},
|
|
3905
|
+
method
|
|
3906
|
+
);
|
|
3907
|
+
}) }),
|
|
3908
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: "Nenhuma cobran\xE7a \xE9 feita agora. Seu pedido \xE9 registrado e a loja confirma o pagamento." })
|
|
3510
3909
|
] }),
|
|
3511
3910
|
error && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.checkoutError, className: "rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive", children: error })
|
|
3512
3911
|
] }),
|
|
@@ -3860,11 +4259,11 @@ function SuccessMark() {
|
|
|
3860
4259
|
}
|
|
3861
4260
|
function OrderConfirmationPage({ orderId }) {
|
|
3862
4261
|
const config = useStorefrontConfig();
|
|
3863
|
-
const [order, setOrder] =
|
|
3864
|
-
const [loading, setLoading] =
|
|
4262
|
+
const [order, setOrder] = React7.useState(null);
|
|
4263
|
+
const [loading, setLoading] = React7.useState(true);
|
|
3865
4264
|
const money = (v) => formatMoney(v, config.currency, config.locale);
|
|
3866
4265
|
useStorefrontHead({ title: order ? `Pedido #${order.orderNumber} \u2014 ${config.name}` : config.name });
|
|
3867
|
-
|
|
4266
|
+
React7.useEffect(() => {
|
|
3868
4267
|
let cancelled = false;
|
|
3869
4268
|
runtime.getShopProvider().getOrder(orderId).then((o) => {
|
|
3870
4269
|
if (!cancelled) setOrder(o);
|
|
@@ -3996,11 +4395,11 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
3996
4395
|
function useMyOrders() {
|
|
3997
4396
|
const customerId = useSessionStore((s) => s.customerId);
|
|
3998
4397
|
const email = useSessionStore((s) => s.email);
|
|
3999
|
-
const [orders, setOrders] =
|
|
4000
|
-
const [loading, setLoading] =
|
|
4001
|
-
const [tick, setTick] =
|
|
4002
|
-
const refresh =
|
|
4003
|
-
|
|
4398
|
+
const [orders, setOrders] = React7.useState([]);
|
|
4399
|
+
const [loading, setLoading] = React7.useState(true);
|
|
4400
|
+
const [tick, setTick] = React7.useState(0);
|
|
4401
|
+
const refresh = React7.useCallback(() => setTick((t) => t + 1), []);
|
|
4402
|
+
React7.useEffect(() => {
|
|
4004
4403
|
let cancelled = false;
|
|
4005
4404
|
if (!customerId && !email) {
|
|
4006
4405
|
setOrders([]);
|
|
@@ -4022,12 +4421,12 @@ function useMyOrders() {
|
|
|
4022
4421
|
return { orders, loading, refresh };
|
|
4023
4422
|
}
|
|
4024
4423
|
function AuthForm() {
|
|
4025
|
-
const [mode, setMode] =
|
|
4026
|
-
const [email, setEmail] =
|
|
4027
|
-
const [password, setPassword] =
|
|
4028
|
-
const [name, setName] =
|
|
4029
|
-
const [busy, setBusy] =
|
|
4030
|
-
const [error, setError] =
|
|
4424
|
+
const [mode, setMode] = React7.useState("signin");
|
|
4425
|
+
const [email, setEmail] = React7.useState("");
|
|
4426
|
+
const [password, setPassword] = React7.useState("");
|
|
4427
|
+
const [name, setName] = React7.useState("");
|
|
4428
|
+
const [busy, setBusy] = React7.useState(false);
|
|
4429
|
+
const [error, setError] = React7.useState(null);
|
|
4031
4430
|
async function submit(e) {
|
|
4032
4431
|
e.preventDefault();
|
|
4033
4432
|
setError(null);
|
|
@@ -4169,9 +4568,9 @@ function OrdersPanel() {
|
|
|
4169
4568
|
}
|
|
4170
4569
|
function ProfilePanel() {
|
|
4171
4570
|
const session = useSessionStore();
|
|
4172
|
-
const [name, setName] =
|
|
4173
|
-
const [saving, setSaving] =
|
|
4174
|
-
const [saved, setSaved] =
|
|
4571
|
+
const [name, setName] = React7.useState(session.name ?? "");
|
|
4572
|
+
const [saving, setSaving] = React7.useState(false);
|
|
4573
|
+
const [saved, setSaved] = React7.useState(false);
|
|
4175
4574
|
async function save(e) {
|
|
4176
4575
|
e.preventDefault();
|
|
4177
4576
|
const trimmed = name.trim();
|
|
@@ -4267,7 +4666,7 @@ var SECTION_SUBTITLE = {
|
|
|
4267
4666
|
function MyPurchasesPage() {
|
|
4268
4667
|
const config = useStorefrontConfig();
|
|
4269
4668
|
const session = useSessionStore();
|
|
4270
|
-
const [section, setSection] =
|
|
4669
|
+
const [section, setSection] = React7.useState("orders");
|
|
4271
4670
|
useStorefrontHead({ title: `Minha conta \u2014 ${config.name}` });
|
|
4272
4671
|
if (!session.email) {
|
|
4273
4672
|
return /* @__PURE__ */ jsxRuntime.jsx("main", { className: "mx-auto max-w-3xl px-4 py-12 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsx(AuthForm, {}) });
|
|
@@ -4330,7 +4729,7 @@ function PolicyPage({ kind }) {
|
|
|
4330
4729
|
] }) })
|
|
4331
4730
|
] });
|
|
4332
4731
|
}
|
|
4333
|
-
var StorefrontErrorBoundary = class extends
|
|
4732
|
+
var StorefrontErrorBoundary = class extends React7__default.default.Component {
|
|
4334
4733
|
constructor() {
|
|
4335
4734
|
super(...arguments);
|
|
4336
4735
|
this.state = { error: null };
|
|
@@ -4374,7 +4773,7 @@ var ACCENT = {
|
|
|
4374
4773
|
function ToastCard({ toast: toast2 }) {
|
|
4375
4774
|
const dismiss = useToastStore((s) => s.dismiss);
|
|
4376
4775
|
const Icon2 = ICON[toast2.variant];
|
|
4377
|
-
|
|
4776
|
+
React7.useEffect(() => {
|
|
4378
4777
|
const timer = window.setTimeout(() => dismiss(toast2.id), toast2.durationMs);
|
|
4379
4778
|
return () => window.clearTimeout(timer);
|
|
4380
4779
|
}, [toast2.id, toast2.durationMs, dismiss]);
|
|
@@ -4625,9 +5024,9 @@ function manifestToStorefrontConfig(manifest, surfaceName) {
|
|
|
4625
5024
|
};
|
|
4626
5025
|
}
|
|
4627
5026
|
function StorefrontScaffold({ manifest, surface }) {
|
|
4628
|
-
const config =
|
|
4629
|
-
const resolved =
|
|
4630
|
-
const inited =
|
|
5027
|
+
const config = React7__default.default.useMemo(() => manifestToStorefrontConfig(manifest, surface), [manifest, surface]);
|
|
5028
|
+
const resolved = React7__default.default.useMemo(() => resolveConfig(config), [config]);
|
|
5029
|
+
const inited = React7__default.default.useRef(false);
|
|
4631
5030
|
if (!inited.current) {
|
|
4632
5031
|
initStorefrontRuntime(config);
|
|
4633
5032
|
inited.current = true;
|