@fayz-ai/storefront 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/createStorefrontApp.d.ts.map +1 -1
- package/dist/app/router.d.ts +1 -1
- package/dist/app/scaffold.d.ts.map +1 -1
- package/dist/{chunk-6B7H3IZ6.js → chunk-SDQHSUFC.js} +1398 -585
- package/dist/chunk-SDQHSUFC.js.map +1 -0
- package/dist/components/Skeleton.d.ts +17 -0
- package/dist/components/Skeleton.d.ts.map +1 -0
- package/dist/components/account/CustomerAccountShell.d.ts +7 -3
- package/dist/components/account/CustomerAccountShell.d.ts.map +1 -1
- package/dist/components/account/section.d.ts +15 -0
- package/dist/components/account/section.d.ts.map +1 -0
- package/dist/components/cart/CartDrawer.d.ts.map +1 -1
- package/dist/components/cart/QuantityInput.d.ts.map +1 -1
- package/dist/components/checkout/CheckoutPayment.d.ts +17 -0
- package/dist/components/checkout/CheckoutPayment.d.ts.map +1 -0
- package/dist/components/checkout/CheckoutSteps.d.ts +9 -0
- package/dist/components/checkout/CheckoutSteps.d.ts.map +1 -0
- package/dist/components/layout/StorefrontHeader.d.ts.map +1 -1
- package/dist/components/order/OrderTrackingTimeline.d.ts.map +1 -1
- package/dist/components/order/SuccessMark.d.ts +10 -0
- package/dist/components/order/SuccessMark.d.ts.map +1 -0
- package/dist/data/loaders.d.ts +0 -4
- package/dist/data/loaders.d.ts.map +1 -1
- package/dist/data/local-orders.d.ts +5 -0
- package/dist/data/local-orders.d.ts.map +1 -0
- package/dist/data/shipping-items.d.ts +20 -0
- package/dist/data/shipping-items.d.ts.map +1 -0
- package/dist/document/config.d.ts +21 -3
- package/dist/document/config.d.ts.map +1 -1
- package/dist/document/sections.d.ts +1 -1
- package/dist/document/sections.d.ts.map +1 -1
- package/dist/document/store-policy.d.ts +29 -0
- package/dist/document/store-policy.d.ts.map +1 -0
- package/dist/hooks/use-my-orders.d.ts.map +1 -1
- package/dist/hooks/use-shipping.d.ts +3 -0
- package/dist/hooks/use-shipping.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/errors.d.ts +9 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/motion.d.ts.map +1 -1
- package/dist/lib/testids.d.ts +12 -0
- package/dist/lib/testids.d.ts.map +1 -1
- package/dist/render.js +1 -1
- package/dist/stores/cart.store.d.ts +28 -0
- package/dist/stores/cart.store.d.ts.map +1 -1
- package/dist/stores/delivery.store.d.ts +9 -0
- package/dist/stores/delivery.store.d.ts.map +1 -1
- package/dist/views/CheckoutPage.d.ts.map +1 -1
- package/dist/views/MyPurchasesPage.d.ts.map +1 -1
- package/dist/views/OrderConfirmationPage.d.ts.map +1 -1
- package/dist/workflows/checkout.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/chunk-6B7H3IZ6.js.map +0 -1
|
@@ -2,13 +2,13 @@ import { create } from 'zustand';
|
|
|
2
2
|
import { persist } from 'zustand/middleware';
|
|
3
3
|
import { createAuthRuntime, createMockAuthAdapter } from '@fayz-ai/plugin-auth';
|
|
4
4
|
import { getShopProvider, setShopProvider } from '@fayz-ai/shop/runtime';
|
|
5
|
-
import { setShopAccessTokenResolver } from '@fayz-ai/sdk/shop';
|
|
6
|
-
import { normalizePostalCode, lookupPostalCode, defaultRouterAdapter, defineLoader,
|
|
5
|
+
import { setShopCarrierQuotesResolver, setShopAccessTokenResolver, setShopShippingItemsResolver } from '@fayz-ai/sdk/shop';
|
|
6
|
+
import { normalizePostalCode, lookupPostalCode, defaultRouterAdapter, defineLoader, staticRouterAdapter, isLoaderParamRef, isLoaderPropRef, resolveLoader, dataNeedKey, getBlockMeta, defaultPropsFromSettings, getBlockErrorVisibility, isBlockEnabledOn, checkBlockConstraints, checkBlockContext, repairBlockTree, renderBlocks, clearExtensionPoints, defineExtensionPoint, runExtensionPoint, runEscapeValve, breadcrumbJsonLd, hasEntityDeclaration, resolveEntityDeclaration, defineBlock, formatPostalCode, defineEntity, listEntityDeclarations, resolveHandle, seoOrigin, RESERVED_ENTITY_FIELDS, ENTITY_FIELD_TYPES, handleNameFor, registerHandle, hasHandle, listHandles, blockRegistry, loaderRegistry, listLoaders } from '@fayz-ai/core';
|
|
7
7
|
import React4, { createContext, useContext, useEffect, useRef, useState, useSyncExternalStore, useMemo, useCallback } from 'react';
|
|
8
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
-
import { ChevronLeft, ChevronRight, ShoppingBag, X, Minus, Plus, Send, MessageCircle, Mail, Truck, RefreshCcw, ShieldCheck, ChevronDown, Check, Search, Trash2, Lock, CreditCard, Star, MapPin, Phone, User, UserCircle, Package, LogOut, Menu,
|
|
9
|
+
import { ChevronLeft, ChevronRight, ShoppingBag, X, Minus, Plus, Send, MessageCircle, Mail, Truck, RefreshCcw, ShieldCheck, ChevronDown, Check, Search, Trash2, Lock, CreditCard, Star, MapPin, Phone, User, UserCircle, Package, LogOut, Menu, QrCode, Copy, XCircle, RotateCcw, Clock, PackageCheck, Info, AlertCircle } from 'lucide-react';
|
|
10
10
|
import { getLucideIcon } from '@fayz-ai/ui/icons';
|
|
11
|
-
import { validateDiscount } from '@fayz-ai/shop';
|
|
11
|
+
import { validateDiscount, documentDigits, isChargeableProvider, formatDocument, isValidDocument } from '@fayz-ai/shop';
|
|
12
12
|
import { storefrontDiscountToPromotion, previewCouponPercent, discountToPromotion, PROMOTION_TYPES, PROMOTION_STATUSES, APPLICATION_METHOD_TYPES, APPLICATION_METHOD_TARGET_TYPES, APPLICATION_METHOD_ALLOCATIONS, hasRuleAttribute, listRuleAttributeKeys, RULE_OPERATORS, resolveRuleAttribute } from '@fayz-ai/shop/rules';
|
|
13
13
|
import { createMockShopProvider } from '@fayz-ai/shop/mock';
|
|
14
14
|
|
|
@@ -156,6 +156,274 @@ async function signOutCustomer() {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
// src/lib/format.ts
|
|
160
|
+
function formatMoney(value, currency = "BRL", locale = "pt-BR") {
|
|
161
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(value);
|
|
162
|
+
}
|
|
163
|
+
function roundCents(value) {
|
|
164
|
+
return Math.round(value * 100) / 100;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/data/product-options.ts
|
|
168
|
+
function cleanValues(value) {
|
|
169
|
+
if (!Array.isArray(value)) return [];
|
|
170
|
+
return Array.from(new Set(value.filter((item) => typeof item === "string" && item.trim().length > 0)));
|
|
171
|
+
}
|
|
172
|
+
function cleanOptionGroup(value) {
|
|
173
|
+
if (!value || typeof value !== "object") return null;
|
|
174
|
+
const record = value;
|
|
175
|
+
const id = typeof record.id === "string" ? record.id : typeof record.name === "string" ? record.name : null;
|
|
176
|
+
const label = typeof record.label === "string" ? record.label : typeof record.name === "string" ? record.name : id;
|
|
177
|
+
const values = cleanValues(record.values);
|
|
178
|
+
if (!id || !label || values.length === 0) return null;
|
|
179
|
+
return { id, label, values };
|
|
180
|
+
}
|
|
181
|
+
var warnedOverlayProducts = /* @__PURE__ */ new Set();
|
|
182
|
+
function resetProductOptionsDeprecationWarnings() {
|
|
183
|
+
warnedOverlayProducts.clear();
|
|
184
|
+
}
|
|
185
|
+
function getProductOptionGroups(product) {
|
|
186
|
+
if (product.options && product.options.length > 0) {
|
|
187
|
+
return [...product.options].sort((a, b) => a.position - b.position).map((option) => ({ id: option.name, label: option.name, values: [...option.values] }));
|
|
188
|
+
}
|
|
189
|
+
const options = product.metadata.options;
|
|
190
|
+
const explicit = Array.isArray(options) ? options.map(cleanOptionGroup).filter((group) => Boolean(group)) : [];
|
|
191
|
+
const groups = [...explicit];
|
|
192
|
+
if (groups.length === 0) {
|
|
193
|
+
const sizes = cleanValues(product.metadata.sizes);
|
|
194
|
+
const colors = cleanValues(product.metadata.colors);
|
|
195
|
+
if (sizes.length > 0) groups.push({ id: "Tamanho", label: "Tamanho", values: sizes });
|
|
196
|
+
if (colors.length > 0) groups.push({ id: "Cor", label: "Cor", values: colors });
|
|
197
|
+
}
|
|
198
|
+
if (groups.length > 0 && !warnedOverlayProducts.has(product.id)) {
|
|
199
|
+
warnedOverlayProducts.add(product.id);
|
|
200
|
+
console.warn(
|
|
201
|
+
`[storefront] product "${product.slug}" renders options from the DEPRECATED metadata overlay (metadata.options/sizes/colors). These labels are display-only \u2014 checkout cannot price or reserve them. Define real variants (0028) instead; the overlay path is removed at M9.`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return groups;
|
|
205
|
+
}
|
|
206
|
+
function resolveVariantForSelection(product, selection) {
|
|
207
|
+
const variants = product.variants;
|
|
208
|
+
if (!variants || variants.length === 0) return null;
|
|
209
|
+
const normalized = normalizeProductOptionSelection(selection);
|
|
210
|
+
const wanted = Object.entries(normalized);
|
|
211
|
+
return variants.find((variant) => {
|
|
212
|
+
const options = variant.options;
|
|
213
|
+
return Object.keys(options).length === wanted.length && wanted.every(([key, value]) => options[key] === value);
|
|
214
|
+
}) ?? null;
|
|
215
|
+
}
|
|
216
|
+
function selectionPrice(product, selection) {
|
|
217
|
+
const variant = resolveVariantForSelection(product, selection);
|
|
218
|
+
return variant?.price ?? product.price;
|
|
219
|
+
}
|
|
220
|
+
function normalizeProductOptionSelection(selection) {
|
|
221
|
+
if (!selection) return {};
|
|
222
|
+
return Object.fromEntries(
|
|
223
|
+
Object.entries(selection).filter((entry) => entry[0].trim().length > 0 && entry[1].trim().length > 0).sort(([a], [b]) => a.localeCompare(b))
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
function formatProductOptionSelection(selection) {
|
|
227
|
+
return Object.entries(normalizeProductOptionSelection(selection)).map(([label, value]) => `${label}: ${value}`).join(" \xB7 ");
|
|
228
|
+
}
|
|
229
|
+
function productOptionSelectionKey(selection) {
|
|
230
|
+
const normalized = normalizeProductOptionSelection(selection);
|
|
231
|
+
const entries = Object.entries(normalized);
|
|
232
|
+
return entries.length > 0 ? JSON.stringify(entries) : "";
|
|
233
|
+
}
|
|
234
|
+
var EMPTY = {
|
|
235
|
+
postalCode: "",
|
|
236
|
+
address: null,
|
|
237
|
+
options: [],
|
|
238
|
+
selectedZoneId: null,
|
|
239
|
+
status: "idle",
|
|
240
|
+
error: null,
|
|
241
|
+
refusedZone: null
|
|
242
|
+
};
|
|
243
|
+
var useDeliveryStore = create()(
|
|
244
|
+
persist(
|
|
245
|
+
(set, get) => ({
|
|
246
|
+
...EMPTY,
|
|
247
|
+
async resolve(postalCode, subtotal) {
|
|
248
|
+
const code = normalizePostalCode(postalCode);
|
|
249
|
+
if (code.length !== 8) {
|
|
250
|
+
set({ ...EMPTY, postalCode, status: "error", error: "Digite os 8 d\xEDgitos do CEP." });
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
set({ postalCode: code, status: "loading", error: null });
|
|
254
|
+
const shop = getShopProvider();
|
|
255
|
+
const refusalPromise = shop.checkDelivery ? shop.checkDelivery(code).then((check) => check.refused ? check.refusedZone || "Regi\xE3o n\xE3o atendida" : null).catch(() => null) : Promise.resolve(null);
|
|
256
|
+
let options = [];
|
|
257
|
+
try {
|
|
258
|
+
options = await shop.quoteShipping?.(code, subtotal) ?? [];
|
|
259
|
+
} catch {
|
|
260
|
+
set({
|
|
261
|
+
...EMPTY,
|
|
262
|
+
postalCode: code,
|
|
263
|
+
status: "error",
|
|
264
|
+
error: "N\xE3o foi poss\xEDvel calcular o frete agora. Tente de novo."
|
|
265
|
+
});
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const refusedZone = await refusalPromise;
|
|
269
|
+
if (refusedZone) options = [];
|
|
270
|
+
let address = null;
|
|
271
|
+
let notFound = false;
|
|
272
|
+
try {
|
|
273
|
+
address = await lookupPostalCode(code);
|
|
274
|
+
notFound = address === null;
|
|
275
|
+
} catch {
|
|
276
|
+
address = null;
|
|
277
|
+
}
|
|
278
|
+
set({
|
|
279
|
+
postalCode: code,
|
|
280
|
+
address,
|
|
281
|
+
options,
|
|
282
|
+
selectedZoneId: options[0]?.zoneId ?? null,
|
|
283
|
+
refusedZone,
|
|
284
|
+
// A store with no zones configured quotes nothing, and that must NOT
|
|
285
|
+
// read as "we don't deliver here" — it falls back to the store-wide
|
|
286
|
+
// flat rate, which the cart already knows how to show.
|
|
287
|
+
status: options.length > 0 ? "served" : "unserved",
|
|
288
|
+
// Reported alongside the price, not instead of it: the CEP may be
|
|
289
|
+
// mistyped even though its range is covered.
|
|
290
|
+
error: notFound ? "CEP n\xE3o encontrado. Confira os n\xFAmeros." : null
|
|
291
|
+
});
|
|
292
|
+
},
|
|
293
|
+
selectZone(zoneId) {
|
|
294
|
+
if (!get().options.some((option) => option.zoneId === zoneId)) return;
|
|
295
|
+
set({ selectedZoneId: zoneId });
|
|
296
|
+
void getShopProvider().selectShippingOption?.(zoneId)?.catch(() => {
|
|
297
|
+
});
|
|
298
|
+
},
|
|
299
|
+
clear() {
|
|
300
|
+
set({ ...EMPTY });
|
|
301
|
+
}
|
|
302
|
+
}),
|
|
303
|
+
{
|
|
304
|
+
name: "fayz.storefront.delivery.v1",
|
|
305
|
+
// Deliberately NOT persisting `options`: a rehydrated
|
|
306
|
+
// price would be shown as current without anything having quoted it. The
|
|
307
|
+
// address survives, the money is asked for again.
|
|
308
|
+
partialize: (state) => ({ postalCode: state.postalCode, address: state.address }),
|
|
309
|
+
onRehydrateStorage: () => (state) => {
|
|
310
|
+
if (state?.address) state.status = "idle";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
);
|
|
315
|
+
function resolveOptionRate(option, subtotal) {
|
|
316
|
+
if (option.freeAbove != null && subtotal >= option.freeAbove) return 0;
|
|
317
|
+
return option.baseRate;
|
|
318
|
+
}
|
|
319
|
+
var selectQuotedShipping = (state, subtotal) => {
|
|
320
|
+
if (state.status !== "served") return null;
|
|
321
|
+
const option = state.options.find((o) => o.zoneId === state.selectedZoneId) ?? state.options[0];
|
|
322
|
+
return option ? resolveOptionRate(option, subtotal) : null;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// src/stores/cart.store.ts
|
|
326
|
+
var useCartStore = create()(
|
|
327
|
+
persist(
|
|
328
|
+
(set, get) => ({
|
|
329
|
+
lines: [],
|
|
330
|
+
discountCode: null,
|
|
331
|
+
discountPercent: 0,
|
|
332
|
+
isOpen: false,
|
|
333
|
+
justAddedLineId: null,
|
|
334
|
+
addItem: (product, qty = 1, options) => set((state) => {
|
|
335
|
+
const normalizedOptions = normalizeProductOptionSelection(options);
|
|
336
|
+
const optionKey = productOptionSelectionKey(normalizedOptions);
|
|
337
|
+
const lineId = optionKey ? `${product.id}:${optionKey}` : product.id;
|
|
338
|
+
const variant = resolveVariantForSelection(product, normalizedOptions);
|
|
339
|
+
const unitPrice = variant?.price ?? product.price;
|
|
340
|
+
const stock = variant ? variant.inventoryCount : product.inventoryCount;
|
|
341
|
+
const existing = state.lines.find((l) => (l.lineId ?? l.productId) === lineId);
|
|
342
|
+
if (existing) {
|
|
343
|
+
const nextQty = Math.min(existing.quantity + qty, existing.maxQuantity || Infinity);
|
|
344
|
+
return {
|
|
345
|
+
// Auto-open the cart on add — the standard premium "slide-in cart"
|
|
346
|
+
// signal — and flag the line so the drawer can highlight it.
|
|
347
|
+
isOpen: true,
|
|
348
|
+
justAddedLineId: lineId,
|
|
349
|
+
lines: state.lines.map(
|
|
350
|
+
(l) => (l.lineId ?? l.productId) === lineId ? { ...l, lineId, quantity: nextQty } : l
|
|
351
|
+
)
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
|
|
355
|
+
const line = {
|
|
356
|
+
lineId,
|
|
357
|
+
productId: product.id,
|
|
358
|
+
variantId: variant?.id,
|
|
359
|
+
slug: product.slug,
|
|
360
|
+
name: product.name,
|
|
361
|
+
sku: variant?.sku ?? product.sku,
|
|
362
|
+
options: normalizedOptions,
|
|
363
|
+
optionsLabel: formatProductOptionSelection(normalizedOptions),
|
|
364
|
+
unitPrice,
|
|
365
|
+
quantity: Math.min(qty, stock || Infinity),
|
|
366
|
+
imageUrl: image?.url ?? null,
|
|
367
|
+
maxQuantity: stock,
|
|
368
|
+
requiresShipping: product.isPhysical !== false,
|
|
369
|
+
weight: variant?.weight ?? product.weight ?? null,
|
|
370
|
+
weightUnit: variant?.weightUnit ?? product.weightUnit ?? null,
|
|
371
|
+
heightCm: variant?.heightCm ?? product.heightCm ?? null,
|
|
372
|
+
widthCm: variant?.widthCm ?? product.widthCm ?? null,
|
|
373
|
+
lengthCm: variant?.lengthCm ?? product.lengthCm ?? null,
|
|
374
|
+
packageId: product.packageId ?? null
|
|
375
|
+
};
|
|
376
|
+
return { isOpen: true, justAddedLineId: lineId, lines: [...state.lines, line] };
|
|
377
|
+
}),
|
|
378
|
+
setQuantity: (lineId, qty) => set((state) => {
|
|
379
|
+
if (qty <= 0) return { lines: state.lines.filter((l) => (l.lineId ?? l.productId) !== lineId) };
|
|
380
|
+
return {
|
|
381
|
+
lines: state.lines.map(
|
|
382
|
+
(l) => (l.lineId ?? l.productId) === lineId ? { ...l, quantity: Math.min(qty, l.maxQuantity || Infinity) } : l
|
|
383
|
+
)
|
|
384
|
+
};
|
|
385
|
+
}),
|
|
386
|
+
removeItem: (lineId) => set((state) => ({ lines: state.lines.filter((l) => (l.lineId ?? l.productId) !== lineId) })),
|
|
387
|
+
applyDiscount: (code, percent) => set({ discountCode: code, discountPercent: percent }),
|
|
388
|
+
clearDiscount: () => set({ discountCode: null, discountPercent: 0 }),
|
|
389
|
+
clear: () => set({ lines: [], discountCode: null, discountPercent: 0 }),
|
|
390
|
+
openDrawer: () => set({ isOpen: true }),
|
|
391
|
+
closeDrawer: () => set({ isOpen: false, justAddedLineId: null }),
|
|
392
|
+
reconcile: async (resolve) => {
|
|
393
|
+
const lines = get().lines;
|
|
394
|
+
if (lines.length === 0) return;
|
|
395
|
+
const keep = await Promise.all(
|
|
396
|
+
lines.map((line) => resolve(line).catch(() => true))
|
|
397
|
+
);
|
|
398
|
+
const kept = lines.filter((_, i) => keep[i]);
|
|
399
|
+
if (kept.length !== lines.length) set({ lines: kept });
|
|
400
|
+
}
|
|
401
|
+
}),
|
|
402
|
+
{
|
|
403
|
+
name: "fayz.storefront.cart.v1",
|
|
404
|
+
partialize: (state) => ({
|
|
405
|
+
lines: state.lines,
|
|
406
|
+
discountCode: state.discountCode,
|
|
407
|
+
discountPercent: state.discountPercent
|
|
408
|
+
})
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
);
|
|
412
|
+
var selectCount = (s2) => s2.lines.reduce((n, l) => n + l.quantity, 0);
|
|
413
|
+
var selectSubtotal = (s2) => roundCents(s2.lines.reduce((sum, l) => sum + l.unitPrice * l.quantity, 0));
|
|
414
|
+
var selectDiscountTotal = (s2) => roundCents(selectSubtotal(s2) * (s2.discountPercent / 100));
|
|
415
|
+
var selectRequiresShipping = (s2) => s2.lines.some((l) => l.requiresShipping !== false);
|
|
416
|
+
var selectShipping = (s2, cfg) => {
|
|
417
|
+
if (s2.lines.length === 0) return 0;
|
|
418
|
+
if (!selectRequiresShipping(s2)) return 0;
|
|
419
|
+
const subtotal = selectSubtotal(s2);
|
|
420
|
+
const quoted = selectQuotedShipping(useDeliveryStore.getState(), subtotal);
|
|
421
|
+
if (quoted != null) return quoted;
|
|
422
|
+
if (cfg.shipping.freeAbove != null && subtotal >= cfg.shipping.freeAbove) return 0;
|
|
423
|
+
return cfg.shipping.flatRate;
|
|
424
|
+
};
|
|
425
|
+
var selectTotal = (s2, cfg) => roundCents(selectSubtotal(s2) - selectDiscountTotal(s2) + selectShipping(s2, cfg));
|
|
426
|
+
|
|
159
427
|
// src/data/facets.ts
|
|
160
428
|
function matchesFacets(metadata, facets) {
|
|
161
429
|
for (const [key, wanted] of Object.entries(facets)) {
|
|
@@ -249,7 +517,7 @@ async function categoriesList(_params, ctx) {
|
|
|
249
517
|
return ctx.shop.listCategories();
|
|
250
518
|
}
|
|
251
519
|
function entityCapableShop(shop) {
|
|
252
|
-
if (!shop.listEntityRecords || !shop.getEntityRecordBySlug
|
|
520
|
+
if (!shop.listEntityRecords || !shop.getEntityRecordBySlug) {
|
|
253
521
|
throw new Error(
|
|
254
522
|
"This backend provider does not support custom entities (describeStore capabilities.customEntities). Both the Fayz Supabase provider and the mock provider do \u2014 a custom provider must implement the entity methods."
|
|
255
523
|
);
|
|
@@ -272,12 +540,6 @@ async function entityBySlug(params, ctx) {
|
|
|
272
540
|
const shop = entityCapableShop(ctx.shop);
|
|
273
541
|
return shop.getEntityRecordBySlug(params.entity, params.slug);
|
|
274
542
|
}
|
|
275
|
-
async function entityFields(params, ctx) {
|
|
276
|
-
resolveSidecarDeclaration(String(params.reference ?? ""));
|
|
277
|
-
if (!params.id) return null;
|
|
278
|
-
const shop = entityCapableShop(ctx.shop);
|
|
279
|
-
return shop.getEntityFields(params.reference, params.id);
|
|
280
|
-
}
|
|
281
543
|
async function collectionsList(_params, ctx) {
|
|
282
544
|
const products = await ctx.shop.listProducts({ status: "active" });
|
|
283
545
|
const tags = /* @__PURE__ */ new Set();
|
|
@@ -422,28 +684,6 @@ function registerStorefrontLoaders() {
|
|
|
422
684
|
}
|
|
423
685
|
]
|
|
424
686
|
});
|
|
425
|
-
defineLoader("entity.fields", entityFields, {
|
|
426
|
-
source: "sdk",
|
|
427
|
-
title: "Custom fields of a core record",
|
|
428
|
-
description: "The declared sidecar fields (defineSidecarFields) of one product or category \u2014 the entity loader path for core-record extensions.",
|
|
429
|
-
output: "The validated custom-field object, or null when the record has no sidecar row.",
|
|
430
|
-
settings: [
|
|
431
|
-
{
|
|
432
|
-
id: "reference",
|
|
433
|
-
type: "select",
|
|
434
|
-
label: "Record kind",
|
|
435
|
-
options: SIDECAR_REFERENCES.map((reference) => ({ value: reference, label: reference })),
|
|
436
|
-
required: true
|
|
437
|
-
},
|
|
438
|
-
{
|
|
439
|
-
id: "id",
|
|
440
|
-
type: "text",
|
|
441
|
-
label: "Record id",
|
|
442
|
-
help: "The core record's id. On a product page, bind through the resolved product.",
|
|
443
|
-
required: true
|
|
444
|
-
}
|
|
445
|
-
]
|
|
446
|
-
});
|
|
447
687
|
}
|
|
448
688
|
|
|
449
689
|
// src/blocks/sections.ts
|
|
@@ -818,6 +1058,7 @@ var TID = {
|
|
|
818
1058
|
// checkout
|
|
819
1059
|
checkoutEmail: "checkout-email",
|
|
820
1060
|
checkoutName: "checkout-name",
|
|
1061
|
+
checkoutDocument: "checkout-document",
|
|
821
1062
|
checkoutStreet: "checkout-street",
|
|
822
1063
|
checkoutCity: "checkout-city",
|
|
823
1064
|
checkoutZip: "checkout-zip",
|
|
@@ -825,6 +1066,19 @@ var TID = {
|
|
|
825
1066
|
checkoutExpiry: "checkout-expiry",
|
|
826
1067
|
checkoutCvc: "checkout-cvc",
|
|
827
1068
|
placeOrder: "place-order",
|
|
1069
|
+
// checkout em passos
|
|
1070
|
+
checkoutSteps: "checkout-steps",
|
|
1071
|
+
checkoutStep: (n) => `checkout-step-${n}`,
|
|
1072
|
+
checkoutPayment: "checkout-payment",
|
|
1073
|
+
checkoutPaymentTotal: "checkout-payment-total",
|
|
1074
|
+
checkoutContinue: "checkout-continue",
|
|
1075
|
+
checkoutBack: "checkout-back",
|
|
1076
|
+
checkoutDone: "checkout-done",
|
|
1077
|
+
// payment step
|
|
1078
|
+
pixPanel: "pix-panel",
|
|
1079
|
+
pixCode: "pix-code",
|
|
1080
|
+
pixCopy: "pix-copy",
|
|
1081
|
+
pixConfirm: "pix-confirm",
|
|
828
1082
|
checkoutError: "checkout-error",
|
|
829
1083
|
checkoutProcessing: "checkout-processing",
|
|
830
1084
|
// confirmation
|
|
@@ -1435,267 +1689,17 @@ var categoriesBlockMeta = {
|
|
|
1435
1689
|
label: "Heading",
|
|
1436
1690
|
help: "Optional heading above the categories. Leave empty to render the grid with no heading."
|
|
1437
1691
|
}
|
|
1438
|
-
],
|
|
1439
|
-
presets: [
|
|
1440
|
-
{ name: "Category tiles", props: { style: "tiles", title: "Nossas categorias" } },
|
|
1441
|
-
{ name: "Category bubbles", props: { style: "bubbles", title: "Compre por categoria" } }
|
|
1442
|
-
],
|
|
1443
|
-
// Not on 'product': a full category index on a product page competes with the
|
|
1444
|
-
// buying decision instead of supporting it.
|
|
1445
|
-
enabledOn: ["home", "catalog", "content"],
|
|
1446
|
-
// The declared read (M3): the whole category list, no params.
|
|
1447
|
-
data: { categories: { loader: "categories.list" } }
|
|
1448
|
-
};
|
|
1449
|
-
|
|
1450
|
-
// src/lib/format.ts
|
|
1451
|
-
function formatMoney(value, currency = "BRL", locale = "pt-BR") {
|
|
1452
|
-
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(value);
|
|
1453
|
-
}
|
|
1454
|
-
function roundCents(value) {
|
|
1455
|
-
return Math.round(value * 100) / 100;
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
// src/data/product-options.ts
|
|
1459
|
-
function cleanValues(value) {
|
|
1460
|
-
if (!Array.isArray(value)) return [];
|
|
1461
|
-
return Array.from(new Set(value.filter((item) => typeof item === "string" && item.trim().length > 0)));
|
|
1462
|
-
}
|
|
1463
|
-
function cleanOptionGroup(value) {
|
|
1464
|
-
if (!value || typeof value !== "object") return null;
|
|
1465
|
-
const record = value;
|
|
1466
|
-
const id = typeof record.id === "string" ? record.id : typeof record.name === "string" ? record.name : null;
|
|
1467
|
-
const label = typeof record.label === "string" ? record.label : typeof record.name === "string" ? record.name : id;
|
|
1468
|
-
const values = cleanValues(record.values);
|
|
1469
|
-
if (!id || !label || values.length === 0) return null;
|
|
1470
|
-
return { id, label, values };
|
|
1471
|
-
}
|
|
1472
|
-
var warnedOverlayProducts = /* @__PURE__ */ new Set();
|
|
1473
|
-
function resetProductOptionsDeprecationWarnings() {
|
|
1474
|
-
warnedOverlayProducts.clear();
|
|
1475
|
-
}
|
|
1476
|
-
function getProductOptionGroups(product) {
|
|
1477
|
-
if (product.options && product.options.length > 0) {
|
|
1478
|
-
return [...product.options].sort((a, b) => a.position - b.position).map((option) => ({ id: option.name, label: option.name, values: [...option.values] }));
|
|
1479
|
-
}
|
|
1480
|
-
const options = product.metadata.options;
|
|
1481
|
-
const explicit = Array.isArray(options) ? options.map(cleanOptionGroup).filter((group) => Boolean(group)) : [];
|
|
1482
|
-
const groups = [...explicit];
|
|
1483
|
-
if (groups.length === 0) {
|
|
1484
|
-
const sizes = cleanValues(product.metadata.sizes);
|
|
1485
|
-
const colors = cleanValues(product.metadata.colors);
|
|
1486
|
-
if (sizes.length > 0) groups.push({ id: "Tamanho", label: "Tamanho", values: sizes });
|
|
1487
|
-
if (colors.length > 0) groups.push({ id: "Cor", label: "Cor", values: colors });
|
|
1488
|
-
}
|
|
1489
|
-
if (groups.length > 0 && !warnedOverlayProducts.has(product.id)) {
|
|
1490
|
-
warnedOverlayProducts.add(product.id);
|
|
1491
|
-
console.warn(
|
|
1492
|
-
`[storefront] product "${product.slug}" renders options from the DEPRECATED metadata overlay (metadata.options/sizes/colors). These labels are display-only \u2014 checkout cannot price or reserve them. Define real variants (0028) instead; the overlay path is removed at M9.`
|
|
1493
|
-
);
|
|
1494
|
-
}
|
|
1495
|
-
return groups;
|
|
1496
|
-
}
|
|
1497
|
-
function resolveVariantForSelection(product, selection) {
|
|
1498
|
-
const variants = product.variants;
|
|
1499
|
-
if (!variants || variants.length === 0) return null;
|
|
1500
|
-
const normalized = normalizeProductOptionSelection(selection);
|
|
1501
|
-
const wanted = Object.entries(normalized);
|
|
1502
|
-
return variants.find((variant) => {
|
|
1503
|
-
const options = variant.options;
|
|
1504
|
-
return Object.keys(options).length === wanted.length && wanted.every(([key, value]) => options[key] === value);
|
|
1505
|
-
}) ?? null;
|
|
1506
|
-
}
|
|
1507
|
-
function selectionPrice(product, selection) {
|
|
1508
|
-
const variant = resolveVariantForSelection(product, selection);
|
|
1509
|
-
return variant?.price ?? product.price;
|
|
1510
|
-
}
|
|
1511
|
-
function normalizeProductOptionSelection(selection) {
|
|
1512
|
-
if (!selection) return {};
|
|
1513
|
-
return Object.fromEntries(
|
|
1514
|
-
Object.entries(selection).filter((entry) => entry[0].trim().length > 0 && entry[1].trim().length > 0).sort(([a], [b]) => a.localeCompare(b))
|
|
1515
|
-
);
|
|
1516
|
-
}
|
|
1517
|
-
function formatProductOptionSelection(selection) {
|
|
1518
|
-
return Object.entries(normalizeProductOptionSelection(selection)).map(([label, value]) => `${label}: ${value}`).join(" \xB7 ");
|
|
1519
|
-
}
|
|
1520
|
-
function productOptionSelectionKey(selection) {
|
|
1521
|
-
const normalized = normalizeProductOptionSelection(selection);
|
|
1522
|
-
const entries = Object.entries(normalized);
|
|
1523
|
-
return entries.length > 0 ? JSON.stringify(entries) : "";
|
|
1524
|
-
}
|
|
1525
|
-
var EMPTY = {
|
|
1526
|
-
postalCode: "",
|
|
1527
|
-
address: null,
|
|
1528
|
-
options: [],
|
|
1529
|
-
selectedZoneId: null,
|
|
1530
|
-
status: "idle",
|
|
1531
|
-
error: null
|
|
1532
|
-
};
|
|
1533
|
-
var useDeliveryStore = create()(
|
|
1534
|
-
persist(
|
|
1535
|
-
(set, get) => ({
|
|
1536
|
-
...EMPTY,
|
|
1537
|
-
async resolve(postalCode, subtotal) {
|
|
1538
|
-
const code = normalizePostalCode(postalCode);
|
|
1539
|
-
if (code.length !== 8) {
|
|
1540
|
-
set({ ...EMPTY, postalCode, status: "error", error: "Digite os 8 d\xEDgitos do CEP." });
|
|
1541
|
-
return;
|
|
1542
|
-
}
|
|
1543
|
-
set({ postalCode: code, status: "loading", error: null });
|
|
1544
|
-
let options = [];
|
|
1545
|
-
try {
|
|
1546
|
-
options = await getShopProvider().quoteShipping?.(code, subtotal) ?? [];
|
|
1547
|
-
} catch {
|
|
1548
|
-
set({
|
|
1549
|
-
...EMPTY,
|
|
1550
|
-
postalCode: code,
|
|
1551
|
-
status: "error",
|
|
1552
|
-
error: "N\xE3o foi poss\xEDvel calcular o frete agora. Tente de novo."
|
|
1553
|
-
});
|
|
1554
|
-
return;
|
|
1555
|
-
}
|
|
1556
|
-
let address = null;
|
|
1557
|
-
let notFound = false;
|
|
1558
|
-
try {
|
|
1559
|
-
address = await lookupPostalCode(code);
|
|
1560
|
-
notFound = address === null;
|
|
1561
|
-
} catch {
|
|
1562
|
-
address = null;
|
|
1563
|
-
}
|
|
1564
|
-
set({
|
|
1565
|
-
postalCode: code,
|
|
1566
|
-
address,
|
|
1567
|
-
options,
|
|
1568
|
-
selectedZoneId: options[0]?.zoneId ?? null,
|
|
1569
|
-
// A store with no zones configured quotes nothing, and that must NOT
|
|
1570
|
-
// read as "we don't deliver here" — it falls back to the store-wide
|
|
1571
|
-
// flat rate, which the cart already knows how to show.
|
|
1572
|
-
status: options.length > 0 ? "served" : "unserved",
|
|
1573
|
-
// Reported alongside the price, not instead of it: the CEP may be
|
|
1574
|
-
// mistyped even though its range is covered.
|
|
1575
|
-
error: notFound ? "CEP n\xE3o encontrado. Confira os n\xFAmeros." : null
|
|
1576
|
-
});
|
|
1577
|
-
},
|
|
1578
|
-
selectZone(zoneId) {
|
|
1579
|
-
if (get().options.some((option) => option.zoneId === zoneId)) set({ selectedZoneId: zoneId });
|
|
1580
|
-
},
|
|
1581
|
-
clear() {
|
|
1582
|
-
set({ ...EMPTY });
|
|
1583
|
-
}
|
|
1584
|
-
}),
|
|
1585
|
-
{
|
|
1586
|
-
name: "fayz.storefront.delivery.v1",
|
|
1587
|
-
// Deliberately NOT persisting `options`: a rehydrated
|
|
1588
|
-
// price would be shown as current without anything having quoted it. The
|
|
1589
|
-
// address survives, the money is asked for again.
|
|
1590
|
-
partialize: (state) => ({ postalCode: state.postalCode, address: state.address }),
|
|
1591
|
-
onRehydrateStorage: () => (state) => {
|
|
1592
|
-
if (state?.address) state.status = "idle";
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
)
|
|
1596
|
-
);
|
|
1597
|
-
function resolveOptionRate(option, subtotal) {
|
|
1598
|
-
if (option.freeAbove != null && subtotal >= option.freeAbove) return 0;
|
|
1599
|
-
return option.baseRate;
|
|
1600
|
-
}
|
|
1601
|
-
var selectQuotedShipping = (state, subtotal) => {
|
|
1602
|
-
if (state.status !== "served") return null;
|
|
1603
|
-
const option = state.options.find((o) => o.zoneId === state.selectedZoneId) ?? state.options[0];
|
|
1604
|
-
return option ? resolveOptionRate(option, subtotal) : null;
|
|
1605
|
-
};
|
|
1606
|
-
|
|
1607
|
-
// src/stores/cart.store.ts
|
|
1608
|
-
var useCartStore = create()(
|
|
1609
|
-
persist(
|
|
1610
|
-
(set, get) => ({
|
|
1611
|
-
lines: [],
|
|
1612
|
-
discountCode: null,
|
|
1613
|
-
discountPercent: 0,
|
|
1614
|
-
isOpen: false,
|
|
1615
|
-
justAddedLineId: null,
|
|
1616
|
-
addItem: (product, qty = 1, options) => set((state) => {
|
|
1617
|
-
const normalizedOptions = normalizeProductOptionSelection(options);
|
|
1618
|
-
const optionKey = productOptionSelectionKey(normalizedOptions);
|
|
1619
|
-
const lineId = optionKey ? `${product.id}:${optionKey}` : product.id;
|
|
1620
|
-
const variant = resolveVariantForSelection(product, normalizedOptions);
|
|
1621
|
-
const unitPrice = variant?.price ?? product.price;
|
|
1622
|
-
const stock = variant ? variant.inventoryCount : product.inventoryCount;
|
|
1623
|
-
const existing = state.lines.find((l) => (l.lineId ?? l.productId) === lineId);
|
|
1624
|
-
if (existing) {
|
|
1625
|
-
const nextQty = Math.min(existing.quantity + qty, existing.maxQuantity || Infinity);
|
|
1626
|
-
return {
|
|
1627
|
-
// Auto-open the cart on add — the standard premium "slide-in cart"
|
|
1628
|
-
// signal — and flag the line so the drawer can highlight it.
|
|
1629
|
-
isOpen: true,
|
|
1630
|
-
justAddedLineId: lineId,
|
|
1631
|
-
lines: state.lines.map(
|
|
1632
|
-
(l) => (l.lineId ?? l.productId) === lineId ? { ...l, lineId, quantity: nextQty } : l
|
|
1633
|
-
)
|
|
1634
|
-
};
|
|
1635
|
-
}
|
|
1636
|
-
const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
|
|
1637
|
-
const line = {
|
|
1638
|
-
lineId,
|
|
1639
|
-
productId: product.id,
|
|
1640
|
-
variantId: variant?.id,
|
|
1641
|
-
slug: product.slug,
|
|
1642
|
-
name: product.name,
|
|
1643
|
-
sku: variant?.sku ?? product.sku,
|
|
1644
|
-
options: normalizedOptions,
|
|
1645
|
-
optionsLabel: formatProductOptionSelection(normalizedOptions),
|
|
1646
|
-
unitPrice,
|
|
1647
|
-
quantity: Math.min(qty, stock || Infinity),
|
|
1648
|
-
imageUrl: image?.url ?? null,
|
|
1649
|
-
maxQuantity: stock
|
|
1650
|
-
};
|
|
1651
|
-
return { isOpen: true, justAddedLineId: lineId, lines: [...state.lines, line] };
|
|
1652
|
-
}),
|
|
1653
|
-
setQuantity: (lineId, qty) => set((state) => {
|
|
1654
|
-
if (qty <= 0) return { lines: state.lines.filter((l) => (l.lineId ?? l.productId) !== lineId) };
|
|
1655
|
-
return {
|
|
1656
|
-
lines: state.lines.map(
|
|
1657
|
-
(l) => (l.lineId ?? l.productId) === lineId ? { ...l, quantity: Math.min(qty, l.maxQuantity || Infinity) } : l
|
|
1658
|
-
)
|
|
1659
|
-
};
|
|
1660
|
-
}),
|
|
1661
|
-
removeItem: (lineId) => set((state) => ({ lines: state.lines.filter((l) => (l.lineId ?? l.productId) !== lineId) })),
|
|
1662
|
-
applyDiscount: (code, percent) => set({ discountCode: code, discountPercent: percent }),
|
|
1663
|
-
clearDiscount: () => set({ discountCode: null, discountPercent: 0 }),
|
|
1664
|
-
clear: () => set({ lines: [], discountCode: null, discountPercent: 0 }),
|
|
1665
|
-
openDrawer: () => set({ isOpen: true }),
|
|
1666
|
-
closeDrawer: () => set({ isOpen: false, justAddedLineId: null }),
|
|
1667
|
-
reconcile: async (resolve) => {
|
|
1668
|
-
const lines = get().lines;
|
|
1669
|
-
if (lines.length === 0) return;
|
|
1670
|
-
const keep = await Promise.all(
|
|
1671
|
-
lines.map((line) => resolve(line).catch(() => true))
|
|
1672
|
-
);
|
|
1673
|
-
const kept = lines.filter((_, i) => keep[i]);
|
|
1674
|
-
if (kept.length !== lines.length) set({ lines: kept });
|
|
1675
|
-
}
|
|
1676
|
-
}),
|
|
1677
|
-
{
|
|
1678
|
-
name: "fayz.storefront.cart.v1",
|
|
1679
|
-
partialize: (state) => ({
|
|
1680
|
-
lines: state.lines,
|
|
1681
|
-
discountCode: state.discountCode,
|
|
1682
|
-
discountPercent: state.discountPercent
|
|
1683
|
-
})
|
|
1684
|
-
}
|
|
1685
|
-
)
|
|
1686
|
-
);
|
|
1687
|
-
var selectCount = (s2) => s2.lines.reduce((n, l) => n + l.quantity, 0);
|
|
1688
|
-
var selectSubtotal = (s2) => roundCents(s2.lines.reduce((sum, l) => sum + l.unitPrice * l.quantity, 0));
|
|
1689
|
-
var selectDiscountTotal = (s2) => roundCents(selectSubtotal(s2) * (s2.discountPercent / 100));
|
|
1690
|
-
var selectShipping = (s2, cfg) => {
|
|
1691
|
-
if (s2.lines.length === 0) return 0;
|
|
1692
|
-
const subtotal = selectSubtotal(s2);
|
|
1693
|
-
const quoted = selectQuotedShipping(useDeliveryStore.getState(), subtotal);
|
|
1694
|
-
if (quoted != null) return quoted;
|
|
1695
|
-
if (cfg.shipping.freeAbove != null && subtotal >= cfg.shipping.freeAbove) return 0;
|
|
1696
|
-
return cfg.shipping.flatRate;
|
|
1692
|
+
],
|
|
1693
|
+
presets: [
|
|
1694
|
+
{ name: "Category tiles", props: { style: "tiles", title: "Nossas categorias" } },
|
|
1695
|
+
{ name: "Category bubbles", props: { style: "bubbles", title: "Compre por categoria" } }
|
|
1696
|
+
],
|
|
1697
|
+
// Not on 'product': a full category index on a product page competes with the
|
|
1698
|
+
// buying decision instead of supporting it.
|
|
1699
|
+
enabledOn: ["home", "catalog", "content"],
|
|
1700
|
+
// The declared read (M3): the whole category list, no params.
|
|
1701
|
+
data: { categories: { loader: "categories.list" } }
|
|
1697
1702
|
};
|
|
1698
|
-
var selectTotal = (s2, cfg) => roundCents(selectSubtotal(s2) - selectDiscountTotal(s2) + selectShipping(s2, cfg));
|
|
1699
1703
|
function useEnquiry() {
|
|
1700
1704
|
const config = useStorefrontConfig();
|
|
1701
1705
|
const createProductEnquiry = async (input) => {
|
|
@@ -3458,7 +3462,7 @@ function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTe
|
|
|
3458
3462
|
"data-testid": decTestId,
|
|
3459
3463
|
"aria-label": "Diminuir quantidade",
|
|
3460
3464
|
onClick: () => onChange(clamp(value - 1)),
|
|
3461
|
-
className: "px-2.5 py-1.5 text-muted-foreground hover:text-foreground disabled:opacity-40",
|
|
3465
|
+
className: "min-h-11 min-w-11 px-2.5 py-1.5 text-muted-foreground hover:text-foreground disabled:opacity-40 sm:min-h-0 sm:min-w-0 grid place-items-center",
|
|
3462
3466
|
disabled: value <= min,
|
|
3463
3467
|
children: /* @__PURE__ */ jsx(Minus, { className: "h-3.5 w-3.5" })
|
|
3464
3468
|
}
|
|
@@ -3475,7 +3479,7 @@ function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTe
|
|
|
3475
3479
|
const n = parseInt(e.target.value, 10);
|
|
3476
3480
|
if (!isNaN(n)) onChange(clamp(n));
|
|
3477
3481
|
},
|
|
3478
|
-
className: "w-12 border-x bg-transparent py-1.5 text-center text-sm [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
|
3482
|
+
className: "h-11 w-12 border-x bg-transparent py-1.5 text-center text-sm [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none sm:h-auto"
|
|
3479
3483
|
}
|
|
3480
3484
|
),
|
|
3481
3485
|
/* @__PURE__ */ jsx(
|
|
@@ -3485,7 +3489,7 @@ function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTe
|
|
|
3485
3489
|
"data-testid": incTestId,
|
|
3486
3490
|
"aria-label": "Aumentar quantidade",
|
|
3487
3491
|
onClick: () => onChange(clamp(value + 1)),
|
|
3488
|
-
className: "px-2.5 py-1.5 text-muted-foreground hover:text-foreground disabled:opacity-40",
|
|
3492
|
+
className: "min-h-11 min-w-11 px-2.5 py-1.5 text-muted-foreground hover:text-foreground disabled:opacity-40 sm:min-h-0 sm:min-w-0 grid place-items-center",
|
|
3489
3493
|
disabled: max != null && value >= max,
|
|
3490
3494
|
children: /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" })
|
|
3491
3495
|
}
|
|
@@ -4333,7 +4337,7 @@ function ProductDetailPage({ slug }) {
|
|
|
4333
4337
|
}
|
|
4334
4338
|
) }),
|
|
4335
4339
|
/* @__PURE__ */ jsx(Slot, { area: "productDetail", name: "afterAddToCart", context: slotContext }),
|
|
4336
|
-
config.commerceMode === "checkout" && /* @__PURE__ */ jsx(DeliveryEstimator, {}),
|
|
4340
|
+
config.commerceMode === "checkout" && product.isPhysical !== false && /* @__PURE__ */ jsx(DeliveryEstimator, {}),
|
|
4337
4341
|
config.commerceMode === "checkout" && /* @__PURE__ */ jsxs("div", { className: "mt-6 grid grid-cols-3 gap-3 border-t pt-6 text-center", children: [
|
|
4338
4342
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
|
|
4339
4343
|
/* @__PURE__ */ jsx(Truck, { className: "h-5 w-5 text-primary" }),
|
|
@@ -5959,6 +5963,49 @@ function sectionsToBlocks(sections) {
|
|
|
5959
5963
|
return { type, id: `${type}-${i}`, props };
|
|
5960
5964
|
});
|
|
5961
5965
|
}
|
|
5966
|
+
function applyStoreSettings(config, settings) {
|
|
5967
|
+
if (!settings) return config;
|
|
5968
|
+
const next = { ...config, policy: settings };
|
|
5969
|
+
if (settings.currency) next.currency = settings.currency;
|
|
5970
|
+
if (settings.locale) next.locale = settings.locale;
|
|
5971
|
+
if (settings.paymentMethods?.length) {
|
|
5972
|
+
next.payments = { ...config.payments, methods: settings.paymentMethods };
|
|
5973
|
+
}
|
|
5974
|
+
if (settings.discountsEnabled !== void 0) {
|
|
5975
|
+
next.features = { ...config.features, discounts: settings.discountsEnabled };
|
|
5976
|
+
}
|
|
5977
|
+
if (settings.guestCheckout === false) {
|
|
5978
|
+
next.features = { ...next.features, accounts: true };
|
|
5979
|
+
}
|
|
5980
|
+
return next;
|
|
5981
|
+
}
|
|
5982
|
+
function StorefrontConfigHost({
|
|
5983
|
+
config,
|
|
5984
|
+
children
|
|
5985
|
+
}) {
|
|
5986
|
+
const [policy, setPolicy] = React4.useState(null);
|
|
5987
|
+
React4.useEffect(() => {
|
|
5988
|
+
let cancelled = false;
|
|
5989
|
+
let provider;
|
|
5990
|
+
try {
|
|
5991
|
+
provider = config.provider ?? getShopProvider();
|
|
5992
|
+
} catch {
|
|
5993
|
+
return;
|
|
5994
|
+
}
|
|
5995
|
+
if (typeof provider.getPublicStoreSettings !== "function") return;
|
|
5996
|
+
void provider.getPublicStoreSettings().then((settings) => {
|
|
5997
|
+
if (cancelled) return;
|
|
5998
|
+
setPolicy(settings);
|
|
5999
|
+
setShopCarrierQuotesResolver(() => settings?.carrierQuotes);
|
|
6000
|
+
}).catch(() => {
|
|
6001
|
+
});
|
|
6002
|
+
return () => {
|
|
6003
|
+
cancelled = true;
|
|
6004
|
+
};
|
|
6005
|
+
}, [config]);
|
|
6006
|
+
const resolved = React4.useMemo(() => applyStoreSettings(config, policy), [config, policy]);
|
|
6007
|
+
return /* @__PURE__ */ jsx(StorefrontConfigProvider, { value: resolved, children });
|
|
6008
|
+
}
|
|
5962
6009
|
var RADIUS_MAP = {
|
|
5963
6010
|
none: { button: "0px", card: "0px", input: "0px" },
|
|
5964
6011
|
soft: { button: "6px", card: "10px", input: "6px" },
|
|
@@ -6381,12 +6428,28 @@ function HeaderActions({ onMenuClick }) {
|
|
|
6381
6428
|
const openDrawer = useCartStore((s2) => s2.openDrawer);
|
|
6382
6429
|
const session = useSessionStore();
|
|
6383
6430
|
const [accountOpen, setAccountOpen] = useState(false);
|
|
6431
|
+
const accountRef = React4.useRef(null);
|
|
6384
6432
|
const pop = usePopOnChange(count);
|
|
6385
6433
|
const firstName = session.name?.split(/\s+/)[0];
|
|
6386
6434
|
const initial2 = (firstName ?? session.email ?? "C").slice(0, 1).toUpperCase();
|
|
6435
|
+
React4.useEffect(() => {
|
|
6436
|
+
if (!accountOpen) return;
|
|
6437
|
+
const onPointerDown = (event) => {
|
|
6438
|
+
if (!accountRef.current?.contains(event.target)) setAccountOpen(false);
|
|
6439
|
+
};
|
|
6440
|
+
const onKey = (event) => {
|
|
6441
|
+
if (event.key === "Escape") setAccountOpen(false);
|
|
6442
|
+
};
|
|
6443
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
6444
|
+
document.addEventListener("keydown", onKey);
|
|
6445
|
+
return () => {
|
|
6446
|
+
document.removeEventListener("pointerdown", onPointerDown);
|
|
6447
|
+
document.removeEventListener("keydown", onKey);
|
|
6448
|
+
};
|
|
6449
|
+
}, [accountOpen]);
|
|
6387
6450
|
return /* @__PURE__ */ jsxs("nav", { className: "flex items-center gap-1", children: [
|
|
6388
6451
|
/* @__PURE__ */ jsx(Slot, { area: "header", name: "beforeActions" }),
|
|
6389
|
-
config.features.accounts && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
6452
|
+
config.features.accounts && /* @__PURE__ */ jsxs("div", { ref: accountRef, className: "relative", children: [
|
|
6390
6453
|
session.email ? /* @__PURE__ */ jsxs(
|
|
6391
6454
|
"button",
|
|
6392
6455
|
{
|
|
@@ -6421,7 +6484,7 @@ function HeaderActions({ onMenuClick }) {
|
|
|
6421
6484
|
/* @__PURE__ */ jsx("p", { className: "font-semibold", children: session.name || firstName || "Cliente" }),
|
|
6422
6485
|
/* @__PURE__ */ jsx("p", { className: "truncate text-sm text-muted-foreground", children: session.email })
|
|
6423
6486
|
] }),
|
|
6424
|
-
/* @__PURE__ */ jsxs("div", { className: "p-2", children: [
|
|
6487
|
+
/* @__PURE__ */ jsxs("div", { className: "p-2", onClick: () => setAccountOpen(false), children: [
|
|
6425
6488
|
/* @__PURE__ */ jsxs(Link, { to: "/account", className: "flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold hover:bg-muted", children: [
|
|
6426
6489
|
/* @__PURE__ */ jsx(UserCircle, { className: "h-4 w-4" }),
|
|
6427
6490
|
"Perfil"
|
|
@@ -6898,6 +6961,10 @@ function CartDrawer() {
|
|
|
6898
6961
|
setDiscountError(result.message ?? "Cupom inv\xE1lido.");
|
|
6899
6962
|
toast.error("Cupom inv\xE1lido", result.message ?? "Verifique o c\xF3digo e tente novamente.");
|
|
6900
6963
|
}
|
|
6964
|
+
} catch (err) {
|
|
6965
|
+
setDiscountError("N\xE3o conseguimos validar esse cupom agora.");
|
|
6966
|
+
toast.error("N\xE3o conseguimos validar o cupom", "Tente de novo em alguns instantes.");
|
|
6967
|
+
console.warn("[cart] falha ao validar cupom", err);
|
|
6901
6968
|
} finally {
|
|
6902
6969
|
setApplying(false);
|
|
6903
6970
|
}
|
|
@@ -6921,11 +6988,9 @@ function CartDrawer() {
|
|
|
6921
6988
|
"data-testid": TID.cartDrawer,
|
|
6922
6989
|
role: "dialog",
|
|
6923
6990
|
"aria-label": "Carrinho",
|
|
6924
|
-
className:
|
|
6925
|
-
// it — an empty cart stays the normal narrow single-column panel.
|
|
6926
|
-
cart.lines.length > 0 ? "lg:max-w-3xl lg:grid lg:grid-cols-[55%_45%] lg:grid-rows-[auto_auto_1fr]" : ""}`,
|
|
6991
|
+
className: "absolute right-0 top-0 flex h-full w-full max-w-md animate-slide-in-from-right flex-col bg-background shadow-2xl",
|
|
6927
6992
|
children: [
|
|
6928
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-5 py-4
|
|
6993
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-5 py-4", children: [
|
|
6929
6994
|
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Carrinho" }),
|
|
6930
6995
|
/* @__PURE__ */ jsx(
|
|
6931
6996
|
"button",
|
|
@@ -6933,13 +6998,13 @@ function CartDrawer() {
|
|
|
6933
6998
|
type: "button",
|
|
6934
6999
|
"aria-label": "Fechar carrinho",
|
|
6935
7000
|
onClick: cart.closeDrawer,
|
|
6936
|
-
className: "rounded-full p-1.5 hover:bg-muted",
|
|
7001
|
+
className: "grid min-h-11 min-w-11 place-items-center rounded-full p-1.5 hover:bg-muted sm:min-h-0 sm:min-w-0",
|
|
6937
7002
|
children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
|
|
6938
7003
|
}
|
|
6939
7004
|
)
|
|
6940
7005
|
] }),
|
|
6941
7006
|
cart.lines.length === 0 ? /* @__PURE__ */ jsx("div", { "data-testid": TID.cartEmpty, className: "flex flex-1 items-center justify-center p-8 text-center text-muted-foreground", children: "Seu carrinho est\xE1 vazio." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6942
|
-
freeAbove != null && /* @__PURE__ */ jsxs("div", { "data-testid": TID.shippingProgress, className: "border-b bg-muted/40 px-5 py-3
|
|
7007
|
+
freeAbove != null && /* @__PURE__ */ jsxs("div", { "data-testid": TID.shippingProgress, className: "border-b bg-muted/40 px-5 py-3", children: [
|
|
6943
7008
|
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium", children: missingForFree > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6944
7009
|
"Faltam ",
|
|
6945
7010
|
/* @__PURE__ */ jsx("strong", { children: money(missingForFree) }),
|
|
@@ -6961,7 +7026,7 @@ function CartDrawer() {
|
|
|
6961
7026
|
"div",
|
|
6962
7027
|
{
|
|
6963
7028
|
"data-testid": TID.cartLinesList,
|
|
6964
|
-
className: "flex-1 space-y-4 overflow-y-auto py-4 pl-5 pr-6
|
|
7029
|
+
className: "flex-1 space-y-4 overflow-y-auto py-4 pl-5 pr-6",
|
|
6965
7030
|
style: { scrollbarGutter: "stable" },
|
|
6966
7031
|
children: [
|
|
6967
7032
|
/* @__PURE__ */ jsx(Slot, { area: "cart", name: "beforeLines", context: cartSlotContext }),
|
|
@@ -6984,7 +7049,7 @@ function CartDrawer() {
|
|
|
6984
7049
|
"data-testid": TID.lineRemove,
|
|
6985
7050
|
"aria-label": `Remover ${line.name}`,
|
|
6986
7051
|
onClick: () => cart.removeItem(line.lineId ?? line.productId),
|
|
6987
|
-
className: "-m-1.5 p-1.5 text-muted-foreground hover:text-destructive",
|
|
7052
|
+
className: "-m-1.5 grid min-h-11 min-w-11 place-items-center p-1.5 text-muted-foreground hover:text-destructive sm:min-h-0 sm:min-w-0",
|
|
6988
7053
|
children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
|
|
6989
7054
|
}
|
|
6990
7055
|
)
|
|
@@ -7025,7 +7090,7 @@ function CartDrawer() {
|
|
|
7025
7090
|
"div",
|
|
7026
7091
|
{
|
|
7027
7092
|
"data-testid": TID.cartFooter,
|
|
7028
|
-
className: "space-y-3 border-t px-5 py-4
|
|
7093
|
+
className: "space-y-3 border-t px-5 py-4",
|
|
7029
7094
|
children: [
|
|
7030
7095
|
config.features.discounts && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
7031
7096
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
@@ -7055,7 +7120,7 @@ function CartDrawer() {
|
|
|
7055
7120
|
discountError && /* @__PURE__ */ jsx("p", { "data-testid": TID.discountError, className: "text-xs text-destructive", children: discountError })
|
|
7056
7121
|
] }),
|
|
7057
7122
|
/* @__PURE__ */ jsx(Slot, { area: "cart", name: "beforeSummary", context: cartSlotContext }),
|
|
7058
|
-
/* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(DeliveryEstimator, { compact: true }) }),
|
|
7123
|
+
selectRequiresShipping(cart) && /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(DeliveryEstimator, { compact: true }) }),
|
|
7059
7124
|
/* @__PURE__ */ jsxs("dl", { className: "space-y-1.5 text-sm", children: [
|
|
7060
7125
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
7061
7126
|
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Subtotal" }),
|
|
@@ -7197,6 +7262,41 @@ async function storefrontPaths(input) {
|
|
|
7197
7262
|
const unique = Array.from(new Set(paths));
|
|
7198
7263
|
return input.includePrivate ? unique : unique.filter((p) => isPublicPath(resolved, p));
|
|
7199
7264
|
}
|
|
7265
|
+
|
|
7266
|
+
// src/data/local-orders.ts
|
|
7267
|
+
var KEY = "fayz.storefront.orders.v1";
|
|
7268
|
+
var MAX = 25;
|
|
7269
|
+
function read() {
|
|
7270
|
+
try {
|
|
7271
|
+
const raw = globalThis.localStorage?.getItem(KEY);
|
|
7272
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
7273
|
+
return Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : [];
|
|
7274
|
+
} catch {
|
|
7275
|
+
return [];
|
|
7276
|
+
}
|
|
7277
|
+
}
|
|
7278
|
+
function rememberLocalOrder(orderId) {
|
|
7279
|
+
if (!orderId) return;
|
|
7280
|
+
try {
|
|
7281
|
+
const next = [orderId, ...read().filter((id) => id !== orderId)].slice(0, MAX);
|
|
7282
|
+
globalThis.localStorage?.setItem(KEY, JSON.stringify(next));
|
|
7283
|
+
} catch {
|
|
7284
|
+
}
|
|
7285
|
+
}
|
|
7286
|
+
function listLocalOrderIds() {
|
|
7287
|
+
return read();
|
|
7288
|
+
}
|
|
7289
|
+
|
|
7290
|
+
// src/lib/errors.ts
|
|
7291
|
+
function errorMessage(err) {
|
|
7292
|
+
if (err instanceof Error) return err.message;
|
|
7293
|
+
if (err && typeof err === "object" && typeof err.message === "string") {
|
|
7294
|
+
return err.message;
|
|
7295
|
+
}
|
|
7296
|
+
return "";
|
|
7297
|
+
}
|
|
7298
|
+
|
|
7299
|
+
// src/workflows/checkout.ts
|
|
7200
7300
|
function formatDeliveryNotes(address, shippingAddress) {
|
|
7201
7301
|
if (address?.notes?.trim()) return address.notes.trim();
|
|
7202
7302
|
if (shippingAddress) return void 0;
|
|
@@ -7204,6 +7304,10 @@ function formatDeliveryNotes(address, shippingAddress) {
|
|
|
7204
7304
|
const parts = [address.street, address.city, address.zip].map((part) => part?.trim()).filter(Boolean);
|
|
7205
7305
|
return parts.length > 0 ? `Entrega: ${parts.join(", ")}` : void 0;
|
|
7206
7306
|
}
|
|
7307
|
+
function isStaleCustomerError(err) {
|
|
7308
|
+
const message = errorMessage(err).toLowerCase();
|
|
7309
|
+
return message.includes("customer email mismatch") || message.includes("customer does not belong to this store") || message.includes("customer belongs to another account");
|
|
7310
|
+
}
|
|
7207
7311
|
async function placeStorefrontOrder({
|
|
7208
7312
|
config,
|
|
7209
7313
|
cart,
|
|
@@ -7218,7 +7322,7 @@ async function placeStorefrontOrder({
|
|
|
7218
7322
|
const name = customer.name.trim();
|
|
7219
7323
|
const provider = getShopProvider();
|
|
7220
7324
|
let customerId = session?.customerId ?? null;
|
|
7221
|
-
if (!customerId || session?.email !== email) {
|
|
7325
|
+
if (!customerId || session?.email?.trim().toLowerCase() !== email) {
|
|
7222
7326
|
const established = await establishCustomerSession(email, { name });
|
|
7223
7327
|
customerId = established.customerId;
|
|
7224
7328
|
}
|
|
@@ -7251,8 +7355,8 @@ async function placeStorefrontOrder({
|
|
|
7251
7355
|
subtotal: cart.lines.reduce((sum, line) => sum + line.unitPrice * line.quantity, 0),
|
|
7252
7356
|
customerEmail: email
|
|
7253
7357
|
});
|
|
7254
|
-
const
|
|
7255
|
-
customerId:
|
|
7358
|
+
const place = (withCustomerId) => provider.placeOrder({
|
|
7359
|
+
customerId: withCustomerId,
|
|
7256
7360
|
customer: { name, email },
|
|
7257
7361
|
currency: config.currency,
|
|
7258
7362
|
notes,
|
|
@@ -7266,10 +7370,19 @@ async function placeStorefrontOrder({
|
|
|
7266
7370
|
metadata,
|
|
7267
7371
|
items
|
|
7268
7372
|
});
|
|
7373
|
+
let order;
|
|
7374
|
+
try {
|
|
7375
|
+
order = await place(customerId ?? void 0);
|
|
7376
|
+
} catch (err) {
|
|
7377
|
+
if (!customerId || !isStaleCustomerError(err)) throw err;
|
|
7378
|
+
customerId = null;
|
|
7379
|
+
order = await place(void 0);
|
|
7380
|
+
}
|
|
7269
7381
|
if (markPaid) {
|
|
7270
7382
|
if (provider.confirmPayment) await provider.confirmPayment(order.id);
|
|
7271
7383
|
else await provider.updateOrder(order.id, { financialStatus: "paid" });
|
|
7272
7384
|
}
|
|
7385
|
+
rememberLocalOrder(order.id);
|
|
7273
7386
|
return { order, customerId: customerId ?? order.customerId ?? "" };
|
|
7274
7387
|
}
|
|
7275
7388
|
function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
|
|
@@ -7382,40 +7495,365 @@ function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
|
|
|
7382
7495
|
}
|
|
7383
7496
|
);
|
|
7384
7497
|
}
|
|
7385
|
-
var
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
cash: { label: "Dinheiro", hint: "Pagamento na entrega" },
|
|
7391
|
-
other: { label: "Combinar com a loja", hint: "A loja entra em contato para acertar o pagamento" }
|
|
7498
|
+
var LABELS2 = {
|
|
7499
|
+
1: "Dados",
|
|
7500
|
+
2: "Entrega",
|
|
7501
|
+
3: "Pagamento",
|
|
7502
|
+
4: "Pronto"
|
|
7392
7503
|
};
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
{
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7504
|
+
function CheckoutSteps({ current, onGoBackTo }) {
|
|
7505
|
+
const steps = [1, 2, 3, 4];
|
|
7506
|
+
return /* @__PURE__ */ jsxs("nav", { "data-testid": TID.checkoutSteps, "aria-label": "Etapas do checkout", className: "mb-8", children: [
|
|
7507
|
+
/* @__PURE__ */ jsx("ol", { className: "flex items-center gap-1.5 sm:gap-2", children: steps.map((step, index) => {
|
|
7508
|
+
const done = step < current;
|
|
7509
|
+
const active2 = step === current;
|
|
7510
|
+
const canGoBack = done && !!onGoBackTo;
|
|
7511
|
+
const Tag = canGoBack ? "button" : "div";
|
|
7512
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex min-w-0 flex-1 items-center gap-1.5 sm:gap-2", children: [
|
|
7513
|
+
/* @__PURE__ */ jsxs(
|
|
7514
|
+
Tag,
|
|
7515
|
+
{
|
|
7516
|
+
...canGoBack ? { type: "button", onClick: () => onGoBackTo(step) } : {},
|
|
7517
|
+
"data-testid": TID.checkoutStep(step),
|
|
7518
|
+
"data-state": done ? "done" : active2 ? "current" : "pending",
|
|
7519
|
+
"aria-current": active2 ? "step" : void 0,
|
|
7520
|
+
className: [
|
|
7521
|
+
"flex min-w-0 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition",
|
|
7522
|
+
canGoBack ? "hover:bg-muted" : "",
|
|
7523
|
+
active2 ? "text-foreground" : done ? "text-foreground" : "text-muted-foreground"
|
|
7524
|
+
].join(" "),
|
|
7525
|
+
children: [
|
|
7526
|
+
/* @__PURE__ */ jsx(
|
|
7527
|
+
"span",
|
|
7528
|
+
{
|
|
7529
|
+
className: [
|
|
7530
|
+
"grid h-7 w-7 shrink-0 place-items-center rounded-full border text-xs font-semibold",
|
|
7531
|
+
done ? "border-primary bg-primary text-primary-foreground" : active2 ? "border-primary bg-background text-primary" : "border-muted-foreground/30 bg-background"
|
|
7532
|
+
].join(" "),
|
|
7533
|
+
children: done ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : step
|
|
7534
|
+
}
|
|
7535
|
+
),
|
|
7536
|
+
/* @__PURE__ */ jsx("span", { className: `hidden truncate text-sm sm:inline ${active2 ? "font-semibold" : ""}`, children: LABELS2[step] })
|
|
7537
|
+
]
|
|
7538
|
+
}
|
|
7539
|
+
),
|
|
7540
|
+
index < steps.length - 1 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: `h-px flex-1 ${done ? "bg-primary" : "bg-border"}` })
|
|
7541
|
+
] }, step);
|
|
7542
|
+
}) }),
|
|
7543
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-2 text-sm font-semibold sm:hidden", children: [
|
|
7544
|
+
current,
|
|
7545
|
+
". ",
|
|
7546
|
+
LABELS2[current]
|
|
7547
|
+
] })
|
|
7548
|
+
] });
|
|
7549
|
+
}
|
|
7550
|
+
function Skeleton({ className = "", style }) {
|
|
7551
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: `block rounded bg-muted ${className}`, style });
|
|
7552
|
+
}
|
|
7553
|
+
function SkeletonGroup({ label, className = "", children, ...rest }) {
|
|
7401
7554
|
return /* @__PURE__ */ jsxs(
|
|
7402
7555
|
"div",
|
|
7403
7556
|
{
|
|
7404
|
-
"data-testid": TID.checkoutProcessing,
|
|
7405
|
-
className: "fixed inset-0 z-50 flex animate-fade-in flex-col items-center justify-center bg-background/95 backdrop-blur-sm",
|
|
7406
7557
|
role: "status",
|
|
7407
|
-
"aria-
|
|
7558
|
+
"aria-busy": "true",
|
|
7559
|
+
className: `animate-pulse motion-reduce:animate-none ${className}`,
|
|
7560
|
+
...rest,
|
|
7408
7561
|
children: [
|
|
7409
|
-
/* @__PURE__ */
|
|
7410
|
-
|
|
7411
|
-
/* @__PURE__ */ jsx(Icon2, { className: "h-9 w-9 animate-pop-in text-primary" }, step)
|
|
7412
|
-
] }),
|
|
7413
|
-
/* @__PURE__ */ jsx("p", { className: "mt-6 animate-fade-up font-medium", children: current.label }, `label-${step}`),
|
|
7414
|
-
/* @__PURE__ */ jsx("p", { className: "mt-8 text-xs text-muted-foreground", children: "Pagamento seguro - n\xE3o feche esta janela" })
|
|
7562
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
|
|
7563
|
+
children
|
|
7415
7564
|
]
|
|
7416
7565
|
}
|
|
7417
7566
|
);
|
|
7418
7567
|
}
|
|
7568
|
+
function PulseDot({ className = "" }) {
|
|
7569
|
+
return /* @__PURE__ */ jsxs("span", { "aria-hidden": true, className: `relative flex h-2.5 w-2.5 shrink-0 ${className}`, children: [
|
|
7570
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 animate-ping rounded-full bg-current opacity-60 motion-reduce:animate-none" }),
|
|
7571
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-current" })
|
|
7572
|
+
] });
|
|
7573
|
+
}
|
|
7574
|
+
var POLL_MS = 3e3;
|
|
7575
|
+
function CheckoutPayment({ input, onPaid, onBack, money, onUnavailable }) {
|
|
7576
|
+
const [phase, setPhase] = useState("opening");
|
|
7577
|
+
const [charge, setCharge] = useState(null);
|
|
7578
|
+
const [total, setTotal] = useState(null);
|
|
7579
|
+
const [sessionId, setSessionId] = useState(null);
|
|
7580
|
+
const [provider, setProvider] = useState(null);
|
|
7581
|
+
const [error, setError] = useState(null);
|
|
7582
|
+
const [copied, setCopied] = useState(false);
|
|
7583
|
+
const [checking, setChecking] = useState(false);
|
|
7584
|
+
const settled = useRef(false);
|
|
7585
|
+
const opened = useRef(false);
|
|
7586
|
+
const open = useCallback(async () => {
|
|
7587
|
+
const shop = getShopProvider();
|
|
7588
|
+
if (!shop.openCheckoutSession) {
|
|
7589
|
+
setError("Esta loja ainda n\xE3o recebe pagamento online.");
|
|
7590
|
+
setPhase("error");
|
|
7591
|
+
return;
|
|
7592
|
+
}
|
|
7593
|
+
setPhase("opening");
|
|
7594
|
+
setError(null);
|
|
7595
|
+
try {
|
|
7596
|
+
const session = await shop.openCheckoutSession(input);
|
|
7597
|
+
setSessionId(session.sessionId);
|
|
7598
|
+
setProvider(session.provider ?? input.provider ?? null);
|
|
7599
|
+
setTotal(session.total);
|
|
7600
|
+
setCharge(session.charge);
|
|
7601
|
+
setPhase(session.charge ? "waiting" : "error");
|
|
7602
|
+
if (!session.charge) setError("N\xE3o conseguimos abrir a cobran\xE7a agora.");
|
|
7603
|
+
} catch (err) {
|
|
7604
|
+
if (err?.status === 404) {
|
|
7605
|
+
onUnavailable?.();
|
|
7606
|
+
return;
|
|
7607
|
+
}
|
|
7608
|
+
setError(err instanceof Error ? err.message : "N\xE3o conseguimos abrir a cobran\xE7a agora.");
|
|
7609
|
+
setPhase("error");
|
|
7610
|
+
}
|
|
7611
|
+
}, [input, onUnavailable]);
|
|
7612
|
+
useEffect(() => {
|
|
7613
|
+
if (opened.current) return;
|
|
7614
|
+
opened.current = true;
|
|
7615
|
+
void open();
|
|
7616
|
+
}, [open]);
|
|
7617
|
+
const check = useCallback(async () => {
|
|
7618
|
+
if (!sessionId || settled.current) return false;
|
|
7619
|
+
const shop = getShopProvider();
|
|
7620
|
+
if (!shop.settleCheckoutSession) return false;
|
|
7621
|
+
try {
|
|
7622
|
+
const result = await shop.settleCheckoutSession(sessionId, provider);
|
|
7623
|
+
if (settled.current) return false;
|
|
7624
|
+
if (result.state === "paid" && result.orderId) {
|
|
7625
|
+
settled.current = true;
|
|
7626
|
+
setPhase("settling");
|
|
7627
|
+
onPaid(result.orderId);
|
|
7628
|
+
return true;
|
|
7629
|
+
}
|
|
7630
|
+
if (result.state === "failed" || result.state === "expired") {
|
|
7631
|
+
setError(
|
|
7632
|
+
result.reason ?? (result.state === "expired" ? "O c\xF3digo expirou. Gere um novo para continuar." : "O pagamento n\xE3o foi conclu\xEDdo.")
|
|
7633
|
+
);
|
|
7634
|
+
setPhase("error");
|
|
7635
|
+
}
|
|
7636
|
+
return false;
|
|
7637
|
+
} catch {
|
|
7638
|
+
return false;
|
|
7639
|
+
}
|
|
7640
|
+
}, [sessionId, provider, onPaid]);
|
|
7641
|
+
useEffect(() => {
|
|
7642
|
+
if (phase !== "waiting" || !sessionId) return;
|
|
7643
|
+
let stopped = false;
|
|
7644
|
+
const timer = setInterval(() => {
|
|
7645
|
+
if (!stopped) void check();
|
|
7646
|
+
}, POLL_MS);
|
|
7647
|
+
return () => {
|
|
7648
|
+
stopped = true;
|
|
7649
|
+
clearInterval(timer);
|
|
7650
|
+
};
|
|
7651
|
+
}, [phase, sessionId, check]);
|
|
7652
|
+
async function confirmPaid() {
|
|
7653
|
+
setChecking(true);
|
|
7654
|
+
const paid = await check();
|
|
7655
|
+
if (!paid) setChecking(false);
|
|
7656
|
+
}
|
|
7657
|
+
async function copy() {
|
|
7658
|
+
if (!charge?.emv) return;
|
|
7659
|
+
try {
|
|
7660
|
+
await navigator.clipboard.writeText(charge.emv);
|
|
7661
|
+
setCopied(true);
|
|
7662
|
+
setTimeout(() => setCopied(false), 2400);
|
|
7663
|
+
} catch {
|
|
7664
|
+
toast.error("Copie o c\xF3digo manualmente");
|
|
7665
|
+
}
|
|
7666
|
+
}
|
|
7667
|
+
if (phase === "opening") {
|
|
7668
|
+
return (
|
|
7669
|
+
// Same layout as the charge below, so nothing jumps when it arrives.
|
|
7670
|
+
/* @__PURE__ */ jsxs(
|
|
7671
|
+
SkeletonGroup,
|
|
7672
|
+
{
|
|
7673
|
+
label: "Preparando seu pagamento",
|
|
7674
|
+
"data-testid": TID.checkoutPayment,
|
|
7675
|
+
className: "rounded-xl border bg-card p-6",
|
|
7676
|
+
children: [
|
|
7677
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
7678
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-5 rounded-md" }),
|
|
7679
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-64 max-w-[70%]" })
|
|
7680
|
+
] }),
|
|
7681
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3.5 w-full max-w-sm" }),
|
|
7682
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 grid gap-5 sm:grid-cols-[auto_minmax(0,1fr)] sm:items-start", children: [
|
|
7683
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mx-auto h-44 w-44 rounded-lg" }),
|
|
7684
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
7685
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" }),
|
|
7686
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-4 h-3 w-28" }),
|
|
7687
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1 h-16 w-full rounded-lg" }),
|
|
7688
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-3", children: [
|
|
7689
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-11 w-40 rounded-lg" }),
|
|
7690
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-11 w-32 rounded-lg" })
|
|
7691
|
+
] })
|
|
7692
|
+
] })
|
|
7693
|
+
] })
|
|
7694
|
+
]
|
|
7695
|
+
}
|
|
7696
|
+
)
|
|
7697
|
+
);
|
|
7698
|
+
}
|
|
7699
|
+
if (phase === "error") {
|
|
7700
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": TID.checkoutPayment, className: "space-y-3 rounded-xl border bg-card p-6", children: [
|
|
7701
|
+
/* @__PURE__ */ 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 ?? "N\xE3o conseguimos abrir a cobran\xE7a agora." }),
|
|
7702
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
7703
|
+
/* @__PURE__ */ jsx(
|
|
7704
|
+
"button",
|
|
7705
|
+
{
|
|
7706
|
+
type: "button",
|
|
7707
|
+
onClick: () => {
|
|
7708
|
+
opened.current = false;
|
|
7709
|
+
void open();
|
|
7710
|
+
},
|
|
7711
|
+
className: "rounded-lg bg-primary px-4 py-2.5 text-sm font-semibold text-primary-foreground transition hover:opacity-90",
|
|
7712
|
+
children: "Tentar de novo"
|
|
7713
|
+
}
|
|
7714
|
+
),
|
|
7715
|
+
/* @__PURE__ */ jsx(
|
|
7716
|
+
"button",
|
|
7717
|
+
{
|
|
7718
|
+
type: "button",
|
|
7719
|
+
onClick: onBack,
|
|
7720
|
+
className: "rounded-lg border px-4 py-2.5 text-sm font-medium transition hover:bg-muted",
|
|
7721
|
+
children: "Voltar"
|
|
7722
|
+
}
|
|
7723
|
+
)
|
|
7724
|
+
] })
|
|
7725
|
+
] });
|
|
7726
|
+
}
|
|
7727
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": TID.checkoutPayment, className: "space-y-4", "aria-live": "polite", children: [
|
|
7728
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-card p-6", children: [
|
|
7729
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
7730
|
+
/* @__PURE__ */ jsx(QrCode, { className: "h-5 w-5 text-primary" }),
|
|
7731
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Pague com Pix para fechar o pedido" })
|
|
7732
|
+
] }),
|
|
7733
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Seu pedido \xE9 criado assim que o pagamento entrar. Pode deixar esta tela aberta." }),
|
|
7734
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 grid gap-5 sm:grid-cols-[auto_minmax(0,1fr)] sm:items-start", children: [
|
|
7735
|
+
charge?.qrcodeBase64 && /* @__PURE__ */ jsx(
|
|
7736
|
+
"img",
|
|
7737
|
+
{
|
|
7738
|
+
src: `data:image/png;base64,${charge.qrcodeBase64}`,
|
|
7739
|
+
alt: "QR code do Pix",
|
|
7740
|
+
className: "mx-auto h-44 w-44 rounded-lg border bg-white p-2"
|
|
7741
|
+
}
|
|
7742
|
+
),
|
|
7743
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
7744
|
+
total != null && /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
7745
|
+
"Valor: ",
|
|
7746
|
+
/* @__PURE__ */ jsx("strong", { "data-testid": TID.checkoutPaymentTotal, children: money(total) })
|
|
7747
|
+
] }),
|
|
7748
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Pix copia e cola" }),
|
|
7749
|
+
/* @__PURE__ */ jsx(
|
|
7750
|
+
"p",
|
|
7751
|
+
{
|
|
7752
|
+
"data-testid": TID.pixCode,
|
|
7753
|
+
className: "mt-1 max-h-24 overflow-y-auto break-all rounded-lg border bg-muted/40 p-3 font-mono text-xs",
|
|
7754
|
+
children: charge?.emv
|
|
7755
|
+
}
|
|
7756
|
+
),
|
|
7757
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-3", children: [
|
|
7758
|
+
/* @__PURE__ */ jsxs(
|
|
7759
|
+
"button",
|
|
7760
|
+
{
|
|
7761
|
+
type: "button",
|
|
7762
|
+
onClick: copy,
|
|
7763
|
+
"data-testid": TID.pixCopy,
|
|
7764
|
+
className: "inline-flex min-h-11 items-center gap-2 rounded-lg bg-primary px-4 py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90",
|
|
7765
|
+
children: [
|
|
7766
|
+
copied ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" }),
|
|
7767
|
+
copied ? "C\xF3digo copiado" : "Copiar c\xF3digo"
|
|
7768
|
+
]
|
|
7769
|
+
}
|
|
7770
|
+
),
|
|
7771
|
+
/* @__PURE__ */ jsxs(
|
|
7772
|
+
"button",
|
|
7773
|
+
{
|
|
7774
|
+
type: "button",
|
|
7775
|
+
onClick: () => {
|
|
7776
|
+
void confirmPaid();
|
|
7777
|
+
},
|
|
7778
|
+
disabled: checking || phase === "settling",
|
|
7779
|
+
"data-testid": TID.pixConfirm,
|
|
7780
|
+
className: "inline-flex min-h-11 items-center gap-2 rounded-lg border border-primary px-4 py-2.5 text-sm font-semibold text-primary transition hover:bg-primary/5 disabled:opacity-60",
|
|
7781
|
+
children: [
|
|
7782
|
+
checking || phase === "settling" ? /* @__PURE__ */ jsx(PulseDot, {}) : /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }),
|
|
7783
|
+
phase === "settling" ? "Confirmando\u2026" : checking ? "Verificando\u2026" : "J\xE1 paguei"
|
|
7784
|
+
]
|
|
7785
|
+
}
|
|
7786
|
+
)
|
|
7787
|
+
] })
|
|
7788
|
+
] })
|
|
7789
|
+
] })
|
|
7790
|
+
] }),
|
|
7791
|
+
/* @__PURE__ */ jsx(
|
|
7792
|
+
"button",
|
|
7793
|
+
{
|
|
7794
|
+
type: "button",
|
|
7795
|
+
onClick: onBack,
|
|
7796
|
+
className: "text-sm text-muted-foreground underline hover:text-foreground",
|
|
7797
|
+
children: "Voltar e revisar o pedido"
|
|
7798
|
+
}
|
|
7799
|
+
)
|
|
7800
|
+
] });
|
|
7801
|
+
}
|
|
7802
|
+
var CONFETTI_COLORS = ["hsl(var(--primary))", "#f59e0b", "#10b981", "#3b82f6", "#ec4899"];
|
|
7803
|
+
function SuccessMark() {
|
|
7804
|
+
const reduced = prefersReducedMotion();
|
|
7805
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative mx-auto h-24 w-24", children: [
|
|
7806
|
+
!reduced && Array.from({ length: 14 }, (_, i) => /* @__PURE__ */ jsx(
|
|
7807
|
+
"span",
|
|
7808
|
+
{
|
|
7809
|
+
"aria-hidden": true,
|
|
7810
|
+
className: "absolute h-2 w-2 animate-confetti-fall rounded-sm",
|
|
7811
|
+
style: {
|
|
7812
|
+
left: `${i * 7.3 % 100}%`,
|
|
7813
|
+
top: "-4px",
|
|
7814
|
+
backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],
|
|
7815
|
+
animationDelay: `${200 + i % 7 * 90}ms`
|
|
7816
|
+
}
|
|
7817
|
+
},
|
|
7818
|
+
i
|
|
7819
|
+
)),
|
|
7820
|
+
/* @__PURE__ */ jsx("span", { className: `absolute inset-0 rounded-full bg-emerald-100 ${reduced ? "" : "animate-pop-in"}` }),
|
|
7821
|
+
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 48 48", className: "absolute inset-0 h-full w-full p-6", "aria-hidden": true, children: /* @__PURE__ */ jsx(
|
|
7822
|
+
"path",
|
|
7823
|
+
{
|
|
7824
|
+
d: "M10 25 L20 35 L38 14",
|
|
7825
|
+
fill: "none",
|
|
7826
|
+
stroke: "rgb(5 150 105)",
|
|
7827
|
+
strokeWidth: "5",
|
|
7828
|
+
strokeLinecap: "round",
|
|
7829
|
+
strokeLinejoin: "round",
|
|
7830
|
+
strokeDasharray: "48",
|
|
7831
|
+
className: reduced ? "" : "animate-draw-check"
|
|
7832
|
+
}
|
|
7833
|
+
) })
|
|
7834
|
+
] });
|
|
7835
|
+
}
|
|
7836
|
+
var PAYMENT_LABELS = {
|
|
7837
|
+
pix: { label: "Pix", hint: "Voc\xEA recebe a chave para pagar ap\xF3s confirmar o pedido" },
|
|
7838
|
+
credit_card: { label: "Cart\xE3o de cr\xE9dito", hint: "Maquininha na entrega" },
|
|
7839
|
+
debit_card: { label: "Cart\xE3o de d\xE9bito", hint: "Maquininha na entrega" },
|
|
7840
|
+
boleto: { label: "Boleto", hint: "Enviado por e-mail ap\xF3s a confirma\xE7\xE3o" },
|
|
7841
|
+
cash: { label: "Dinheiro", hint: "Pagamento na entrega" },
|
|
7842
|
+
other: { label: "Combinar com a loja", hint: "A loja entra em contato para acertar o pagamento" }
|
|
7843
|
+
};
|
|
7844
|
+
function placeOrderMessage(err) {
|
|
7845
|
+
const message = errorMessage(err);
|
|
7846
|
+
if (/customer (email mismatch|does not belong|belongs to another)/i.test(message)) {
|
|
7847
|
+
return "Sua sess\xE3o ficou presa a outro cadastro. Recarregue a p\xE1gina e tente de novo.";
|
|
7848
|
+
}
|
|
7849
|
+
if (/out of stock|insufficient|estoque/i.test(message)) {
|
|
7850
|
+
return "Um item do carrinho ficou sem estoque. Revise o carrinho e tente de novo.";
|
|
7851
|
+
}
|
|
7852
|
+
if (/delivery not available|postal code/i.test(message)) {
|
|
7853
|
+
return "Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.";
|
|
7854
|
+
}
|
|
7855
|
+
return message.replace(/^shop_[a-z_]+:\s*/i, "") || "N\xE3o foi poss\xEDvel concluir o pedido.";
|
|
7856
|
+
}
|
|
7419
7857
|
function field(label, testId, value, onChange, props) {
|
|
7420
7858
|
return /* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
7421
7859
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
|
|
@@ -7440,13 +7878,22 @@ function CheckoutPage() {
|
|
|
7440
7878
|
const validateDiscount2 = useDiscountValidator();
|
|
7441
7879
|
useStorefrontHead({ title: `Checkout \u2014 ${config.name}` });
|
|
7442
7880
|
const paymentMethods = config.payments.methods;
|
|
7881
|
+
const policy = config.policy;
|
|
7882
|
+
const requiresAccount = policy?.guestCheckout === false;
|
|
7883
|
+
const fieldPolicy = policy?.checkoutFields ?? {};
|
|
7884
|
+
const gatewayCheckout = config.payments.mode === "gateway" || !!policy?.paymentProvider;
|
|
7885
|
+
const shows = (key) => key === "document" ? gatewayCheckout || fieldPolicy.document?.visible === true : fieldPolicy[key]?.visible !== false;
|
|
7886
|
+
const requires = (key) => key === "document" ? gatewayCheckout || fieldPolicy.document?.visible === true && fieldPolicy.document?.required === true : fieldPolicy[key]?.visible !== false && fieldPolicy[key]?.required === true;
|
|
7443
7887
|
const [selectedAddressId, setSelectedAddressId] = useState("new");
|
|
7444
7888
|
const [savedAddresses, setSavedAddresses] = useState([]);
|
|
7889
|
+
const [addressesLoading, setAddressesLoading] = useState(false);
|
|
7445
7890
|
const [addressMode, setAddressMode] = useState("new");
|
|
7446
7891
|
const [paymentMethod, setPaymentMethod] = useState(paymentMethods[0] ?? "pix");
|
|
7447
7892
|
const [form, setForm] = useState({
|
|
7448
7893
|
email: session.email ?? "",
|
|
7449
7894
|
name: session.name ?? "",
|
|
7895
|
+
phone: "",
|
|
7896
|
+
document: "",
|
|
7450
7897
|
street: "",
|
|
7451
7898
|
city: "",
|
|
7452
7899
|
zip: "",
|
|
@@ -7455,33 +7902,69 @@ function CheckoutPage() {
|
|
|
7455
7902
|
district: "",
|
|
7456
7903
|
state: ""
|
|
7457
7904
|
});
|
|
7905
|
+
const [step, setStep] = useState(1);
|
|
7906
|
+
const [paidOrderId, setPaidOrderId] = useState(null);
|
|
7458
7907
|
const [error, setError] = useState(null);
|
|
7459
7908
|
const [discountCode, setDiscountCode] = useState("");
|
|
7460
7909
|
const [discountError, setDiscountError] = useState(null);
|
|
7461
7910
|
const [discountSuccess, setDiscountSuccess] = useState(null);
|
|
7462
7911
|
const [applyingDiscount, setApplyingDiscount] = useState(false);
|
|
7463
|
-
const [placing, setPlacing] = useState(false);
|
|
7464
|
-
const [processingStep, setProcessingStep] = useState(0);
|
|
7465
7912
|
const [showSignin, setShowSignin] = useState(false);
|
|
7913
|
+
const [placing, setPlacing] = useState(false);
|
|
7914
|
+
const leaving = React4.useRef(false);
|
|
7466
7915
|
const [zipLookup, setZipLookup] = useState("idle");
|
|
7467
7916
|
const subtotal = selectSubtotal(cart);
|
|
7468
7917
|
const discountTotal = selectDiscountTotal(cart);
|
|
7469
7918
|
const shipping = selectShipping(cart, config);
|
|
7470
7919
|
const total = selectTotal(cart, config);
|
|
7471
7920
|
const checkoutSlotContext = { subtotal, total };
|
|
7921
|
+
const checkoutSessionInput = React4.useMemo(() => ({
|
|
7922
|
+
items: cart.lines.map((line) => ({
|
|
7923
|
+
productId: line.productId,
|
|
7924
|
+
variantId: line.variantId ?? null,
|
|
7925
|
+
quantity: line.quantity,
|
|
7926
|
+
optionsLabel: line.optionsLabel ?? null
|
|
7927
|
+
})),
|
|
7928
|
+
customer: { name: form.name.trim(), email: form.email.trim() },
|
|
7929
|
+
shippingAddress: {
|
|
7930
|
+
postalCode: form.zip.trim(),
|
|
7931
|
+
street: form.street.trim(),
|
|
7932
|
+
number: form.number.trim() || void 0,
|
|
7933
|
+
complement: form.complement.trim() || void 0,
|
|
7934
|
+
district: form.district.trim() || void 0,
|
|
7935
|
+
city: form.city.trim(),
|
|
7936
|
+
state: form.state.trim().toUpperCase(),
|
|
7937
|
+
phone: form.phone.trim() || void 0,
|
|
7938
|
+
document: documentDigits(form.document) || void 0
|
|
7939
|
+
},
|
|
7940
|
+
discountCode: cart.discountCode ?? null,
|
|
7941
|
+
paymentMethod,
|
|
7942
|
+
// Qual adquirente cobra. Sai dos ajustes da loja e não de uma escolha do
|
|
7943
|
+
// comprador — a função reconfere de qualquer forma, mas mandar daqui é o
|
|
7944
|
+
// que faz o clique bater na função do gateway que a loja realmente conectou.
|
|
7945
|
+
provider: policy?.paymentProvider ?? null
|
|
7946
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7947
|
+
}), [cart.lines, cart.discountCode, form, paymentMethod, policy?.paymentProvider]);
|
|
7948
|
+
const [acquirerMissing, setAcquirerMissing] = useState(false);
|
|
7949
|
+
const chargesOnline = paymentMethod === "pix" && !acquirerMissing && (!policy?.paymentProvider || isChargeableProvider(policy.paymentProvider));
|
|
7472
7950
|
const money = (value) => formatMoney(value, config.currency, config.locale);
|
|
7473
7951
|
useEffect(() => {
|
|
7474
|
-
if (cart.lines.length === 0 && !
|
|
7475
|
-
}, [cart.lines.length, config.catalogPath,
|
|
7476
|
-
const set = (key) => (value) =>
|
|
7952
|
+
if (cart.lines.length === 0 && !paidOrderId && !leaving.current) navigateTo(config.catalogPath);
|
|
7953
|
+
}, [cart.lines.length, config.catalogPath, paidOrderId]);
|
|
7954
|
+
const set = (key) => (value) => {
|
|
7955
|
+
setError(null);
|
|
7956
|
+
setForm((current) => ({ ...current, [key]: value }));
|
|
7957
|
+
};
|
|
7477
7958
|
async function setZip(value) {
|
|
7478
7959
|
const masked = formatPostalCode(value);
|
|
7960
|
+
setError(null);
|
|
7479
7961
|
setForm((current) => ({ ...current, zip: masked }));
|
|
7480
7962
|
if (normalizePostalCode(masked).length !== 8) {
|
|
7481
7963
|
setZipLookup("idle");
|
|
7482
7964
|
return;
|
|
7483
7965
|
}
|
|
7484
7966
|
setZipLookup("loading");
|
|
7967
|
+
void delivery.resolve(masked, subtotal);
|
|
7485
7968
|
try {
|
|
7486
7969
|
const found = await lookupPostalCode(masked);
|
|
7487
7970
|
if (!found) {
|
|
@@ -7531,15 +8014,24 @@ function CheckoutPage() {
|
|
|
7531
8014
|
if (!customerId) {
|
|
7532
8015
|
setSavedAddresses([]);
|
|
7533
8016
|
setAddressMode("new");
|
|
8017
|
+
setAddressesLoading(false);
|
|
7534
8018
|
return;
|
|
7535
8019
|
}
|
|
7536
8020
|
let cancelled = false;
|
|
7537
8021
|
const provider = getShopProvider();
|
|
7538
|
-
|
|
8022
|
+
const listAddresses = provider.listCustomerAddresses?.bind(provider);
|
|
8023
|
+
if (!listAddresses) {
|
|
8024
|
+
setAddressesLoading(false);
|
|
8025
|
+
return;
|
|
8026
|
+
}
|
|
8027
|
+
setAddressesLoading(true);
|
|
8028
|
+
void listAddresses(customerId).then((addresses) => {
|
|
7539
8029
|
if (cancelled || addresses.length === 0) return;
|
|
7540
8030
|
setSavedAddresses(addresses);
|
|
7541
8031
|
applySavedAddress(addresses[0]);
|
|
7542
8032
|
}).catch(() => {
|
|
8033
|
+
}).finally(() => {
|
|
8034
|
+
if (!cancelled) setAddressesLoading(false);
|
|
7543
8035
|
});
|
|
7544
8036
|
return () => {
|
|
7545
8037
|
cancelled = true;
|
|
@@ -7572,7 +8064,11 @@ function CheckoutPage() {
|
|
|
7572
8064
|
number: address.number ?? "",
|
|
7573
8065
|
complement: address.complement ?? "",
|
|
7574
8066
|
district: address.district ?? "",
|
|
7575
|
-
state: address.state
|
|
8067
|
+
state: address.state,
|
|
8068
|
+
// Kept from the book when it has one, and NOT cleared when it does not:
|
|
8069
|
+
// a shopper who just typed their phone should not lose it by picking a
|
|
8070
|
+
// saved address that predates the field.
|
|
8071
|
+
phone: address.phone ?? current.phone
|
|
7576
8072
|
}));
|
|
7577
8073
|
}
|
|
7578
8074
|
function selectAddress(addressId) {
|
|
@@ -7584,44 +8080,69 @@ function CheckoutPage() {
|
|
|
7584
8080
|
setSelectedAddressId("new");
|
|
7585
8081
|
setForm((current) => ({ ...current, street: "", city: "", zip: "", number: "", complement: "", district: "", state: "" }));
|
|
7586
8082
|
}
|
|
7587
|
-
function
|
|
8083
|
+
function fail(message, testId) {
|
|
8084
|
+
setError(message);
|
|
8085
|
+
if (testId) {
|
|
8086
|
+
const field2 = document.querySelector(`[data-testid="${testId}"]`);
|
|
8087
|
+
field2?.scrollIntoView({ block: "center", behavior: prefersReducedMotion() ? "auto" : "smooth" });
|
|
8088
|
+
field2?.focus({ preventScroll: true });
|
|
8089
|
+
}
|
|
8090
|
+
return false;
|
|
8091
|
+
}
|
|
8092
|
+
function validateContact() {
|
|
7588
8093
|
setError(null);
|
|
8094
|
+
if (requiresAccount && !session.customerId) {
|
|
8095
|
+
setShowSignin(true);
|
|
8096
|
+
return fail("Esta loja pede uma conta para finalizar a compra. Entre ou crie a sua.");
|
|
8097
|
+
}
|
|
7589
8098
|
if (!form.email.trim() || !/.+@.+\..+/.test(form.email)) {
|
|
7590
|
-
|
|
7591
|
-
return false;
|
|
8099
|
+
return fail("Informe um e-mail v\xE1lido.", TID.checkoutEmail);
|
|
7592
8100
|
}
|
|
7593
|
-
if (!form.name.trim())
|
|
7594
|
-
|
|
7595
|
-
return
|
|
8101
|
+
if (!form.name.trim()) return fail("Informe seu nome completo.", TID.checkoutName);
|
|
8102
|
+
if (requires("phone") && !form.phone.trim()) {
|
|
8103
|
+
return fail("Informe um telefone de contato.", "checkout-phone");
|
|
8104
|
+
}
|
|
8105
|
+
if (requires("document") && !isValidDocument(form.document)) {
|
|
8106
|
+
return fail(
|
|
8107
|
+
form.document.trim() ? "CPF ou CNPJ inv\xE1lido \u2014 confira os n\xFAmeros." : "Informe o CPF ou CNPJ do comprador.",
|
|
8108
|
+
TID.checkoutDocument
|
|
8109
|
+
);
|
|
7596
8110
|
}
|
|
8111
|
+
return true;
|
|
8112
|
+
}
|
|
8113
|
+
function validateDelivery() {
|
|
8114
|
+
setError(null);
|
|
8115
|
+
if (!selectRequiresShipping(cart)) return true;
|
|
7597
8116
|
if (!form.street.trim() || !form.city.trim() || !form.zip.trim() || !form.district.trim()) {
|
|
7598
|
-
|
|
7599
|
-
return false;
|
|
8117
|
+
return fail("Preencha ou selecione o endere\xE7o de entrega.", TID.checkoutStreet);
|
|
7600
8118
|
}
|
|
7601
|
-
if (!form.number.trim())
|
|
7602
|
-
|
|
7603
|
-
return
|
|
8119
|
+
if (!form.number.trim()) return fail("Falta o n\xFAmero do endere\xE7o.", "checkout-number");
|
|
8120
|
+
if (requires("complement") && !form.complement.trim()) {
|
|
8121
|
+
return fail("Informe o complemento (apto, bloco).", "checkout-complement");
|
|
7604
8122
|
}
|
|
7605
|
-
if (form.state.trim()
|
|
7606
|
-
|
|
7607
|
-
return false;
|
|
8123
|
+
if (!/^[A-Za-z]{2}$/.test(form.state.trim())) {
|
|
8124
|
+
return fail("Informe a UF com duas letras (ex.: RJ).", "checkout-state");
|
|
7608
8125
|
}
|
|
7609
8126
|
if (delivery.status === "unserved" && delivery.postalCode === normalizePostalCode(form.zip)) {
|
|
7610
|
-
|
|
7611
|
-
|
|
8127
|
+
return fail(
|
|
8128
|
+
delivery.refusedZone ? "Esta loja n\xE3o entrega nesse endere\xE7o. Tente outro." : "Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.",
|
|
8129
|
+
TID.checkoutZip
|
|
8130
|
+
);
|
|
7612
8131
|
}
|
|
7613
8132
|
return true;
|
|
7614
8133
|
}
|
|
7615
|
-
|
|
8134
|
+
function goToStep(next) {
|
|
8135
|
+
setError(null);
|
|
8136
|
+
setStep(next);
|
|
8137
|
+
if (typeof window !== "undefined") window.scrollTo({ top: 0, behavior: "smooth" });
|
|
8138
|
+
}
|
|
8139
|
+
function advance() {
|
|
8140
|
+
if (step === 1 && validateContact()) goToStep(2);
|
|
8141
|
+
else if (step === 2 && validateDelivery()) goToStep(3);
|
|
8142
|
+
}
|
|
8143
|
+
async function confirmWithoutCharge() {
|
|
7616
8144
|
if (!validate()) return;
|
|
7617
8145
|
setPlacing(true);
|
|
7618
|
-
setProcessingStep(0);
|
|
7619
|
-
const stepMs = prefersReducedMotion() ? 120 : 600;
|
|
7620
|
-
const stepTimers = [
|
|
7621
|
-
setTimeout(() => setProcessingStep(1), stepMs),
|
|
7622
|
-
setTimeout(() => setProcessingStep(2), stepMs * 2)
|
|
7623
|
-
];
|
|
7624
|
-
const minDuration = sleep(stepMs * 2);
|
|
7625
8146
|
try {
|
|
7626
8147
|
const { order, customerId } = await placeStorefrontOrder({
|
|
7627
8148
|
config,
|
|
@@ -7636,31 +8157,67 @@ function CheckoutPage() {
|
|
|
7636
8157
|
complement: form.complement.trim() || void 0,
|
|
7637
8158
|
district: form.district.trim() || void 0,
|
|
7638
8159
|
city: form.city.trim(),
|
|
7639
|
-
state: form.state.trim().toUpperCase()
|
|
8160
|
+
state: form.state.trim().toUpperCase(),
|
|
8161
|
+
phone: form.phone.trim() || void 0,
|
|
8162
|
+
// Viaja no endereço porque a assinatura do shop_place_order não pode
|
|
8163
|
+
// ganhar parâmetro sem DROP (0050); uma trigger o levanta para a
|
|
8164
|
+
// coluna e o tira do jsonb, então nunca vira linha de endereço.
|
|
8165
|
+
document: documentDigits(form.document) || void 0
|
|
7640
8166
|
},
|
|
7641
|
-
// The method the buyer actually chose, which opens the ledger row in
|
|
7642
|
-
// public.transactions with the right kind.
|
|
7643
8167
|
paymentMethod
|
|
7644
|
-
// Never marked paid from the browser. It used to be, through an RPC that
|
|
7645
|
-
// was granted to anon — so any buyer holding their own order id could
|
|
7646
|
-
// declare it settled. The order is now born `pending` and only the
|
|
7647
|
-
// merchant (or a PSP webhook) can confirm the money arrived.
|
|
7648
8168
|
});
|
|
7649
8169
|
if (customerId) session.setSession({ customerId, email: form.email, name: form.name });
|
|
7650
|
-
|
|
8170
|
+
leaving.current = true;
|
|
7651
8171
|
cart.clear();
|
|
7652
8172
|
navigateTo(`/order/${order.id}`);
|
|
7653
|
-
} catch (
|
|
7654
|
-
setError(
|
|
8173
|
+
} catch (err) {
|
|
8174
|
+
setError(placeOrderMessage(err));
|
|
7655
8175
|
setPlacing(false);
|
|
7656
|
-
} finally {
|
|
7657
|
-
stepTimers.forEach(clearTimeout);
|
|
7658
8176
|
}
|
|
7659
8177
|
}
|
|
7660
|
-
|
|
8178
|
+
function validate() {
|
|
8179
|
+
setError(null);
|
|
8180
|
+
if (requiresAccount && !session.customerId) {
|
|
8181
|
+
setShowSignin(true);
|
|
8182
|
+
return fail("Esta loja pede uma conta para finalizar a compra. Entre ou crie a sua.");
|
|
8183
|
+
}
|
|
8184
|
+
if (!form.email.trim() || !/.+@.+\..+/.test(form.email)) {
|
|
8185
|
+
return fail("Informe um e-mail v\xE1lido.", TID.checkoutEmail);
|
|
8186
|
+
}
|
|
8187
|
+
if (requires("phone") && !form.phone.trim()) {
|
|
8188
|
+
return fail("Informe um telefone de contato.", "checkout-phone");
|
|
8189
|
+
}
|
|
8190
|
+
if (!form.name.trim()) {
|
|
8191
|
+
return fail("Informe seu nome completo.", TID.checkoutName);
|
|
8192
|
+
}
|
|
8193
|
+
if (requires("document") && !isValidDocument(form.document)) {
|
|
8194
|
+
return fail(
|
|
8195
|
+
form.document.trim() ? "CPF ou CNPJ inv\xE1lido \u2014 confira os n\xFAmeros." : "Informe o CPF ou CNPJ do comprador.",
|
|
8196
|
+
TID.checkoutDocument
|
|
8197
|
+
);
|
|
8198
|
+
}
|
|
8199
|
+
if (!form.street.trim() || !form.city.trim() || !form.zip.trim() || !form.district.trim()) {
|
|
8200
|
+
return fail("Preencha ou selecione o endere\xE7o de entrega.", TID.checkoutStreet);
|
|
8201
|
+
}
|
|
8202
|
+
if (!form.number.trim()) {
|
|
8203
|
+
return fail("Falta o n\xFAmero do endere\xE7o.", "checkout-number");
|
|
8204
|
+
}
|
|
8205
|
+
if (requires("complement") && !form.complement.trim()) {
|
|
8206
|
+
return fail("Informe o complemento (apto, bloco).", "checkout-complement");
|
|
8207
|
+
}
|
|
8208
|
+
if (form.state.trim().length !== 2) {
|
|
8209
|
+
return fail("Informe a UF com duas letras (ex.: RJ).", "checkout-state");
|
|
8210
|
+
}
|
|
8211
|
+
if (selectRequiresShipping(cart) && delivery.status === "unserved" && delivery.postalCode === normalizePostalCode(form.zip)) {
|
|
8212
|
+
return fail(
|
|
8213
|
+
delivery.refusedZone ? "Esta loja n\xE3o entrega nesse endere\xE7o. Tente outro." : "Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.",
|
|
8214
|
+
TID.checkoutZip
|
|
8215
|
+
);
|
|
8216
|
+
}
|
|
8217
|
+
return true;
|
|
8218
|
+
}
|
|
7661
8219
|
return /* @__PURE__ */ jsxs("main", { className: "min-h-screen bg-background", children: [
|
|
7662
8220
|
/* @__PURE__ */ jsx(Slot, { area: "checkout", name: "beforeForm", context: checkoutSlotContext }),
|
|
7663
|
-
placing && /* @__PURE__ */ jsx(ProcessingOverlay, { step: processingStep }),
|
|
7664
8221
|
showSignin && !session.email && /* @__PURE__ */ jsx(
|
|
7665
8222
|
SignInModal,
|
|
7666
8223
|
{
|
|
@@ -7682,132 +8239,281 @@ function CheckoutPage() {
|
|
|
7682
8239
|
] }) }),
|
|
7683
8240
|
/* @__PURE__ */ jsxs("div", { className: "mx-auto grid max-w-6xl grid-cols-1 gap-8 px-4 py-8 sm:px-6 lg:grid-cols-5 lg:px-6", children: [
|
|
7684
8241
|
/* @__PURE__ */ jsxs("section", { className: "lg:col-span-3 lg:pr-10", children: [
|
|
7685
|
-
/* @__PURE__ */
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
/* @__PURE__ */ jsxs(
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
className: "text-sm
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
8242
|
+
/* @__PURE__ */ jsx(
|
|
8243
|
+
CheckoutSteps,
|
|
8244
|
+
{
|
|
8245
|
+
current: step,
|
|
8246
|
+
onGoBackTo: step === 4 ? void 0 : (target) => goToStep(target)
|
|
8247
|
+
}
|
|
8248
|
+
),
|
|
8249
|
+
/* @__PURE__ */ jsxs(
|
|
8250
|
+
"form",
|
|
8251
|
+
{
|
|
8252
|
+
className: "space-y-8",
|
|
8253
|
+
onSubmit: (event) => {
|
|
8254
|
+
event.preventDefault();
|
|
8255
|
+
advance();
|
|
8256
|
+
},
|
|
8257
|
+
children: [
|
|
8258
|
+
step === 1 && /* @__PURE__ */ jsxs("section", { children: [
|
|
8259
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-4", children: [
|
|
8260
|
+
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold tracking-tight", children: "Informa\xE7\xF5es de contato" }),
|
|
8261
|
+
session.email ? /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
8262
|
+
"Conectado como ",
|
|
8263
|
+
session.email
|
|
8264
|
+
] }) : /* @__PURE__ */ jsx(
|
|
8265
|
+
"button",
|
|
8266
|
+
{
|
|
8267
|
+
type: "button",
|
|
8268
|
+
onClick: () => setShowSignin((value) => !value),
|
|
8269
|
+
className: "text-sm font-medium text-primary hover:underline",
|
|
8270
|
+
children: "J\xE1 tem conta? Entrar"
|
|
8271
|
+
}
|
|
8272
|
+
)
|
|
8273
|
+
] }),
|
|
8274
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
8275
|
+
field("E-mail", TID.checkoutEmail, form.email, set("email"), {
|
|
8276
|
+
type: "email",
|
|
8277
|
+
placeholder: "E-mail"
|
|
8278
|
+
}),
|
|
8279
|
+
field("Nome completo", TID.checkoutName, form.name, set("name"), {
|
|
8280
|
+
placeholder: "Nome completo"
|
|
8281
|
+
}),
|
|
8282
|
+
shows("phone") && field("Telefone", "checkout-phone", form.phone, set("phone"), {
|
|
8283
|
+
type: "tel",
|
|
8284
|
+
autoComplete: "tel",
|
|
8285
|
+
placeholder: requires("phone") ? "Telefone" : "Telefone (opcional)"
|
|
8286
|
+
}),
|
|
8287
|
+
shows("document") && field(
|
|
8288
|
+
"CPF ou CNPJ",
|
|
8289
|
+
TID.checkoutDocument,
|
|
8290
|
+
form.document,
|
|
8291
|
+
(value) => {
|
|
8292
|
+
setError(null);
|
|
8293
|
+
setForm((current) => ({ ...current, document: formatDocument(value) }));
|
|
8294
|
+
},
|
|
8295
|
+
{
|
|
8296
|
+
// `inputMode` and not `type="number"`: the mask writes dots
|
|
8297
|
+
// and a dash, which a number input silently refuses to hold.
|
|
8298
|
+
inputMode: "numeric",
|
|
8299
|
+
autoComplete: "off",
|
|
8300
|
+
placeholder: requires("document") ? "CPF ou CNPJ" : "CPF ou CNPJ (opcional)"
|
|
8301
|
+
}
|
|
8302
|
+
)
|
|
8303
|
+
] }),
|
|
8304
|
+
requiresAccount && !session.customerId && /* @__PURE__ */ jsxs("p", { className: "mt-3 rounded-lg border border-primary/30 bg-primary/5 px-3 py-2 text-sm", children: [
|
|
8305
|
+
"Esta loja pede uma conta para finalizar a compra.",
|
|
8306
|
+
" ",
|
|
8307
|
+
/* @__PURE__ */ jsx(
|
|
8308
|
+
"button",
|
|
8309
|
+
{
|
|
8310
|
+
type: "button",
|
|
8311
|
+
onClick: () => setShowSignin(true),
|
|
8312
|
+
className: "font-semibold text-primary hover:underline",
|
|
8313
|
+
children: "Entrar ou criar conta"
|
|
8314
|
+
}
|
|
8315
|
+
)
|
|
8316
|
+
] })
|
|
8317
|
+
] }),
|
|
8318
|
+
step === 2 && /* @__PURE__ */ jsxs("section", { children: [
|
|
8319
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Endere\xE7o de entrega" }),
|
|
8320
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 grid gap-3", children: [
|
|
8321
|
+
addressesLoading && savedAddresses.length === 0 && /* @__PURE__ */ jsx(SkeletonGroup, { label: "Buscando seus endere\xE7os salvos", className: "grid gap-3", children: [0, 1].map((row) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border p-3", children: [
|
|
8322
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-28" }),
|
|
8323
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3 w-3/4" }),
|
|
8324
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-1/2" })
|
|
8325
|
+
] }, row)) }),
|
|
8326
|
+
savedAddresses.map((address) => /* @__PURE__ */ jsx(
|
|
8327
|
+
"div",
|
|
8328
|
+
{
|
|
8329
|
+
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"}`,
|
|
8330
|
+
children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => selectAddress(address.id), className: "block w-full text-left", children: [
|
|
8331
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
|
|
8332
|
+
address.label,
|
|
8333
|
+
addressMode === "saved" && selectedAddressId === address.id && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
|
|
8334
|
+
] }),
|
|
8335
|
+
/* @__PURE__ */ jsxs("span", { className: "mt-1 block text-muted-foreground", children: [
|
|
8336
|
+
[address.street, address.number].filter(Boolean).join(", "),
|
|
8337
|
+
address.complement ? ` \u2014 ${address.complement}` : ""
|
|
8338
|
+
] }),
|
|
8339
|
+
/* @__PURE__ */ jsxs("span", { className: "block text-muted-foreground", children: [
|
|
8340
|
+
[address.district, address.city, address.state].filter(Boolean).join(" \xB7 "),
|
|
8341
|
+
" \u2014 ",
|
|
8342
|
+
address.postalCode
|
|
8343
|
+
] })
|
|
8344
|
+
] })
|
|
8345
|
+
},
|
|
8346
|
+
address.id
|
|
8347
|
+
)),
|
|
8348
|
+
/* @__PURE__ */ jsxs(
|
|
8349
|
+
"button",
|
|
8350
|
+
{
|
|
8351
|
+
type: "button",
|
|
8352
|
+
onClick: addNewAddress,
|
|
8353
|
+
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"}`,
|
|
8354
|
+
children: [
|
|
8355
|
+
/* @__PURE__ */ jsx("span", { className: "block font-semibold text-primary", children: savedAddresses.length > 0 ? "+ Adicionar novo endere\xE7o" : "Informe o endere\xE7o de entrega" }),
|
|
8356
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 block text-muted-foreground", children: "Preencher outro endere\xE7o para esta compra" })
|
|
8357
|
+
]
|
|
8358
|
+
}
|
|
8359
|
+
)
|
|
8360
|
+
] }),
|
|
8361
|
+
addressMode === "new" && /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
8362
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-1 sm:grid-cols-[200px_1fr] sm:items-center", children: [
|
|
8363
|
+
field("CEP", TID.checkoutZip, form.zip, setZip, {
|
|
8364
|
+
placeholder: "CEP",
|
|
8365
|
+
inputMode: "numeric",
|
|
8366
|
+
autoComplete: "postal-code"
|
|
8367
|
+
}),
|
|
8368
|
+
zipLookup === "loading" ? /* @__PURE__ */ jsxs(SkeletonGroup, { label: "Buscando o endere\xE7o deste CEP", className: "grid gap-1.5 px-1", children: [
|
|
8369
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-40" }),
|
|
8370
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-24" })
|
|
8371
|
+
] }) : /* @__PURE__ */ jsx("p", { className: "px-1 text-xs text-muted-foreground", children: zipLookup === "not-found" ? "CEP n\xE3o encontrado \u2014 preencha o endere\xE7o \xE0 m\xE3o." : "Preenchemos o endere\xE7o para voc\xEA." })
|
|
8372
|
+
] }),
|
|
8373
|
+
field("Endere\xE7o", TID.checkoutStreet, form.street, set("street"), { placeholder: "Rua, avenida\u2026" }),
|
|
8374
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
8375
|
+
field("N\xFAmero", "checkout-number", form.number, set("number"), { placeholder: "N\xFAmero" }),
|
|
8376
|
+
shows("complement") && field("Complemento", "checkout-complement", form.complement, set("complement"), {
|
|
8377
|
+
placeholder: requires("complement") ? "Apto, bloco" : "Apto, bloco (opcional)"
|
|
8378
|
+
})
|
|
8379
|
+
] }),
|
|
8380
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
8381
|
+
field("Bairro", "checkout-district", form.district, set("district"), { placeholder: "Bairro" }),
|
|
8382
|
+
field("UF", "checkout-state", form.state, set("state"), { placeholder: "UF" })
|
|
8383
|
+
] }),
|
|
8384
|
+
field("Cidade", TID.checkoutCity, form.city, set("city"), { placeholder: "Cidade" })
|
|
8385
|
+
] })
|
|
8386
|
+
] }),
|
|
8387
|
+
step === 3 && placing && /* @__PURE__ */ jsxs("section", { children: [
|
|
8388
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Confirmando seu pedido" }),
|
|
8389
|
+
/* @__PURE__ */ jsxs(SkeletonGroup, { label: "Criando seu pedido", className: "rounded-xl border bg-card p-6 sm:p-8", children: [
|
|
8390
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 border-b pb-6 sm:grid-cols-[120px_minmax(0,1fr)]", children: [
|
|
8391
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mx-auto h-24 w-24 rounded-full" }),
|
|
8392
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
8393
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-56 max-w-full" }),
|
|
8394
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3.5 w-full max-w-sm" }),
|
|
8395
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-4 h-5 w-40" })
|
|
7739
8396
|
] })
|
|
7740
|
-
] })
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
/* @__PURE__ */ jsx("span", { className: "mt-1 block text-muted-foreground", children: "Preencher outro endere\xE7o para esta compra" })
|
|
7753
|
-
]
|
|
7754
|
-
}
|
|
7755
|
-
)
|
|
7756
|
-
] }),
|
|
7757
|
-
addressMode === "new" && /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
7758
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-1 sm:grid-cols-[200px_1fr] sm:items-center", children: [
|
|
7759
|
-
field("CEP", TID.checkoutZip, form.zip, setZip, {
|
|
7760
|
-
placeholder: "CEP",
|
|
7761
|
-
inputMode: "numeric",
|
|
7762
|
-
autoComplete: "postal-code"
|
|
7763
|
-
}),
|
|
7764
|
-
/* @__PURE__ */ 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." })
|
|
8397
|
+
] }),
|
|
8398
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6 grid gap-4 rounded-xl border bg-muted/20 p-4 sm:grid-cols-3", children: [0, 1, 2].map((column) => /* @__PURE__ */ jsxs("div", { children: [
|
|
8399
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-16" }),
|
|
8400
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-4 w-24" })
|
|
8401
|
+
] }, column)) }),
|
|
8402
|
+
/* @__PURE__ */ jsx("div", { className: "mt-8 space-y-3", children: cart.lines.slice(0, 3).map((line) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
8403
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-12 rounded-lg" }),
|
|
8404
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 flex-1" }),
|
|
8405
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-16" })
|
|
8406
|
+
] }, line.lineId ?? line.productId)) })
|
|
8407
|
+
] }),
|
|
8408
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 text-center text-sm text-muted-foreground", children: "N\xE3o feche esta p\xE1gina \u2014 estamos registrando seu pedido na loja." })
|
|
7765
8409
|
] }),
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
8410
|
+
step === 3 && !placing && /* @__PURE__ */ jsxs("section", { children: [
|
|
8411
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Pagamento" }),
|
|
8412
|
+
/* @__PURE__ */ jsx("div", { className: "mb-3 grid gap-3", role: "radiogroup", "aria-label": "Forma de pagamento", children: paymentMethods.map((method) => {
|
|
8413
|
+
const copy = PAYMENT_LABELS[method];
|
|
8414
|
+
const selected = paymentMethod === method;
|
|
8415
|
+
return (
|
|
8416
|
+
// Cada forma de pagamento abre o que ELA pede, embaixo dela
|
|
8417
|
+
// mesma. Antes o painel do Pix ficava no fim da lista, longe
|
|
8418
|
+
// do cartão escolhido, e escolher parecia não fazer nada.
|
|
8419
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
8420
|
+
/* @__PURE__ */ jsxs(
|
|
8421
|
+
"button",
|
|
8422
|
+
{
|
|
8423
|
+
type: "button",
|
|
8424
|
+
role: "radio",
|
|
8425
|
+
"aria-checked": selected,
|
|
8426
|
+
"data-testid": `checkout-payment-${method}`,
|
|
8427
|
+
onClick: () => setPaymentMethod(method),
|
|
8428
|
+
className: `w-full rounded-lg border p-3 text-left text-sm transition hover:bg-muted/40 ${selected ? "border-primary bg-primary/5" : "border-border bg-background"}`,
|
|
8429
|
+
children: [
|
|
8430
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
|
|
8431
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
8432
|
+
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }),
|
|
8433
|
+
copy.label
|
|
8434
|
+
] }),
|
|
8435
|
+
selected && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
|
|
8436
|
+
] }),
|
|
8437
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 block text-muted-foreground", children: copy.hint })
|
|
8438
|
+
]
|
|
8439
|
+
}
|
|
8440
|
+
),
|
|
8441
|
+
selected && chargesOnline && /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
|
|
8442
|
+
CheckoutPayment,
|
|
8443
|
+
{
|
|
8444
|
+
input: checkoutSessionInput,
|
|
8445
|
+
money,
|
|
8446
|
+
onBack: () => goToStep(2),
|
|
8447
|
+
onUnavailable: () => setAcquirerMissing(true),
|
|
8448
|
+
onPaid: (orderId) => {
|
|
8449
|
+
leaving.current = true;
|
|
8450
|
+
setPaidOrderId(orderId);
|
|
8451
|
+
goToStep(4);
|
|
8452
|
+
cart.clear();
|
|
8453
|
+
}
|
|
8454
|
+
}
|
|
8455
|
+
) })
|
|
8456
|
+
] }, method)
|
|
8457
|
+
);
|
|
8458
|
+
}) }),
|
|
8459
|
+
/* @__PURE__ */ jsx(Slot, { area: "checkout", name: "afterPaymentMethods", context: checkoutSlotContext }),
|
|
8460
|
+
!chargesOnline && /* @__PURE__ */ jsxs("div", { className: "mt-5", children: [
|
|
8461
|
+
/* @__PURE__ */ jsx(
|
|
8462
|
+
"button",
|
|
8463
|
+
{
|
|
8464
|
+
type: "button",
|
|
8465
|
+
"data-testid": TID.placeOrder,
|
|
8466
|
+
disabled: placing,
|
|
8467
|
+
onClick: () => {
|
|
8468
|
+
void confirmWithoutCharge();
|
|
8469
|
+
},
|
|
8470
|
+
className: "min-h-12 w-full rounded-xl bg-primary px-6 py-3 font-semibold text-primary-foreground transition hover:opacity-90 disabled:opacity-60",
|
|
8471
|
+
children: `Fazer pedido - ${money(total)}`
|
|
8472
|
+
}
|
|
8473
|
+
),
|
|
8474
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-center text-xs text-muted-foreground", children: "O pedido fica aguardando pagamento at\xE9 a loja confirmar." })
|
|
8475
|
+
] })
|
|
7770
8476
|
] }),
|
|
7771
|
-
/* @__PURE__ */ jsxs("
|
|
7772
|
-
|
|
7773
|
-
|
|
8477
|
+
step === 4 && paidOrderId && /* @__PURE__ */ jsxs("section", { "data-testid": TID.checkoutDone, className: "rounded-xl border bg-card p-8 text-center", children: [
|
|
8478
|
+
/* @__PURE__ */ jsx(SuccessMark, {}),
|
|
8479
|
+
/* @__PURE__ */ jsx("h2", { className: "mt-4 text-2xl font-bold tracking-tight", children: "Pagamento confirmado!" }),
|
|
8480
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: "Seu pedido foi criado e a loja j\xE1 foi avisada." }),
|
|
8481
|
+
/* @__PURE__ */ jsx(
|
|
8482
|
+
"button",
|
|
8483
|
+
{
|
|
8484
|
+
type: "button",
|
|
8485
|
+
onClick: () => navigateTo(`/order/${paidOrderId}`),
|
|
8486
|
+
className: "mt-6 inline-flex min-h-11 items-center rounded-lg bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground transition hover:opacity-90",
|
|
8487
|
+
children: "Ver meu pedido"
|
|
8488
|
+
}
|
|
8489
|
+
)
|
|
7774
8490
|
] }),
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
]
|
|
7802
|
-
},
|
|
7803
|
-
method
|
|
7804
|
-
);
|
|
7805
|
-
}) }),
|
|
7806
|
-
/* @__PURE__ */ 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." }),
|
|
7807
|
-
/* @__PURE__ */ jsx(Slot, { area: "checkout", name: "afterPaymentMethods", context: checkoutSlotContext })
|
|
7808
|
-
] }),
|
|
7809
|
-
error && /* @__PURE__ */ 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 })
|
|
7810
|
-
] }),
|
|
8491
|
+
error && /* @__PURE__ */ 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 }),
|
|
8492
|
+
(step === 1 || step === 2) && /* @__PURE__ */ jsxs("div", { className: "grid gap-3 border-t pt-5", children: [
|
|
8493
|
+
/* @__PURE__ */ jsx(
|
|
8494
|
+
"button",
|
|
8495
|
+
{
|
|
8496
|
+
type: "submit",
|
|
8497
|
+
"data-testid": TID.checkoutContinue,
|
|
8498
|
+
className: "min-h-12 rounded-lg bg-primary px-6 py-4 text-sm font-bold text-primary-foreground transition hover:opacity-90",
|
|
8499
|
+
children: step === 1 ? "Continuar para a entrega" : `Ir para o pagamento - ${money(total)}`
|
|
8500
|
+
}
|
|
8501
|
+
),
|
|
8502
|
+
step === 2 && /* @__PURE__ */ jsx(
|
|
8503
|
+
"button",
|
|
8504
|
+
{
|
|
8505
|
+
type: "button",
|
|
8506
|
+
"data-testid": TID.checkoutBack,
|
|
8507
|
+
onClick: () => goToStep(1),
|
|
8508
|
+
className: "min-h-11 rounded-lg border px-6 py-3 text-sm font-medium transition hover:bg-muted",
|
|
8509
|
+
children: "Voltar aos dados"
|
|
8510
|
+
}
|
|
8511
|
+
),
|
|
8512
|
+
/* @__PURE__ */ jsx(Link, { to: config.catalogPath, className: "text-center text-sm text-muted-foreground hover:text-foreground", children: "Voltar ao carrinho" })
|
|
8513
|
+
] })
|
|
8514
|
+
]
|
|
8515
|
+
}
|
|
8516
|
+
),
|
|
7811
8517
|
/* @__PURE__ */ jsxs("footer", { className: "mt-10 flex flex-wrap gap-4 border-t pt-5 text-xs text-muted-foreground", children: [
|
|
7812
8518
|
/* @__PURE__ */ jsx(Link, { to: "/privacy", className: "hover:text-foreground", children: "Privacidade" }),
|
|
7813
8519
|
/* @__PURE__ */ jsx(Link, { to: "/terms", className: "hover:text-foreground", children: "Termos" }),
|
|
@@ -7901,21 +8607,7 @@ function CheckoutPage() {
|
|
|
7901
8607
|
/* @__PURE__ */ jsx(ShieldCheck, { className: "h-4 w-4 text-primary" }),
|
|
7902
8608
|
"Compra protegida e dados criptografados."
|
|
7903
8609
|
] }),
|
|
7904
|
-
/* @__PURE__ */
|
|
7905
|
-
/* @__PURE__ */ jsx(
|
|
7906
|
-
"button",
|
|
7907
|
-
{
|
|
7908
|
-
type: "button",
|
|
7909
|
-
"data-testid": TID.placeOrder,
|
|
7910
|
-
onClick: placeOrder,
|
|
7911
|
-
disabled: placing,
|
|
7912
|
-
className: "rounded-lg bg-primary px-6 py-4 text-sm font-bold text-primary-foreground transition hover:opacity-90 disabled:opacity-60",
|
|
7913
|
-
children: primaryCta
|
|
7914
|
-
}
|
|
7915
|
-
),
|
|
7916
|
-
/* @__PURE__ */ jsx(Link, { to: config.catalogPath, className: "text-center text-sm text-muted-foreground hover:text-foreground", children: "Voltar ao carrinho" }),
|
|
7917
|
-
/* @__PURE__ */ jsx(Slot, { area: "checkout", name: "afterForm", context: checkoutSlotContext })
|
|
7918
|
-
] })
|
|
8610
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6 border-t pt-5", children: /* @__PURE__ */ jsx(Slot, { area: "checkout", name: "afterForm", context: checkoutSlotContext }) })
|
|
7919
8611
|
] })
|
|
7920
8612
|
] })
|
|
7921
8613
|
] });
|
|
@@ -7980,9 +8672,10 @@ function orderTrackingSteps(order) {
|
|
|
7980
8672
|
{
|
|
7981
8673
|
id: "payment",
|
|
7982
8674
|
label: paid ? "Pagamento aprovado" : "Aguardando pagamento",
|
|
7983
|
-
|
|
8675
|
+
// "Processando" descrevia um pagamento que ninguém iniciou.
|
|
8676
|
+
description: paid ? "Compra confirmada" : "Ainda n\xE3o recebemos",
|
|
7984
8677
|
state: paid ? "complete" : "current",
|
|
7985
|
-
icon: Check
|
|
8678
|
+
icon: paid ? Check : Clock
|
|
7986
8679
|
},
|
|
7987
8680
|
{
|
|
7988
8681
|
id: "preparing",
|
|
@@ -8016,7 +8709,9 @@ function OrderTrackingTimeline({ order, compact: compact2 = false }) {
|
|
|
8016
8709
|
children: steps.map((step) => {
|
|
8017
8710
|
const Icon2 = step.icon;
|
|
8018
8711
|
const cancelled = step.state === "cancelled";
|
|
8019
|
-
const
|
|
8712
|
+
const done = step.state === "complete";
|
|
8713
|
+
const current = step.state === "current";
|
|
8714
|
+
const active2 = done || current;
|
|
8020
8715
|
return /* @__PURE__ */ jsx(
|
|
8021
8716
|
"li",
|
|
8022
8717
|
{
|
|
@@ -8033,15 +8728,15 @@ function OrderTrackingTimeline({ order, compact: compact2 = false }) {
|
|
|
8033
8728
|
{
|
|
8034
8729
|
className: [
|
|
8035
8730
|
"grid h-7 w-7 shrink-0 place-items-center rounded-full border",
|
|
8036
|
-
cancelled ? "border-rose-300 bg-rose-500 text-white" :
|
|
8731
|
+
cancelled ? "border-rose-300 bg-rose-500 text-white" : done ? "border-primary bg-primary text-primary-foreground" : current ? "border-primary bg-background text-primary" : "border-muted-foreground/30 bg-background"
|
|
8037
8732
|
].join(" "),
|
|
8038
8733
|
"aria-hidden": true,
|
|
8039
8734
|
children: /* @__PURE__ */ jsx(Icon2, { className: "h-3.5 w-3.5" })
|
|
8040
8735
|
}
|
|
8041
8736
|
),
|
|
8042
8737
|
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
8043
|
-
/* @__PURE__ */ jsx("span", { className: "block
|
|
8044
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block
|
|
8738
|
+
/* @__PURE__ */ jsx("span", { className: "block font-semibold leading-snug text-foreground", children: step.label }),
|
|
8739
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block leading-snug text-muted-foreground", children: step.description })
|
|
8045
8740
|
] })
|
|
8046
8741
|
] })
|
|
8047
8742
|
},
|
|
@@ -8051,6 +8746,32 @@ function OrderTrackingTimeline({ order, compact: compact2 = false }) {
|
|
|
8051
8746
|
}
|
|
8052
8747
|
);
|
|
8053
8748
|
}
|
|
8749
|
+
|
|
8750
|
+
// src/components/account/section.ts
|
|
8751
|
+
var ACCOUNT_SECTIONS = [
|
|
8752
|
+
"profile",
|
|
8753
|
+
"orders",
|
|
8754
|
+
"addresses",
|
|
8755
|
+
"payments"
|
|
8756
|
+
];
|
|
8757
|
+
var DEFAULT_ACCOUNT_SECTION = "orders";
|
|
8758
|
+
function isAccountSection(value) {
|
|
8759
|
+
return ACCOUNT_SECTIONS.includes(value);
|
|
8760
|
+
}
|
|
8761
|
+
function parseAccountSection(path) {
|
|
8762
|
+
const query = path.split("?")[1];
|
|
8763
|
+
if (!query) return DEFAULT_ACCOUNT_SECTION;
|
|
8764
|
+
for (const pair of query.split("&")) {
|
|
8765
|
+
const [key, raw = ""] = pair.split("=");
|
|
8766
|
+
if (key !== "section") continue;
|
|
8767
|
+
const value = decodeURIComponent(raw);
|
|
8768
|
+
return isAccountSection(value) ? value : DEFAULT_ACCOUNT_SECTION;
|
|
8769
|
+
}
|
|
8770
|
+
return DEFAULT_ACCOUNT_SECTION;
|
|
8771
|
+
}
|
|
8772
|
+
function accountSectionPath(section) {
|
|
8773
|
+
return `/account?section=${section}`;
|
|
8774
|
+
}
|
|
8054
8775
|
var menuItems = [
|
|
8055
8776
|
{ id: "profile", label: "Perfil", icon: User },
|
|
8056
8777
|
{ id: "orders", label: "Meus pedidos", icon: Package },
|
|
@@ -8084,11 +8805,16 @@ function CustomerAccountShell({ title, subtitle, active: active2, onSelect, chil
|
|
|
8084
8805
|
const Icon2 = item.icon;
|
|
8085
8806
|
const selected = item.id === active2;
|
|
8086
8807
|
return /* @__PURE__ */ jsxs(
|
|
8087
|
-
|
|
8808
|
+
Link,
|
|
8088
8809
|
{
|
|
8089
|
-
|
|
8810
|
+
to: accountSectionPath(item.id),
|
|
8811
|
+
"data-section": item.id,
|
|
8090
8812
|
"aria-current": selected ? "page" : void 0,
|
|
8091
|
-
onClick: () =>
|
|
8813
|
+
onClick: (event) => {
|
|
8814
|
+
if (!onSelect) return;
|
|
8815
|
+
event.preventDefault();
|
|
8816
|
+
onSelect(item.id);
|
|
8817
|
+
},
|
|
8092
8818
|
className: `flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left text-sm font-semibold transition ${selected ? "bg-primary/10 text-primary" : "text-muted-foreground hover:bg-muted hover:text-foreground"}`,
|
|
8093
8819
|
children: [
|
|
8094
8820
|
/* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4" }),
|
|
@@ -8123,38 +8849,23 @@ function CustomerAccountShell({ title, subtitle, active: active2, onSelect, chil
|
|
|
8123
8849
|
] })
|
|
8124
8850
|
] });
|
|
8125
8851
|
}
|
|
8126
|
-
|
|
8127
|
-
function SuccessMark() {
|
|
8128
|
-
const reduced = prefersReducedMotion();
|
|
8852
|
+
function PendingMark() {
|
|
8129
8853
|
return /* @__PURE__ */ jsxs("div", { className: "relative mx-auto h-24 w-24", children: [
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
{
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8854
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 rounded-full bg-amber-100" }),
|
|
8855
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", className: "absolute inset-0 h-full w-full p-6", "aria-hidden": true, children: [
|
|
8856
|
+
/* @__PURE__ */ jsx("circle", { cx: "24", cy: "24", r: "17", fill: "none", stroke: "rgb(180 83 9)", strokeWidth: "4" }),
|
|
8857
|
+
/* @__PURE__ */ jsx(
|
|
8858
|
+
"path",
|
|
8859
|
+
{
|
|
8860
|
+
d: "M24 14 L24 25 L31 29",
|
|
8861
|
+
fill: "none",
|
|
8862
|
+
stroke: "rgb(180 83 9)",
|
|
8863
|
+
strokeWidth: "4",
|
|
8864
|
+
strokeLinecap: "round",
|
|
8865
|
+
strokeLinejoin: "round"
|
|
8140
8866
|
}
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
)),
|
|
8144
|
-
/* @__PURE__ */ jsx("span", { className: `absolute inset-0 rounded-full bg-emerald-100 ${reduced ? "" : "animate-pop-in"}` }),
|
|
8145
|
-
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 48 48", className: "absolute inset-0 h-full w-full p-6", "aria-hidden": true, children: /* @__PURE__ */ jsx(
|
|
8146
|
-
"path",
|
|
8147
|
-
{
|
|
8148
|
-
d: "M10 25 L20 35 L38 14",
|
|
8149
|
-
fill: "none",
|
|
8150
|
-
stroke: "rgb(5 150 105)",
|
|
8151
|
-
strokeWidth: "5",
|
|
8152
|
-
strokeLinecap: "round",
|
|
8153
|
-
strokeLinejoin: "round",
|
|
8154
|
-
strokeDasharray: "48",
|
|
8155
|
-
className: reduced ? "" : "animate-draw-check"
|
|
8156
|
-
}
|
|
8157
|
-
) })
|
|
8867
|
+
)
|
|
8868
|
+
] })
|
|
8158
8869
|
] });
|
|
8159
8870
|
}
|
|
8160
8871
|
function OrderConfirmationPage({ orderId }) {
|
|
@@ -8163,7 +8874,7 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
8163
8874
|
const [loading, setLoading] = useState(true);
|
|
8164
8875
|
const money = (v) => formatMoney(v, config.currency, config.locale);
|
|
8165
8876
|
useStorefrontHead({ title: order ? `Pedido #${order.orderNumber} \u2014 ${config.name}` : config.name });
|
|
8166
|
-
|
|
8877
|
+
const reload = React4.useCallback(() => {
|
|
8167
8878
|
let cancelled = false;
|
|
8168
8879
|
getShopProvider().getOrder(orderId).then((o) => {
|
|
8169
8880
|
if (!cancelled) setOrder(o);
|
|
@@ -8174,8 +8885,40 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
8174
8885
|
cancelled = true;
|
|
8175
8886
|
};
|
|
8176
8887
|
}, [orderId]);
|
|
8888
|
+
useEffect(() => reload(), [reload]);
|
|
8177
8889
|
if (loading) {
|
|
8178
|
-
return /* @__PURE__ */ jsx("
|
|
8890
|
+
return /* @__PURE__ */ jsx(CustomerAccountShell, { title: "Detalhe do pedido", active: "orders", children: /* @__PURE__ */ jsxs(SkeletonGroup, { label: "Carregando seu pedido", className: "rounded-xl border bg-card p-6 sm:p-8", children: [
|
|
8891
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 border-b pb-6 sm:grid-cols-[120px_minmax(0,1fr)]", children: [
|
|
8892
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mx-auto h-24 w-24 rounded-full" }),
|
|
8893
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
8894
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-64 max-w-full" }),
|
|
8895
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3.5 w-full max-w-md" }),
|
|
8896
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-4 h-6 w-48" })
|
|
8897
|
+
] })
|
|
8898
|
+
] }),
|
|
8899
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6 grid gap-4 rounded-xl border bg-muted/20 p-4 sm:grid-cols-3", children: [0, 1, 2].map((column) => /* @__PURE__ */ jsxs("div", { children: [
|
|
8900
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-16" }),
|
|
8901
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-4 w-28" }),
|
|
8902
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-20" })
|
|
8903
|
+
] }, column)) }),
|
|
8904
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
|
|
8905
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-32" }),
|
|
8906
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 flex items-center gap-2", children: [0, 1, 2, 3].map((node) => /* @__PURE__ */ jsxs(React4.Fragment, { children: [
|
|
8907
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-7 shrink-0 rounded-full" }),
|
|
8908
|
+
node < 3 && /* @__PURE__ */ jsx(Skeleton, { className: "h-px flex-1" })
|
|
8909
|
+
] }, node)) })
|
|
8910
|
+
] }),
|
|
8911
|
+
/* @__PURE__ */ jsx("div", { className: "mt-8 space-y-3", children: [0, 1].map((row) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
8912
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-12 rounded-lg" }),
|
|
8913
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 flex-1" }),
|
|
8914
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-16" })
|
|
8915
|
+
] }, row)) }),
|
|
8916
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2 border-t pt-4", children: [
|
|
8917
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-full max-w-xs" }),
|
|
8918
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-full max-w-[14rem]" }),
|
|
8919
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-40" })
|
|
8920
|
+
] })
|
|
8921
|
+
] }) });
|
|
8179
8922
|
}
|
|
8180
8923
|
if (!order) {
|
|
8181
8924
|
return /* @__PURE__ */ jsxs("main", { className: "mx-auto max-w-2xl px-4 py-20 text-center", children: [
|
|
@@ -8183,24 +8926,20 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
8183
8926
|
/* @__PURE__ */ jsx(Link, { to: "/", className: "mt-4 inline-block text-primary underline", children: "Voltar \xE0 loja" })
|
|
8184
8927
|
] });
|
|
8185
8928
|
}
|
|
8929
|
+
const paid = isPaid(order.financialStatus);
|
|
8186
8930
|
return /* @__PURE__ */ jsx(
|
|
8187
8931
|
CustomerAccountShell,
|
|
8188
8932
|
{
|
|
8189
8933
|
title: "Detalhe do pedido",
|
|
8190
|
-
subtitle: "Compra confirmada e vinculada \xE0 sua conta.",
|
|
8934
|
+
subtitle: paid ? "Compra confirmada e vinculada \xE0 sua conta." : "Pedido guardado, aguardando o pagamento.",
|
|
8191
8935
|
active: "orders",
|
|
8936
|
+
onSelect: (section) => navigateTo(`/account?section=${section}`),
|
|
8192
8937
|
children: /* @__PURE__ */ jsxs("article", { "data-testid": TID.orderDetail, className: "animate-fade-up rounded-xl border bg-card p-6 sm:p-8", children: [
|
|
8193
8938
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 border-b pb-6 sm:grid-cols-[120px_minmax(0,1fr)]", children: [
|
|
8194
|
-
/* @__PURE__ */ jsx(SuccessMark, {}),
|
|
8939
|
+
paid ? /* @__PURE__ */ jsx(SuccessMark, {}) : /* @__PURE__ */ jsx(PendingMark, {}),
|
|
8195
8940
|
/* @__PURE__ */ jsxs("div", { className: "text-center sm:text-left", children: [
|
|
8196
|
-
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-3xl font-bold tracking-tight", children: "Pedido confirmado!" }),
|
|
8197
|
-
/* @__PURE__ */
|
|
8198
|
-
"Obrigado pela compra, ",
|
|
8199
|
-
order.customerName ?? "cliente",
|
|
8200
|
-
". Enviamos a confirma\xE7\xE3o para ",
|
|
8201
|
-
order.customerEmail,
|
|
8202
|
-
"."
|
|
8203
|
-
] }),
|
|
8941
|
+
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-3xl font-bold tracking-tight", children: paid ? "Pedido confirmado!" : "Falta o pagamento" }),
|
|
8942
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: paid ? `Obrigado pela compra, ${order.customerName ?? "cliente"}. A loja j\xE1 est\xE1 preparando seu pedido.` : `Guardamos seu pedido, ${order.customerName ?? "cliente"}. Ele s\xF3 \xE9 preparado depois que o pagamento entrar.` }),
|
|
8204
8943
|
/* @__PURE__ */ jsxs("p", { className: "mt-4 text-lg", children: [
|
|
8205
8944
|
"Pedido ",
|
|
8206
8945
|
/* @__PURE__ */ jsxs("span", { "data-testid": TID.orderNumber, className: "font-semibold", children: [
|
|
@@ -8227,18 +8966,39 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
8227
8966
|
] }),
|
|
8228
8967
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
8229
8968
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Entrega" }),
|
|
8230
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 font-semibold", children: order.fulfillmentStatus === "fulfilled" ? "Enviado" : "Preparando envio" }),
|
|
8969
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 font-semibold", children: order.fulfillmentStatus === "fulfilled" ? "Enviado" : paid ? "Preparando envio" : "Aguardando pagamento" }),
|
|
8231
8970
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: order.notes ?? "Endere\xE7o salvo no pedido" })
|
|
8232
8971
|
] }),
|
|
8233
8972
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
8234
8973
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Pagamento" }),
|
|
8235
8974
|
/* @__PURE__ */ jsx("p", { className: "mt-1 font-semibold", children: financialStatusBadge(order.financialStatus).label }),
|
|
8236
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "
|
|
8975
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: paid ? "Recebido pela loja" : "Ainda n\xE3o recebido" })
|
|
8237
8976
|
] })
|
|
8238
8977
|
] }),
|
|
8239
8978
|
/* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
|
|
8240
8979
|
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase text-muted-foreground", children: "Acompanhamento" }),
|
|
8241
|
-
/* @__PURE__ */ jsx(OrderTrackingTimeline, { order })
|
|
8980
|
+
/* @__PURE__ */ jsx(OrderTrackingTimeline, { order }),
|
|
8981
|
+
(order.shipments ?? []).filter((s2) => s2.trackingCode).map((shipment) => /* @__PURE__ */ jsxs(
|
|
8982
|
+
"div",
|
|
8983
|
+
{
|
|
8984
|
+
"data-testid": TID.orderTracking + "-code",
|
|
8985
|
+
className: "mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 rounded-lg border bg-muted/30 px-4 py-3 text-sm",
|
|
8986
|
+
children: [
|
|
8987
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: shipment.carrier ? `Enviado por ${shipment.carrier}` : "Enviado" }),
|
|
8988
|
+
shipment.trackingUrl ? /* @__PURE__ */ jsx(
|
|
8989
|
+
"a",
|
|
8990
|
+
{
|
|
8991
|
+
href: shipment.trackingUrl,
|
|
8992
|
+
target: "_blank",
|
|
8993
|
+
rel: "noreferrer",
|
|
8994
|
+
className: "font-mono font-medium text-primary hover:underline",
|
|
8995
|
+
children: shipment.trackingCode
|
|
8996
|
+
}
|
|
8997
|
+
) : /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: shipment.trackingCode })
|
|
8998
|
+
]
|
|
8999
|
+
},
|
|
9000
|
+
shipment.id
|
|
9001
|
+
))
|
|
8242
9002
|
] }),
|
|
8243
9003
|
/* @__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: [
|
|
8244
9004
|
item.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: item.imageUrl, alt: item.name, className: "h-12 w-12 rounded-lg border object-cover" }),
|
|
@@ -8301,16 +9061,23 @@ function useMyOrders() {
|
|
|
8301
9061
|
const refresh = useCallback(() => setTick((t) => t + 1), []);
|
|
8302
9062
|
useEffect(() => {
|
|
8303
9063
|
let cancelled = false;
|
|
9064
|
+
const local = listLocalOrderIds();
|
|
8304
9065
|
if (!customerId && !email) {
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
9066
|
+
if (local.length === 0) {
|
|
9067
|
+
setOrders([]);
|
|
9068
|
+
setLoading(false);
|
|
9069
|
+
return;
|
|
9070
|
+
}
|
|
8308
9071
|
}
|
|
8309
9072
|
setLoading(true);
|
|
8310
9073
|
const query = customerId ? { customerId, limit: 50 } : { customerEmail: email, limit: 50 };
|
|
8311
9074
|
getShopProvider().listOrders(query).then((data) => {
|
|
8312
9075
|
if (!cancelled) setOrders(data);
|
|
8313
|
-
}).catch(() => {
|
|
9076
|
+
}).catch(async () => {
|
|
9077
|
+
const mine = await Promise.all(
|
|
9078
|
+
listLocalOrderIds().map((id) => getShopProvider().getOrder(id).catch(() => null))
|
|
9079
|
+
);
|
|
9080
|
+
if (!cancelled) setOrders(mine.filter((o) => !!o));
|
|
8314
9081
|
}).finally(() => {
|
|
8315
9082
|
if (!cancelled) setLoading(false);
|
|
8316
9083
|
});
|
|
@@ -8432,7 +9199,27 @@ function OrdersPanel() {
|
|
|
8432
9199
|
const config = useStorefrontConfig();
|
|
8433
9200
|
const { orders, loading } = useMyOrders();
|
|
8434
9201
|
const money = (v) => formatMoney(v, config.currency, config.locale);
|
|
8435
|
-
if (loading)
|
|
9202
|
+
if (loading) {
|
|
9203
|
+
return /* @__PURE__ */ jsx(SkeletonGroup, { label: "Carregando seus pedidos", className: "space-y-4", children: [0, 1].map((row) => /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border bg-card p-5", children: [
|
|
9204
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
9205
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" }),
|
|
9206
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-24 rounded-full" })
|
|
9207
|
+
] }),
|
|
9208
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3 w-20" }),
|
|
9209
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 flex items-center gap-2", children: [0, 1, 2, 3].map((node) => /* @__PURE__ */ jsxs(React4.Fragment, { children: [
|
|
9210
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-6 shrink-0 rounded-full" }),
|
|
9211
|
+
node < 3 && /* @__PURE__ */ jsx(Skeleton, { className: "h-px flex-1" })
|
|
9212
|
+
] }, node)) }),
|
|
9213
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex flex-wrap gap-2", children: [
|
|
9214
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-32 rounded-lg" }),
|
|
9215
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-24 rounded-lg" })
|
|
9216
|
+
] }),
|
|
9217
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center justify-between border-t pt-3", children: [
|
|
9218
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24" }),
|
|
9219
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20" })
|
|
9220
|
+
] })
|
|
9221
|
+
] }, row)) });
|
|
9222
|
+
}
|
|
8436
9223
|
if (orders.length === 0) {
|
|
8437
9224
|
return /* @__PURE__ */ jsxs("div", { "data-testid": TID.purchasesEmpty, className: "rounded-2xl border bg-card py-16 text-center", children: [
|
|
8438
9225
|
/* @__PURE__ */ jsx(Package, { className: "mx-auto h-10 w-10 text-muted-foreground" }),
|
|
@@ -8479,12 +9266,14 @@ function ProfilePanel() {
|
|
|
8479
9266
|
const [name, setName] = useState(session.name ?? "");
|
|
8480
9267
|
const [saving, setSaving] = useState(false);
|
|
8481
9268
|
const [saved, setSaved] = useState(false);
|
|
9269
|
+
const [error, setError] = useState(null);
|
|
8482
9270
|
async function save(e) {
|
|
8483
9271
|
e.preventDefault();
|
|
8484
9272
|
const trimmed = name.trim();
|
|
8485
9273
|
if (!trimmed) return;
|
|
8486
9274
|
setSaving(true);
|
|
8487
9275
|
setSaved(false);
|
|
9276
|
+
setError(null);
|
|
8488
9277
|
try {
|
|
8489
9278
|
if (session.customerId) {
|
|
8490
9279
|
const parts = trimmed.split(/\s+/).filter(Boolean);
|
|
@@ -8495,6 +9284,8 @@ function ProfilePanel() {
|
|
|
8495
9284
|
}
|
|
8496
9285
|
useSessionStore.setState({ name: trimmed });
|
|
8497
9286
|
setSaved(true);
|
|
9287
|
+
} catch {
|
|
9288
|
+
setError("N\xE3o foi poss\xEDvel salvar seu nome agora. Tente novamente em instantes.");
|
|
8498
9289
|
} finally {
|
|
8499
9290
|
setSaving(false);
|
|
8500
9291
|
}
|
|
@@ -8539,7 +9330,8 @@ function ProfilePanel() {
|
|
|
8539
9330
|
}
|
|
8540
9331
|
),
|
|
8541
9332
|
saved && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-emerald-700", children: "Salvo" })
|
|
8542
|
-
] })
|
|
9333
|
+
] }),
|
|
9334
|
+
error && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-destructive", children: error })
|
|
8543
9335
|
] });
|
|
8544
9336
|
}
|
|
8545
9337
|
function AddressesPanel() {
|
|
@@ -8574,7 +9366,7 @@ var SECTION_SUBTITLE = {
|
|
|
8574
9366
|
function MyPurchasesPage() {
|
|
8575
9367
|
const config = useStorefrontConfig();
|
|
8576
9368
|
const session = useSessionStore();
|
|
8577
|
-
const
|
|
9369
|
+
const section = parseAccountSection(useRoutePath());
|
|
8578
9370
|
useStorefrontHead({ title: `Minha conta \u2014 ${config.name}` });
|
|
8579
9371
|
if (!session.email) {
|
|
8580
9372
|
return /* @__PURE__ */ jsx("main", { className: "mx-auto max-w-3xl px-4 py-12 sm:px-6", children: /* @__PURE__ */ jsx(AuthForm, {}) });
|
|
@@ -8585,7 +9377,6 @@ function MyPurchasesPage() {
|
|
|
8585
9377
|
title: "Minha conta",
|
|
8586
9378
|
subtitle: SECTION_SUBTITLE[section],
|
|
8587
9379
|
active: section,
|
|
8588
|
-
onSelect: setSection,
|
|
8589
9380
|
children: [
|
|
8590
9381
|
section === "profile" && /* @__PURE__ */ jsx(ProfilePanel, {}),
|
|
8591
9382
|
section === "orders" && /* @__PURE__ */ jsx(OrdersPanel, {}),
|
|
@@ -8595,6 +9386,27 @@ function MyPurchasesPage() {
|
|
|
8595
9386
|
}
|
|
8596
9387
|
);
|
|
8597
9388
|
}
|
|
9389
|
+
function initShippingItems() {
|
|
9390
|
+
setShopShippingItemsResolver(
|
|
9391
|
+
() => useCartStore.getState().lines.map((line) => ({
|
|
9392
|
+
quantity: line.quantity,
|
|
9393
|
+
weight: line.weight ?? null,
|
|
9394
|
+
weightUnit: line.weightUnit ?? null,
|
|
9395
|
+
height: line.heightCm ?? null,
|
|
9396
|
+
width: line.widthCm ?? null,
|
|
9397
|
+
length: line.lengthCm ?? null,
|
|
9398
|
+
packageId: line.packageId ?? null,
|
|
9399
|
+
// Undefined is a line added before this existed. Treated as physical:
|
|
9400
|
+
// freight wrongly charged is a complaint, freight wrongly skipped is a
|
|
9401
|
+
// parcel the merchant pays for.
|
|
9402
|
+
isPhysical: line.requiresShipping !== false,
|
|
9403
|
+
productId: line.productId,
|
|
9404
|
+
variantId: line.variantId,
|
|
9405
|
+
name: line.name,
|
|
9406
|
+
sku: line.sku
|
|
9407
|
+
}))
|
|
9408
|
+
);
|
|
9409
|
+
}
|
|
8598
9410
|
function NotFoundPage() {
|
|
8599
9411
|
const config = useStorefrontConfig();
|
|
8600
9412
|
useStorefrontHead({ title: `P\xE1gina n\xE3o encontrada \u2014 ${config.name}` });
|
|
@@ -8827,6 +9639,7 @@ function initStorefrontRuntime(config) {
|
|
|
8827
9639
|
}));
|
|
8828
9640
|
}
|
|
8829
9641
|
setShopProvider(resolveStorefrontProvider(config));
|
|
9642
|
+
initShippingItems();
|
|
8830
9643
|
const reconcileCart = () => {
|
|
8831
9644
|
const provider = getShopProvider();
|
|
8832
9645
|
void useCartStore.getState().reconcile(async (line) => {
|
|
@@ -8879,7 +9692,7 @@ function createStorefrontApp(config) {
|
|
|
8879
9692
|
initStorefrontRuntime(config);
|
|
8880
9693
|
const router = defaultRouterAdapter(resolved.routing ?? "hash");
|
|
8881
9694
|
function StorefrontApp() {
|
|
8882
|
-
return /* @__PURE__ */ jsx(
|
|
9695
|
+
return /* @__PURE__ */ jsx(StorefrontConfigHost, { config: resolved, children: /* @__PURE__ */ jsx(StorefrontShell, { router }) });
|
|
8883
9696
|
}
|
|
8884
9697
|
StorefrontApp.displayName = "StorefrontApp";
|
|
8885
9698
|
return StorefrontApp;
|
|
@@ -8902,7 +9715,7 @@ function createStorefront(options) {
|
|
|
8902
9715
|
// src/document/sections.ts
|
|
8903
9716
|
var STORE_DOCUMENT_KIND = "fayz.store";
|
|
8904
9717
|
var STORE_COMMERCE_MODES = ["checkout", "catalog", "enquiry"];
|
|
8905
|
-
var STORE_PAYMENT_MODES = ["mock", "pix-mercadopago"];
|
|
9718
|
+
var STORE_PAYMENT_MODES = ["mock", "pix-mercadopago", "gateway"];
|
|
8906
9719
|
var STORE_PAYMENT_METHOD_KINDS = [
|
|
8907
9720
|
"pix",
|
|
8908
9721
|
"credit_card",
|
|
@@ -10227,6 +11040,6 @@ ${formatErrors(report.errors)}`;
|
|
|
10227
11040
|
return config;
|
|
10228
11041
|
}
|
|
10229
11042
|
|
|
10230
|
-
export { BEFORE_PLACE_ORDER_VALVE, BLOCK_STRUCTURAL_KEYS, BenefitsRow, BlockDataError, CART_LINE_ANNOTATION_POINT, CATALOG_BODY_BLOCK, CHECKOUT_FIELD_POINT, CartDrawer, CatalogPage, CategoryShowcase, CheckoutPage, CollectionCta, CollectionHero, ContentPage, CountdownBand, EmailConfirmationRequiredError, EntityListBlock, FaqSection, FiltersPanel, FormBlock, HeroSection, ImageTiles, Link, ManifestoBlock, MediaCarousel, MotifStrip, MyPurchasesPage, NewsletterBand, ORDER_METADATA_POINT, OrderConfirmationPage, OrderTrackingTimeline, PAGE_DATA_SCRIPT_ID, PLACEHOLDER_ASPECTS, PRODUCT_BODY_BLOCK, PRODUCT_CARD_BADGE_POINT, PageDataScope, PaymentTerms, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGallery, ProductGrid, ProductOptionSelector, ProductRail, ProductReviews, ProductSlider, ProductSpecs, ProductSpotlight, PromoBanner, QuantityInput, Reveal, STOREFRONT_COMPONENT_KEYS, STOREFRONT_PAGE_KINDS, STOREFRONT_SLOTS, STORE_BACKEND_PROVIDERS, STORE_COMMERCE_MODES, STORE_DOCUMENT_KIND, STORE_PAYMENT_METHOD_KINDS, STORE_PAYMENT_MODES, STORE_ROUTE_CHROMES, STORE_ROUTE_KINDS, STORE_SECTIONS, STORE_SECTION_NAMES, SealsBand, SearchOverlay, Slot, SmoothImage, StepsSection, StorefrontConfigProvider, StorefrontFooter, StorefrontHeader, StorefrontPage, StorefrontRouterProvider, StorefrontShell, StorefrontThemeStyle, StoryQuote, THEME_SCALE_FIELDS, THEME_SCALE_VAR_PREFIX, TID, Testimonials, TierCards, bannerPlaceholder, blockInstanceId, buildEnquiryFromForm, checkPageBlocks, collectDataNeeds, collectOrderMetadata, contentPagePath, createStorefront, createStorefrontApp, currentStaticRenderPass, defaultPageBlocks, entityListBlockMeta, establishCustomerSession, exportStore, formatMoney, formatProductOptionSelection, getCartLineAnnotations, getCheckoutFieldContributions, getCustomerAuthAdapter, getProductCardBadges, getProductOptionGroups, hydrateStoreDocument, hydratedEntriesFor, initStorefrontRuntime, isJsonValue, isKnownPath, isNestedBlockRef, isPublicPath, isRecord, listSlots, matchContentPage, matchPath, matchesFacets, mergeCollectionFacet, mergePageTheme, navigateTo, nextMilestone, normalizeProductOptionSelection, orderGalleryImages, overrideComponent, overrideProps, pageSeo, pageThemeSelector, pickProductCardImage, placeStorefrontOrder, prefersReducedMotion, primaryImageUrl, productCardComponentContract, productJsonLd, productOptionSelectionKey, productPlaceholder, productSeo, readHydratedPageData, registerStorefrontBlocks, registerStorefrontEntities, registerStorefrontExtensionPoints, registerStorefrontLoaders, resetPageDiagnostics, resetProductOptionsDeprecationWarnings, resetStorefrontExtensionPoints, resolveAuthAdapter, resolveConfig, resolveDataParams, resolveOverride, resolvePageData, resolvePageDataNeed, resolvePaymentTerms, resolveStorefrontProvider, resolveStorefrontRoute, resolveVariantForSelection, roundCents, routePathParams, runBeforePlaceOrderValve, sectionToJsonSchema, sectionsToBlocks, selectCount, selectDiscountTotal, selectShipping, selectSubtotal, selectTotal, selectionPrice, serializePageData, shellRouterAdapter, signInByEmail, signOutCustomer, signUpCustomer, slugifyStoreName, storefrontComponentContracts, storefrontPaths, themeToCss, toBlockNodes, toFacetMap, useBlockData, useCartStore, useCatalogStore, useCategories, useDeliveryStore, useDiscountValidator, useEnquiry, useHashPath, useInView, useLoader, useMyOrders, useNavigate, usePopOnChange, useProduct, useProducts, useRoutePath, useRouterAdapter, useScrollToTopOnNavigate, useScrolled, useSessionStore, useSlotContext, useStorefrontActions, useStorefrontConfig, useStorefrontConfigOptional, useStorefrontHead, useStorefrontPage, useStorefrontPageOptional, usedSlots, validateStore, withFacet, withStaticRenderPass };
|
|
10231
|
-
//# sourceMappingURL=chunk-
|
|
10232
|
-
//# sourceMappingURL=chunk-
|
|
11043
|
+
export { BEFORE_PLACE_ORDER_VALVE, BLOCK_STRUCTURAL_KEYS, BenefitsRow, BlockDataError, CART_LINE_ANNOTATION_POINT, CATALOG_BODY_BLOCK, CHECKOUT_FIELD_POINT, CartDrawer, CatalogPage, CategoryShowcase, CheckoutPage, CollectionCta, CollectionHero, ContentPage, CountdownBand, EmailConfirmationRequiredError, EntityListBlock, FaqSection, FiltersPanel, FormBlock, HeroSection, ImageTiles, Link, ManifestoBlock, MediaCarousel, MotifStrip, MyPurchasesPage, NewsletterBand, ORDER_METADATA_POINT, OrderConfirmationPage, OrderTrackingTimeline, PAGE_DATA_SCRIPT_ID, PLACEHOLDER_ASPECTS, PRODUCT_BODY_BLOCK, PRODUCT_CARD_BADGE_POINT, PageDataScope, PaymentTerms, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGallery, ProductGrid, ProductOptionSelector, ProductRail, ProductReviews, ProductSlider, ProductSpecs, ProductSpotlight, PromoBanner, QuantityInput, Reveal, STOREFRONT_COMPONENT_KEYS, STOREFRONT_PAGE_KINDS, STOREFRONT_SLOTS, STORE_BACKEND_PROVIDERS, STORE_COMMERCE_MODES, STORE_DOCUMENT_KIND, STORE_PAYMENT_METHOD_KINDS, STORE_PAYMENT_MODES, STORE_ROUTE_CHROMES, STORE_ROUTE_KINDS, STORE_SECTIONS, STORE_SECTION_NAMES, SealsBand, SearchOverlay, Slot, SmoothImage, StepsSection, StorefrontConfigHost, StorefrontConfigProvider, StorefrontFooter, StorefrontHeader, StorefrontPage, StorefrontRouterProvider, StorefrontShell, StorefrontThemeStyle, StoryQuote, THEME_SCALE_FIELDS, THEME_SCALE_VAR_PREFIX, TID, Testimonials, TierCards, applyStoreSettings, bannerPlaceholder, blockInstanceId, buildEnquiryFromForm, checkPageBlocks, collectDataNeeds, collectOrderMetadata, contentPagePath, createStorefront, createStorefrontApp, currentStaticRenderPass, defaultPageBlocks, entityListBlockMeta, establishCustomerSession, exportStore, formatMoney, formatProductOptionSelection, getCartLineAnnotations, getCheckoutFieldContributions, getCustomerAuthAdapter, getProductCardBadges, getProductOptionGroups, hydrateStoreDocument, hydratedEntriesFor, initStorefrontRuntime, isJsonValue, isKnownPath, isNestedBlockRef, isPublicPath, isRecord, listSlots, matchContentPage, matchPath, matchesFacets, mergeCollectionFacet, mergePageTheme, navigateTo, nextMilestone, normalizeProductOptionSelection, orderGalleryImages, overrideComponent, overrideProps, pageSeo, pageThemeSelector, pickProductCardImage, placeStorefrontOrder, prefersReducedMotion, primaryImageUrl, productCardComponentContract, productJsonLd, productOptionSelectionKey, productPlaceholder, productSeo, readHydratedPageData, registerStorefrontBlocks, registerStorefrontEntities, registerStorefrontExtensionPoints, registerStorefrontLoaders, resetPageDiagnostics, resetProductOptionsDeprecationWarnings, resetStorefrontExtensionPoints, resolveAuthAdapter, resolveConfig, resolveDataParams, resolveOverride, resolvePageData, resolvePageDataNeed, resolvePaymentTerms, resolveStorefrontProvider, resolveStorefrontRoute, resolveVariantForSelection, roundCents, routePathParams, runBeforePlaceOrderValve, sectionToJsonSchema, sectionsToBlocks, selectCount, selectDiscountTotal, selectRequiresShipping, selectShipping, selectSubtotal, selectTotal, selectionPrice, serializePageData, shellRouterAdapter, signInByEmail, signOutCustomer, signUpCustomer, slugifyStoreName, storefrontComponentContracts, storefrontPaths, themeToCss, toBlockNodes, toFacetMap, useBlockData, useCartStore, useCatalogStore, useCategories, useDeliveryStore, useDiscountValidator, useEnquiry, useHashPath, useInView, useLoader, useMyOrders, useNavigate, usePopOnChange, useProduct, useProducts, useRoutePath, useRouterAdapter, useScrollToTopOnNavigate, useScrolled, useSessionStore, useSlotContext, useStorefrontActions, useStorefrontConfig, useStorefrontConfigOptional, useStorefrontHead, useStorefrontPage, useStorefrontPageOptional, usedSlots, validateStore, withFacet, withStaticRenderPass };
|
|
11044
|
+
//# sourceMappingURL=chunk-SDQHSUFC.js.map
|
|
11045
|
+
//# sourceMappingURL=chunk-SDQHSUFC.js.map
|