@fayz-ai/storefront 0.10.0 → 0.11.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-SX3J3QIT.js} +1399 -585
- package/dist/chunk-SX3J3QIT.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/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, 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
|
}
|
|
@@ -6906,6 +6973,7 @@ function CartDrawer() {
|
|
|
6906
6973
|
const freeAbove = config.shipping.freeAbove;
|
|
6907
6974
|
const missingForFree = freeAbove != null && subtotal < freeAbove ? freeAbove - subtotal : 0;
|
|
6908
6975
|
const progress = freeAbove != null ? Math.min(100, subtotal / freeAbove * 100) : 0;
|
|
6976
|
+
const wide = cart.lines.length >= 3;
|
|
6909
6977
|
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50", children: [
|
|
6910
6978
|
/* @__PURE__ */ jsx(
|
|
6911
6979
|
"div",
|
|
@@ -6921,11 +6989,14 @@ function CartDrawer() {
|
|
|
6921
6989
|
"data-testid": TID.cartDrawer,
|
|
6922
6990
|
role: "dialog",
|
|
6923
6991
|
"aria-label": "Carrinho",
|
|
6924
|
-
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 ${// The 2-column desktop layout only makes sense with items to
|
|
6925
|
-
//
|
|
6926
|
-
|
|
6992
|
+
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 ${// The 2-column desktop layout only makes sense with items to FILL it.
|
|
6993
|
+
// At one or two lines the left column was a 700px void with a card
|
|
6994
|
+
// floating at the top — the panel read as broken, not as spacious.
|
|
6995
|
+
// Below the threshold the drawer stays the narrow single column every
|
|
6996
|
+
// store uses, which is also the shape a cart of one item deserves.
|
|
6997
|
+
wide ? "lg:max-w-3xl lg:grid lg:grid-cols-[55%_45%] lg:grid-rows-[auto_auto_1fr]" : ""}`,
|
|
6927
6998
|
children: [
|
|
6928
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
6999
|
+
/* @__PURE__ */ jsxs("div", { className: `flex items-center justify-between border-b px-5 py-4 ${wide ? "lg:col-start-2 lg:row-start-1" : ""}`, children: [
|
|
6929
7000
|
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Carrinho" }),
|
|
6930
7001
|
/* @__PURE__ */ jsx(
|
|
6931
7002
|
"button",
|
|
@@ -6933,13 +7004,13 @@ function CartDrawer() {
|
|
|
6933
7004
|
type: "button",
|
|
6934
7005
|
"aria-label": "Fechar carrinho",
|
|
6935
7006
|
onClick: cart.closeDrawer,
|
|
6936
|
-
className: "rounded-full p-1.5 hover:bg-muted",
|
|
7007
|
+
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
7008
|
children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
|
|
6938
7009
|
}
|
|
6939
7010
|
)
|
|
6940
7011
|
] }),
|
|
6941
7012
|
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:
|
|
7013
|
+
freeAbove != null && /* @__PURE__ */ jsxs("div", { "data-testid": TID.shippingProgress, className: `border-b bg-muted/40 px-5 py-3 ${wide ? "lg:col-start-2 lg:row-start-2" : ""}`, children: [
|
|
6943
7014
|
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium", children: missingForFree > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6944
7015
|
"Faltam ",
|
|
6945
7016
|
/* @__PURE__ */ jsx("strong", { children: money(missingForFree) }),
|
|
@@ -6961,7 +7032,7 @@ function CartDrawer() {
|
|
|
6961
7032
|
"div",
|
|
6962
7033
|
{
|
|
6963
7034
|
"data-testid": TID.cartLinesList,
|
|
6964
|
-
className:
|
|
7035
|
+
className: `flex-1 space-y-4 overflow-y-auto py-4 pl-5 pr-6 ${wide ? "lg:col-start-1 lg:row-start-1 lg:row-span-3 lg:border-r" : ""}`,
|
|
6965
7036
|
style: { scrollbarGutter: "stable" },
|
|
6966
7037
|
children: [
|
|
6967
7038
|
/* @__PURE__ */ jsx(Slot, { area: "cart", name: "beforeLines", context: cartSlotContext }),
|
|
@@ -6984,7 +7055,7 @@ function CartDrawer() {
|
|
|
6984
7055
|
"data-testid": TID.lineRemove,
|
|
6985
7056
|
"aria-label": `Remover ${line.name}`,
|
|
6986
7057
|
onClick: () => cart.removeItem(line.lineId ?? line.productId),
|
|
6987
|
-
className: "-m-1.5 p-1.5 text-muted-foreground hover:text-destructive",
|
|
7058
|
+
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
7059
|
children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
|
|
6989
7060
|
}
|
|
6990
7061
|
)
|
|
@@ -7025,7 +7096,7 @@ function CartDrawer() {
|
|
|
7025
7096
|
"div",
|
|
7026
7097
|
{
|
|
7027
7098
|
"data-testid": TID.cartFooter,
|
|
7028
|
-
className:
|
|
7099
|
+
className: `space-y-3 border-t px-5 py-4 ${wide ? "lg:col-start-2 lg:row-start-3 lg:overflow-y-auto" : ""}`,
|
|
7029
7100
|
children: [
|
|
7030
7101
|
config.features.discounts && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
7031
7102
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
@@ -7055,7 +7126,7 @@ function CartDrawer() {
|
|
|
7055
7126
|
discountError && /* @__PURE__ */ jsx("p", { "data-testid": TID.discountError, className: "text-xs text-destructive", children: discountError })
|
|
7056
7127
|
] }),
|
|
7057
7128
|
/* @__PURE__ */ jsx(Slot, { area: "cart", name: "beforeSummary", context: cartSlotContext }),
|
|
7058
|
-
/* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(DeliveryEstimator, { compact: true }) }),
|
|
7129
|
+
selectRequiresShipping(cart) && /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(DeliveryEstimator, { compact: true }) }),
|
|
7059
7130
|
/* @__PURE__ */ jsxs("dl", { className: "space-y-1.5 text-sm", children: [
|
|
7060
7131
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
7061
7132
|
/* @__PURE__ */ jsx("dt", { className: "text-muted-foreground", children: "Subtotal" }),
|
|
@@ -7197,6 +7268,41 @@ async function storefrontPaths(input) {
|
|
|
7197
7268
|
const unique = Array.from(new Set(paths));
|
|
7198
7269
|
return input.includePrivate ? unique : unique.filter((p) => isPublicPath(resolved, p));
|
|
7199
7270
|
}
|
|
7271
|
+
|
|
7272
|
+
// src/data/local-orders.ts
|
|
7273
|
+
var KEY = "fayz.storefront.orders.v1";
|
|
7274
|
+
var MAX = 25;
|
|
7275
|
+
function read() {
|
|
7276
|
+
try {
|
|
7277
|
+
const raw = globalThis.localStorage?.getItem(KEY);
|
|
7278
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
7279
|
+
return Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : [];
|
|
7280
|
+
} catch {
|
|
7281
|
+
return [];
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
function rememberLocalOrder(orderId) {
|
|
7285
|
+
if (!orderId) return;
|
|
7286
|
+
try {
|
|
7287
|
+
const next = [orderId, ...read().filter((id) => id !== orderId)].slice(0, MAX);
|
|
7288
|
+
globalThis.localStorage?.setItem(KEY, JSON.stringify(next));
|
|
7289
|
+
} catch {
|
|
7290
|
+
}
|
|
7291
|
+
}
|
|
7292
|
+
function listLocalOrderIds() {
|
|
7293
|
+
return read();
|
|
7294
|
+
}
|
|
7295
|
+
|
|
7296
|
+
// src/lib/errors.ts
|
|
7297
|
+
function errorMessage(err) {
|
|
7298
|
+
if (err instanceof Error) return err.message;
|
|
7299
|
+
if (err && typeof err === "object" && typeof err.message === "string") {
|
|
7300
|
+
return err.message;
|
|
7301
|
+
}
|
|
7302
|
+
return "";
|
|
7303
|
+
}
|
|
7304
|
+
|
|
7305
|
+
// src/workflows/checkout.ts
|
|
7200
7306
|
function formatDeliveryNotes(address, shippingAddress) {
|
|
7201
7307
|
if (address?.notes?.trim()) return address.notes.trim();
|
|
7202
7308
|
if (shippingAddress) return void 0;
|
|
@@ -7204,6 +7310,10 @@ function formatDeliveryNotes(address, shippingAddress) {
|
|
|
7204
7310
|
const parts = [address.street, address.city, address.zip].map((part) => part?.trim()).filter(Boolean);
|
|
7205
7311
|
return parts.length > 0 ? `Entrega: ${parts.join(", ")}` : void 0;
|
|
7206
7312
|
}
|
|
7313
|
+
function isStaleCustomerError(err) {
|
|
7314
|
+
const message = errorMessage(err).toLowerCase();
|
|
7315
|
+
return message.includes("customer email mismatch") || message.includes("customer does not belong to this store") || message.includes("customer belongs to another account");
|
|
7316
|
+
}
|
|
7207
7317
|
async function placeStorefrontOrder({
|
|
7208
7318
|
config,
|
|
7209
7319
|
cart,
|
|
@@ -7218,7 +7328,7 @@ async function placeStorefrontOrder({
|
|
|
7218
7328
|
const name = customer.name.trim();
|
|
7219
7329
|
const provider = getShopProvider();
|
|
7220
7330
|
let customerId = session?.customerId ?? null;
|
|
7221
|
-
if (!customerId || session?.email !== email) {
|
|
7331
|
+
if (!customerId || session?.email?.trim().toLowerCase() !== email) {
|
|
7222
7332
|
const established = await establishCustomerSession(email, { name });
|
|
7223
7333
|
customerId = established.customerId;
|
|
7224
7334
|
}
|
|
@@ -7251,8 +7361,8 @@ async function placeStorefrontOrder({
|
|
|
7251
7361
|
subtotal: cart.lines.reduce((sum, line) => sum + line.unitPrice * line.quantity, 0),
|
|
7252
7362
|
customerEmail: email
|
|
7253
7363
|
});
|
|
7254
|
-
const
|
|
7255
|
-
customerId:
|
|
7364
|
+
const place = (withCustomerId) => provider.placeOrder({
|
|
7365
|
+
customerId: withCustomerId,
|
|
7256
7366
|
customer: { name, email },
|
|
7257
7367
|
currency: config.currency,
|
|
7258
7368
|
notes,
|
|
@@ -7266,10 +7376,19 @@ async function placeStorefrontOrder({
|
|
|
7266
7376
|
metadata,
|
|
7267
7377
|
items
|
|
7268
7378
|
});
|
|
7379
|
+
let order;
|
|
7380
|
+
try {
|
|
7381
|
+
order = await place(customerId ?? void 0);
|
|
7382
|
+
} catch (err) {
|
|
7383
|
+
if (!customerId || !isStaleCustomerError(err)) throw err;
|
|
7384
|
+
customerId = null;
|
|
7385
|
+
order = await place(void 0);
|
|
7386
|
+
}
|
|
7269
7387
|
if (markPaid) {
|
|
7270
7388
|
if (provider.confirmPayment) await provider.confirmPayment(order.id);
|
|
7271
7389
|
else await provider.updateOrder(order.id, { financialStatus: "paid" });
|
|
7272
7390
|
}
|
|
7391
|
+
rememberLocalOrder(order.id);
|
|
7273
7392
|
return { order, customerId: customerId ?? order.customerId ?? "" };
|
|
7274
7393
|
}
|
|
7275
7394
|
function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
|
|
@@ -7382,40 +7501,364 @@ function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
|
|
|
7382
7501
|
}
|
|
7383
7502
|
);
|
|
7384
7503
|
}
|
|
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" }
|
|
7504
|
+
var LABELS2 = {
|
|
7505
|
+
1: "Dados",
|
|
7506
|
+
2: "Entrega",
|
|
7507
|
+
3: "Pagamento",
|
|
7508
|
+
4: "Pronto"
|
|
7392
7509
|
};
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
{
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7510
|
+
function CheckoutSteps({ current, onGoBackTo }) {
|
|
7511
|
+
const steps = [1, 2, 3, 4];
|
|
7512
|
+
return /* @__PURE__ */ jsxs("nav", { "data-testid": TID.checkoutSteps, "aria-label": "Etapas do checkout", className: "mb-8", children: [
|
|
7513
|
+
/* @__PURE__ */ jsx("ol", { className: "flex items-center gap-1.5 sm:gap-2", children: steps.map((step, index) => {
|
|
7514
|
+
const done = step < current;
|
|
7515
|
+
const active2 = step === current;
|
|
7516
|
+
const canGoBack = done && !!onGoBackTo;
|
|
7517
|
+
const Tag = canGoBack ? "button" : "div";
|
|
7518
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex min-w-0 flex-1 items-center gap-1.5 sm:gap-2", children: [
|
|
7519
|
+
/* @__PURE__ */ jsxs(
|
|
7520
|
+
Tag,
|
|
7521
|
+
{
|
|
7522
|
+
...canGoBack ? { type: "button", onClick: () => onGoBackTo(step) } : {},
|
|
7523
|
+
"data-testid": TID.checkoutStep(step),
|
|
7524
|
+
"data-state": done ? "done" : active2 ? "current" : "pending",
|
|
7525
|
+
"aria-current": active2 ? "step" : void 0,
|
|
7526
|
+
className: [
|
|
7527
|
+
"flex min-w-0 items-center gap-2 rounded-lg px-2 py-1.5 text-left transition",
|
|
7528
|
+
canGoBack ? "hover:bg-muted" : "",
|
|
7529
|
+
active2 ? "text-foreground" : done ? "text-foreground" : "text-muted-foreground"
|
|
7530
|
+
].join(" "),
|
|
7531
|
+
children: [
|
|
7532
|
+
/* @__PURE__ */ jsx(
|
|
7533
|
+
"span",
|
|
7534
|
+
{
|
|
7535
|
+
className: [
|
|
7536
|
+
"grid h-7 w-7 shrink-0 place-items-center rounded-full border text-xs font-semibold",
|
|
7537
|
+
done ? "border-primary bg-primary text-primary-foreground" : active2 ? "border-primary bg-background text-primary" : "border-muted-foreground/30 bg-background"
|
|
7538
|
+
].join(" "),
|
|
7539
|
+
children: done ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : step
|
|
7540
|
+
}
|
|
7541
|
+
),
|
|
7542
|
+
/* @__PURE__ */ jsx("span", { className: `hidden truncate text-sm sm:inline ${active2 ? "font-semibold" : ""}`, children: LABELS2[step] })
|
|
7543
|
+
]
|
|
7544
|
+
}
|
|
7545
|
+
),
|
|
7546
|
+
index < steps.length - 1 && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: `h-px flex-1 ${done ? "bg-primary" : "bg-border"}` })
|
|
7547
|
+
] }, step);
|
|
7548
|
+
}) }),
|
|
7549
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-2 text-sm font-semibold sm:hidden", children: [
|
|
7550
|
+
current,
|
|
7551
|
+
". ",
|
|
7552
|
+
LABELS2[current]
|
|
7553
|
+
] })
|
|
7554
|
+
] });
|
|
7555
|
+
}
|
|
7556
|
+
function Skeleton({ className = "", style }) {
|
|
7557
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: `block rounded bg-muted ${className}`, style });
|
|
7558
|
+
}
|
|
7559
|
+
function SkeletonGroup({ label, className = "", children, ...rest }) {
|
|
7401
7560
|
return /* @__PURE__ */ jsxs(
|
|
7402
7561
|
"div",
|
|
7403
7562
|
{
|
|
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
7563
|
role: "status",
|
|
7407
|
-
"aria-
|
|
7564
|
+
"aria-busy": "true",
|
|
7565
|
+
className: `animate-pulse motion-reduce:animate-none ${className}`,
|
|
7566
|
+
...rest,
|
|
7408
7567
|
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" })
|
|
7568
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
|
|
7569
|
+
children
|
|
7415
7570
|
]
|
|
7416
7571
|
}
|
|
7417
7572
|
);
|
|
7418
7573
|
}
|
|
7574
|
+
function PulseDot({ className = "" }) {
|
|
7575
|
+
return /* @__PURE__ */ jsxs("span", { "aria-hidden": true, className: `relative flex h-2.5 w-2.5 shrink-0 ${className}`, children: [
|
|
7576
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 animate-ping rounded-full bg-current opacity-60 motion-reduce:animate-none" }),
|
|
7577
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-current" })
|
|
7578
|
+
] });
|
|
7579
|
+
}
|
|
7580
|
+
var POLL_MS = 3e3;
|
|
7581
|
+
function CheckoutPayment({ input, onPaid, onBack, money, onUnavailable }) {
|
|
7582
|
+
const [phase, setPhase] = useState("opening");
|
|
7583
|
+
const [charge, setCharge] = useState(null);
|
|
7584
|
+
const [total, setTotal] = useState(null);
|
|
7585
|
+
const [sessionId, setSessionId] = useState(null);
|
|
7586
|
+
const [error, setError] = useState(null);
|
|
7587
|
+
const [copied, setCopied] = useState(false);
|
|
7588
|
+
const [checking, setChecking] = useState(false);
|
|
7589
|
+
const settled = useRef(false);
|
|
7590
|
+
const opened = useRef(false);
|
|
7591
|
+
const open = useCallback(async () => {
|
|
7592
|
+
const provider = getShopProvider();
|
|
7593
|
+
if (!provider.openCheckoutSession) {
|
|
7594
|
+
setError("Esta loja ainda n\xE3o recebe pagamento online.");
|
|
7595
|
+
setPhase("error");
|
|
7596
|
+
return;
|
|
7597
|
+
}
|
|
7598
|
+
setPhase("opening");
|
|
7599
|
+
setError(null);
|
|
7600
|
+
try {
|
|
7601
|
+
const session = await provider.openCheckoutSession(input);
|
|
7602
|
+
setSessionId(session.sessionId);
|
|
7603
|
+
setTotal(session.total);
|
|
7604
|
+
setCharge(session.charge);
|
|
7605
|
+
setPhase(session.charge ? "waiting" : "error");
|
|
7606
|
+
if (!session.charge) setError("N\xE3o conseguimos abrir a cobran\xE7a agora.");
|
|
7607
|
+
} catch (err) {
|
|
7608
|
+
if (err?.status === 404) {
|
|
7609
|
+
onUnavailable?.();
|
|
7610
|
+
return;
|
|
7611
|
+
}
|
|
7612
|
+
setError(err instanceof Error ? err.message : "N\xE3o conseguimos abrir a cobran\xE7a agora.");
|
|
7613
|
+
setPhase("error");
|
|
7614
|
+
}
|
|
7615
|
+
}, [input, onUnavailable]);
|
|
7616
|
+
useEffect(() => {
|
|
7617
|
+
if (opened.current) return;
|
|
7618
|
+
opened.current = true;
|
|
7619
|
+
void open();
|
|
7620
|
+
}, [open]);
|
|
7621
|
+
const check = useCallback(async () => {
|
|
7622
|
+
if (!sessionId || settled.current) return false;
|
|
7623
|
+
const provider = getShopProvider();
|
|
7624
|
+
if (!provider.settleCheckoutSession) return false;
|
|
7625
|
+
try {
|
|
7626
|
+
const result = await provider.settleCheckoutSession(sessionId);
|
|
7627
|
+
if (settled.current) return false;
|
|
7628
|
+
if (result.state === "paid" && result.orderId) {
|
|
7629
|
+
settled.current = true;
|
|
7630
|
+
setPhase("settling");
|
|
7631
|
+
onPaid(result.orderId);
|
|
7632
|
+
return true;
|
|
7633
|
+
}
|
|
7634
|
+
if (result.state === "failed" || result.state === "expired") {
|
|
7635
|
+
setError(
|
|
7636
|
+
result.reason ?? (result.state === "expired" ? "O c\xF3digo expirou. Gere um novo para continuar." : "O pagamento n\xE3o foi conclu\xEDdo.")
|
|
7637
|
+
);
|
|
7638
|
+
setPhase("error");
|
|
7639
|
+
}
|
|
7640
|
+
return false;
|
|
7641
|
+
} catch {
|
|
7642
|
+
return false;
|
|
7643
|
+
}
|
|
7644
|
+
}, [sessionId, onPaid]);
|
|
7645
|
+
useEffect(() => {
|
|
7646
|
+
if (phase !== "waiting" || !sessionId) return;
|
|
7647
|
+
let stopped = false;
|
|
7648
|
+
const timer = setInterval(() => {
|
|
7649
|
+
if (!stopped) void check();
|
|
7650
|
+
}, POLL_MS);
|
|
7651
|
+
return () => {
|
|
7652
|
+
stopped = true;
|
|
7653
|
+
clearInterval(timer);
|
|
7654
|
+
};
|
|
7655
|
+
}, [phase, sessionId, check]);
|
|
7656
|
+
async function confirmPaid() {
|
|
7657
|
+
setChecking(true);
|
|
7658
|
+
const paid = await check();
|
|
7659
|
+
if (!paid) setChecking(false);
|
|
7660
|
+
}
|
|
7661
|
+
async function copy() {
|
|
7662
|
+
if (!charge?.emv) return;
|
|
7663
|
+
try {
|
|
7664
|
+
await navigator.clipboard.writeText(charge.emv);
|
|
7665
|
+
setCopied(true);
|
|
7666
|
+
setTimeout(() => setCopied(false), 2400);
|
|
7667
|
+
} catch {
|
|
7668
|
+
toast.error("Copie o c\xF3digo manualmente");
|
|
7669
|
+
}
|
|
7670
|
+
}
|
|
7671
|
+
if (phase === "opening") {
|
|
7672
|
+
return (
|
|
7673
|
+
// Same layout as the charge below, so nothing jumps when it arrives.
|
|
7674
|
+
/* @__PURE__ */ jsxs(
|
|
7675
|
+
SkeletonGroup,
|
|
7676
|
+
{
|
|
7677
|
+
label: "Preparando seu pagamento",
|
|
7678
|
+
"data-testid": TID.checkoutPayment,
|
|
7679
|
+
className: "rounded-xl border bg-card p-6",
|
|
7680
|
+
children: [
|
|
7681
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
7682
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-5 rounded-md" }),
|
|
7683
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-64 max-w-[70%]" })
|
|
7684
|
+
] }),
|
|
7685
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3.5 w-full max-w-sm" }),
|
|
7686
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 grid gap-5 sm:grid-cols-[auto_minmax(0,1fr)] sm:items-start", children: [
|
|
7687
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mx-auto h-44 w-44 rounded-lg" }),
|
|
7688
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
7689
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" }),
|
|
7690
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-4 h-3 w-28" }),
|
|
7691
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1 h-16 w-full rounded-lg" }),
|
|
7692
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-3", children: [
|
|
7693
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-11 w-40 rounded-lg" }),
|
|
7694
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-11 w-32 rounded-lg" })
|
|
7695
|
+
] })
|
|
7696
|
+
] })
|
|
7697
|
+
] })
|
|
7698
|
+
]
|
|
7699
|
+
}
|
|
7700
|
+
)
|
|
7701
|
+
);
|
|
7702
|
+
}
|
|
7703
|
+
if (phase === "error") {
|
|
7704
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": TID.checkoutPayment, className: "space-y-3 rounded-xl border bg-card p-6", children: [
|
|
7705
|
+
/* @__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." }),
|
|
7706
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
7707
|
+
/* @__PURE__ */ jsx(
|
|
7708
|
+
"button",
|
|
7709
|
+
{
|
|
7710
|
+
type: "button",
|
|
7711
|
+
onClick: () => {
|
|
7712
|
+
opened.current = false;
|
|
7713
|
+
void open();
|
|
7714
|
+
},
|
|
7715
|
+
className: "rounded-lg bg-primary px-4 py-2.5 text-sm font-semibold text-primary-foreground transition hover:opacity-90",
|
|
7716
|
+
children: "Tentar de novo"
|
|
7717
|
+
}
|
|
7718
|
+
),
|
|
7719
|
+
/* @__PURE__ */ jsx(
|
|
7720
|
+
"button",
|
|
7721
|
+
{
|
|
7722
|
+
type: "button",
|
|
7723
|
+
onClick: onBack,
|
|
7724
|
+
className: "rounded-lg border px-4 py-2.5 text-sm font-medium transition hover:bg-muted",
|
|
7725
|
+
children: "Voltar"
|
|
7726
|
+
}
|
|
7727
|
+
)
|
|
7728
|
+
] })
|
|
7729
|
+
] });
|
|
7730
|
+
}
|
|
7731
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": TID.checkoutPayment, className: "space-y-4", "aria-live": "polite", children: [
|
|
7732
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-card p-6", children: [
|
|
7733
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
7734
|
+
/* @__PURE__ */ jsx(QrCode, { className: "h-5 w-5 text-primary" }),
|
|
7735
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Pague com Pix para fechar o pedido" })
|
|
7736
|
+
] }),
|
|
7737
|
+
/* @__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." }),
|
|
7738
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 grid gap-5 sm:grid-cols-[auto_minmax(0,1fr)] sm:items-start", children: [
|
|
7739
|
+
charge?.qrcodeBase64 && /* @__PURE__ */ jsx(
|
|
7740
|
+
"img",
|
|
7741
|
+
{
|
|
7742
|
+
src: `data:image/png;base64,${charge.qrcodeBase64}`,
|
|
7743
|
+
alt: "QR code do Pix",
|
|
7744
|
+
className: "mx-auto h-44 w-44 rounded-lg border bg-white p-2"
|
|
7745
|
+
}
|
|
7746
|
+
),
|
|
7747
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
7748
|
+
total != null && /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
7749
|
+
"Valor: ",
|
|
7750
|
+
/* @__PURE__ */ jsx("strong", { "data-testid": TID.checkoutPaymentTotal, children: money(total) })
|
|
7751
|
+
] }),
|
|
7752
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: "Pix copia e cola" }),
|
|
7753
|
+
/* @__PURE__ */ jsx(
|
|
7754
|
+
"p",
|
|
7755
|
+
{
|
|
7756
|
+
"data-testid": TID.pixCode,
|
|
7757
|
+
className: "mt-1 max-h-24 overflow-y-auto break-all rounded-lg border bg-muted/40 p-3 font-mono text-xs",
|
|
7758
|
+
children: charge?.emv
|
|
7759
|
+
}
|
|
7760
|
+
),
|
|
7761
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-3", children: [
|
|
7762
|
+
/* @__PURE__ */ jsxs(
|
|
7763
|
+
"button",
|
|
7764
|
+
{
|
|
7765
|
+
type: "button",
|
|
7766
|
+
onClick: copy,
|
|
7767
|
+
"data-testid": TID.pixCopy,
|
|
7768
|
+
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",
|
|
7769
|
+
children: [
|
|
7770
|
+
copied ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" }),
|
|
7771
|
+
copied ? "C\xF3digo copiado" : "Copiar c\xF3digo"
|
|
7772
|
+
]
|
|
7773
|
+
}
|
|
7774
|
+
),
|
|
7775
|
+
/* @__PURE__ */ jsxs(
|
|
7776
|
+
"button",
|
|
7777
|
+
{
|
|
7778
|
+
type: "button",
|
|
7779
|
+
onClick: () => {
|
|
7780
|
+
void confirmPaid();
|
|
7781
|
+
},
|
|
7782
|
+
disabled: checking || phase === "settling",
|
|
7783
|
+
"data-testid": TID.pixConfirm,
|
|
7784
|
+
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",
|
|
7785
|
+
children: [
|
|
7786
|
+
checking || phase === "settling" ? /* @__PURE__ */ jsx(PulseDot, {}) : /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }),
|
|
7787
|
+
phase === "settling" ? "Confirmando\u2026" : checking ? "Verificando\u2026" : "J\xE1 paguei"
|
|
7788
|
+
]
|
|
7789
|
+
}
|
|
7790
|
+
)
|
|
7791
|
+
] })
|
|
7792
|
+
] })
|
|
7793
|
+
] })
|
|
7794
|
+
] }),
|
|
7795
|
+
/* @__PURE__ */ jsx(
|
|
7796
|
+
"button",
|
|
7797
|
+
{
|
|
7798
|
+
type: "button",
|
|
7799
|
+
onClick: onBack,
|
|
7800
|
+
className: "text-sm text-muted-foreground underline hover:text-foreground",
|
|
7801
|
+
children: "Voltar e revisar o pedido"
|
|
7802
|
+
}
|
|
7803
|
+
)
|
|
7804
|
+
] });
|
|
7805
|
+
}
|
|
7806
|
+
var CONFETTI_COLORS = ["hsl(var(--primary))", "#f59e0b", "#10b981", "#3b82f6", "#ec4899"];
|
|
7807
|
+
function SuccessMark() {
|
|
7808
|
+
const reduced = prefersReducedMotion();
|
|
7809
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative mx-auto h-24 w-24", children: [
|
|
7810
|
+
!reduced && Array.from({ length: 14 }, (_, i) => /* @__PURE__ */ jsx(
|
|
7811
|
+
"span",
|
|
7812
|
+
{
|
|
7813
|
+
"aria-hidden": true,
|
|
7814
|
+
className: "absolute h-2 w-2 animate-confetti-fall rounded-sm",
|
|
7815
|
+
style: {
|
|
7816
|
+
left: `${i * 7.3 % 100}%`,
|
|
7817
|
+
top: "-4px",
|
|
7818
|
+
backgroundColor: CONFETTI_COLORS[i % CONFETTI_COLORS.length],
|
|
7819
|
+
animationDelay: `${200 + i % 7 * 90}ms`
|
|
7820
|
+
}
|
|
7821
|
+
},
|
|
7822
|
+
i
|
|
7823
|
+
)),
|
|
7824
|
+
/* @__PURE__ */ jsx("span", { className: `absolute inset-0 rounded-full bg-emerald-100 ${reduced ? "" : "animate-pop-in"}` }),
|
|
7825
|
+
/* @__PURE__ */ jsx("svg", { viewBox: "0 0 48 48", className: "absolute inset-0 h-full w-full p-6", "aria-hidden": true, children: /* @__PURE__ */ jsx(
|
|
7826
|
+
"path",
|
|
7827
|
+
{
|
|
7828
|
+
d: "M10 25 L20 35 L38 14",
|
|
7829
|
+
fill: "none",
|
|
7830
|
+
stroke: "rgb(5 150 105)",
|
|
7831
|
+
strokeWidth: "5",
|
|
7832
|
+
strokeLinecap: "round",
|
|
7833
|
+
strokeLinejoin: "round",
|
|
7834
|
+
strokeDasharray: "48",
|
|
7835
|
+
className: reduced ? "" : "animate-draw-check"
|
|
7836
|
+
}
|
|
7837
|
+
) })
|
|
7838
|
+
] });
|
|
7839
|
+
}
|
|
7840
|
+
var CHARGEABLE_PROVIDERS = ["abacatepay"];
|
|
7841
|
+
var PAYMENT_LABELS = {
|
|
7842
|
+
pix: { label: "Pix", hint: "Voc\xEA recebe a chave para pagar ap\xF3s confirmar o pedido" },
|
|
7843
|
+
credit_card: { label: "Cart\xE3o de cr\xE9dito", hint: "Maquininha na entrega" },
|
|
7844
|
+
debit_card: { label: "Cart\xE3o de d\xE9bito", hint: "Maquininha na entrega" },
|
|
7845
|
+
boleto: { label: "Boleto", hint: "Enviado por e-mail ap\xF3s a confirma\xE7\xE3o" },
|
|
7846
|
+
cash: { label: "Dinheiro", hint: "Pagamento na entrega" },
|
|
7847
|
+
other: { label: "Combinar com a loja", hint: "A loja entra em contato para acertar o pagamento" }
|
|
7848
|
+
};
|
|
7849
|
+
function placeOrderMessage(err) {
|
|
7850
|
+
const message = errorMessage(err);
|
|
7851
|
+
if (/customer (email mismatch|does not belong|belongs to another)/i.test(message)) {
|
|
7852
|
+
return "Sua sess\xE3o ficou presa a outro cadastro. Recarregue a p\xE1gina e tente de novo.";
|
|
7853
|
+
}
|
|
7854
|
+
if (/out of stock|insufficient|estoque/i.test(message)) {
|
|
7855
|
+
return "Um item do carrinho ficou sem estoque. Revise o carrinho e tente de novo.";
|
|
7856
|
+
}
|
|
7857
|
+
if (/delivery not available|postal code/i.test(message)) {
|
|
7858
|
+
return "Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.";
|
|
7859
|
+
}
|
|
7860
|
+
return message.replace(/^shop_[a-z_]+:\s*/i, "") || "N\xE3o foi poss\xEDvel concluir o pedido.";
|
|
7861
|
+
}
|
|
7419
7862
|
function field(label, testId, value, onChange, props) {
|
|
7420
7863
|
return /* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
7421
7864
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
|
|
@@ -7440,13 +7883,22 @@ function CheckoutPage() {
|
|
|
7440
7883
|
const validateDiscount2 = useDiscountValidator();
|
|
7441
7884
|
useStorefrontHead({ title: `Checkout \u2014 ${config.name}` });
|
|
7442
7885
|
const paymentMethods = config.payments.methods;
|
|
7886
|
+
const policy = config.policy;
|
|
7887
|
+
const requiresAccount = policy?.guestCheckout === false;
|
|
7888
|
+
const fieldPolicy = policy?.checkoutFields ?? {};
|
|
7889
|
+
const gatewayCheckout = config.payments.mode === "gateway" || !!policy?.paymentProvider;
|
|
7890
|
+
const shows = (key) => key === "document" ? gatewayCheckout || fieldPolicy.document?.visible === true : fieldPolicy[key]?.visible !== false;
|
|
7891
|
+
const requires = (key) => key === "document" ? gatewayCheckout || fieldPolicy.document?.visible === true && fieldPolicy.document?.required === true : fieldPolicy[key]?.visible !== false && fieldPolicy[key]?.required === true;
|
|
7443
7892
|
const [selectedAddressId, setSelectedAddressId] = useState("new");
|
|
7444
7893
|
const [savedAddresses, setSavedAddresses] = useState([]);
|
|
7894
|
+
const [addressesLoading, setAddressesLoading] = useState(false);
|
|
7445
7895
|
const [addressMode, setAddressMode] = useState("new");
|
|
7446
7896
|
const [paymentMethod, setPaymentMethod] = useState(paymentMethods[0] ?? "pix");
|
|
7447
7897
|
const [form, setForm] = useState({
|
|
7448
7898
|
email: session.email ?? "",
|
|
7449
7899
|
name: session.name ?? "",
|
|
7900
|
+
phone: "",
|
|
7901
|
+
document: "",
|
|
7450
7902
|
street: "",
|
|
7451
7903
|
city: "",
|
|
7452
7904
|
zip: "",
|
|
@@ -7455,33 +7907,65 @@ function CheckoutPage() {
|
|
|
7455
7907
|
district: "",
|
|
7456
7908
|
state: ""
|
|
7457
7909
|
});
|
|
7910
|
+
const [step, setStep] = useState(1);
|
|
7911
|
+
const [paidOrderId, setPaidOrderId] = useState(null);
|
|
7458
7912
|
const [error, setError] = useState(null);
|
|
7459
7913
|
const [discountCode, setDiscountCode] = useState("");
|
|
7460
7914
|
const [discountError, setDiscountError] = useState(null);
|
|
7461
7915
|
const [discountSuccess, setDiscountSuccess] = useState(null);
|
|
7462
7916
|
const [applyingDiscount, setApplyingDiscount] = useState(false);
|
|
7463
|
-
const [placing, setPlacing] = useState(false);
|
|
7464
|
-
const [processingStep, setProcessingStep] = useState(0);
|
|
7465
7917
|
const [showSignin, setShowSignin] = useState(false);
|
|
7918
|
+
const [placing, setPlacing] = useState(false);
|
|
7919
|
+
const leaving = React4.useRef(false);
|
|
7466
7920
|
const [zipLookup, setZipLookup] = useState("idle");
|
|
7467
7921
|
const subtotal = selectSubtotal(cart);
|
|
7468
7922
|
const discountTotal = selectDiscountTotal(cart);
|
|
7469
7923
|
const shipping = selectShipping(cart, config);
|
|
7470
7924
|
const total = selectTotal(cart, config);
|
|
7471
7925
|
const checkoutSlotContext = { subtotal, total };
|
|
7926
|
+
const checkoutSessionInput = React4.useMemo(() => ({
|
|
7927
|
+
items: cart.lines.map((line) => ({
|
|
7928
|
+
productId: line.productId,
|
|
7929
|
+
variantId: line.variantId ?? null,
|
|
7930
|
+
quantity: line.quantity,
|
|
7931
|
+
optionsLabel: line.optionsLabel ?? null
|
|
7932
|
+
})),
|
|
7933
|
+
customer: { name: form.name.trim(), email: form.email.trim() },
|
|
7934
|
+
shippingAddress: {
|
|
7935
|
+
postalCode: form.zip.trim(),
|
|
7936
|
+
street: form.street.trim(),
|
|
7937
|
+
number: form.number.trim() || void 0,
|
|
7938
|
+
complement: form.complement.trim() || void 0,
|
|
7939
|
+
district: form.district.trim() || void 0,
|
|
7940
|
+
city: form.city.trim(),
|
|
7941
|
+
state: form.state.trim().toUpperCase(),
|
|
7942
|
+
phone: form.phone.trim() || void 0,
|
|
7943
|
+
document: documentDigits(form.document) || void 0
|
|
7944
|
+
},
|
|
7945
|
+
discountCode: cart.discountCode ?? null,
|
|
7946
|
+
paymentMethod
|
|
7947
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7948
|
+
}), [cart.lines, cart.discountCode, form, paymentMethod]);
|
|
7949
|
+
const [acquirerMissing, setAcquirerMissing] = useState(false);
|
|
7950
|
+
const chargesOnline = paymentMethod === "pix" && !acquirerMissing && (!policy?.paymentProvider || CHARGEABLE_PROVIDERS.includes(policy.paymentProvider));
|
|
7472
7951
|
const money = (value) => formatMoney(value, config.currency, config.locale);
|
|
7473
7952
|
useEffect(() => {
|
|
7474
|
-
if (cart.lines.length === 0 && !
|
|
7475
|
-
}, [cart.lines.length, config.catalogPath,
|
|
7476
|
-
const set = (key) => (value) =>
|
|
7953
|
+
if (cart.lines.length === 0 && !paidOrderId && !leaving.current) navigateTo(config.catalogPath);
|
|
7954
|
+
}, [cart.lines.length, config.catalogPath, paidOrderId]);
|
|
7955
|
+
const set = (key) => (value) => {
|
|
7956
|
+
setError(null);
|
|
7957
|
+
setForm((current) => ({ ...current, [key]: value }));
|
|
7958
|
+
};
|
|
7477
7959
|
async function setZip(value) {
|
|
7478
7960
|
const masked = formatPostalCode(value);
|
|
7961
|
+
setError(null);
|
|
7479
7962
|
setForm((current) => ({ ...current, zip: masked }));
|
|
7480
7963
|
if (normalizePostalCode(masked).length !== 8) {
|
|
7481
7964
|
setZipLookup("idle");
|
|
7482
7965
|
return;
|
|
7483
7966
|
}
|
|
7484
7967
|
setZipLookup("loading");
|
|
7968
|
+
void delivery.resolve(masked, subtotal);
|
|
7485
7969
|
try {
|
|
7486
7970
|
const found = await lookupPostalCode(masked);
|
|
7487
7971
|
if (!found) {
|
|
@@ -7531,15 +8015,24 @@ function CheckoutPage() {
|
|
|
7531
8015
|
if (!customerId) {
|
|
7532
8016
|
setSavedAddresses([]);
|
|
7533
8017
|
setAddressMode("new");
|
|
8018
|
+
setAddressesLoading(false);
|
|
7534
8019
|
return;
|
|
7535
8020
|
}
|
|
7536
8021
|
let cancelled = false;
|
|
7537
8022
|
const provider = getShopProvider();
|
|
7538
|
-
|
|
8023
|
+
const listAddresses = provider.listCustomerAddresses?.bind(provider);
|
|
8024
|
+
if (!listAddresses) {
|
|
8025
|
+
setAddressesLoading(false);
|
|
8026
|
+
return;
|
|
8027
|
+
}
|
|
8028
|
+
setAddressesLoading(true);
|
|
8029
|
+
void listAddresses(customerId).then((addresses) => {
|
|
7539
8030
|
if (cancelled || addresses.length === 0) return;
|
|
7540
8031
|
setSavedAddresses(addresses);
|
|
7541
8032
|
applySavedAddress(addresses[0]);
|
|
7542
8033
|
}).catch(() => {
|
|
8034
|
+
}).finally(() => {
|
|
8035
|
+
if (!cancelled) setAddressesLoading(false);
|
|
7543
8036
|
});
|
|
7544
8037
|
return () => {
|
|
7545
8038
|
cancelled = true;
|
|
@@ -7572,7 +8065,11 @@ function CheckoutPage() {
|
|
|
7572
8065
|
number: address.number ?? "",
|
|
7573
8066
|
complement: address.complement ?? "",
|
|
7574
8067
|
district: address.district ?? "",
|
|
7575
|
-
state: address.state
|
|
8068
|
+
state: address.state,
|
|
8069
|
+
// Kept from the book when it has one, and NOT cleared when it does not:
|
|
8070
|
+
// a shopper who just typed their phone should not lose it by picking a
|
|
8071
|
+
// saved address that predates the field.
|
|
8072
|
+
phone: address.phone ?? current.phone
|
|
7576
8073
|
}));
|
|
7577
8074
|
}
|
|
7578
8075
|
function selectAddress(addressId) {
|
|
@@ -7584,44 +8081,69 @@ function CheckoutPage() {
|
|
|
7584
8081
|
setSelectedAddressId("new");
|
|
7585
8082
|
setForm((current) => ({ ...current, street: "", city: "", zip: "", number: "", complement: "", district: "", state: "" }));
|
|
7586
8083
|
}
|
|
7587
|
-
function
|
|
8084
|
+
function fail(message, testId) {
|
|
8085
|
+
setError(message);
|
|
8086
|
+
if (testId) {
|
|
8087
|
+
const field2 = document.querySelector(`[data-testid="${testId}"]`);
|
|
8088
|
+
field2?.scrollIntoView({ block: "center", behavior: prefersReducedMotion() ? "auto" : "smooth" });
|
|
8089
|
+
field2?.focus({ preventScroll: true });
|
|
8090
|
+
}
|
|
8091
|
+
return false;
|
|
8092
|
+
}
|
|
8093
|
+
function validateContact() {
|
|
7588
8094
|
setError(null);
|
|
8095
|
+
if (requiresAccount && !session.customerId) {
|
|
8096
|
+
setShowSignin(true);
|
|
8097
|
+
return fail("Esta loja pede uma conta para finalizar a compra. Entre ou crie a sua.");
|
|
8098
|
+
}
|
|
7589
8099
|
if (!form.email.trim() || !/.+@.+\..+/.test(form.email)) {
|
|
7590
|
-
|
|
7591
|
-
return false;
|
|
8100
|
+
return fail("Informe um e-mail v\xE1lido.", TID.checkoutEmail);
|
|
7592
8101
|
}
|
|
7593
|
-
if (!form.name.trim())
|
|
7594
|
-
|
|
7595
|
-
return
|
|
8102
|
+
if (!form.name.trim()) return fail("Informe seu nome completo.", TID.checkoutName);
|
|
8103
|
+
if (requires("phone") && !form.phone.trim()) {
|
|
8104
|
+
return fail("Informe um telefone de contato.", "checkout-phone");
|
|
8105
|
+
}
|
|
8106
|
+
if (requires("document") && !isValidDocument(form.document)) {
|
|
8107
|
+
return fail(
|
|
8108
|
+
form.document.trim() ? "CPF ou CNPJ inv\xE1lido \u2014 confira os n\xFAmeros." : "Informe o CPF ou CNPJ do comprador.",
|
|
8109
|
+
TID.checkoutDocument
|
|
8110
|
+
);
|
|
7596
8111
|
}
|
|
8112
|
+
return true;
|
|
8113
|
+
}
|
|
8114
|
+
function validateDelivery() {
|
|
8115
|
+
setError(null);
|
|
8116
|
+
if (!selectRequiresShipping(cart)) return true;
|
|
7597
8117
|
if (!form.street.trim() || !form.city.trim() || !form.zip.trim() || !form.district.trim()) {
|
|
7598
|
-
|
|
7599
|
-
return false;
|
|
8118
|
+
return fail("Preencha ou selecione o endere\xE7o de entrega.", TID.checkoutStreet);
|
|
7600
8119
|
}
|
|
7601
|
-
if (!form.number.trim())
|
|
7602
|
-
|
|
7603
|
-
return
|
|
8120
|
+
if (!form.number.trim()) return fail("Falta o n\xFAmero do endere\xE7o.", "checkout-number");
|
|
8121
|
+
if (requires("complement") && !form.complement.trim()) {
|
|
8122
|
+
return fail("Informe o complemento (apto, bloco).", "checkout-complement");
|
|
7604
8123
|
}
|
|
7605
|
-
if (form.state.trim()
|
|
7606
|
-
|
|
7607
|
-
return false;
|
|
8124
|
+
if (!/^[A-Za-z]{2}$/.test(form.state.trim())) {
|
|
8125
|
+
return fail("Informe a UF com duas letras (ex.: RJ).", "checkout-state");
|
|
7608
8126
|
}
|
|
7609
8127
|
if (delivery.status === "unserved" && delivery.postalCode === normalizePostalCode(form.zip)) {
|
|
7610
|
-
|
|
7611
|
-
|
|
8128
|
+
return fail(
|
|
8129
|
+
delivery.refusedZone ? "Esta loja n\xE3o entrega nesse endere\xE7o. Tente outro." : "Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.",
|
|
8130
|
+
TID.checkoutZip
|
|
8131
|
+
);
|
|
7612
8132
|
}
|
|
7613
8133
|
return true;
|
|
7614
8134
|
}
|
|
7615
|
-
|
|
8135
|
+
function goToStep(next) {
|
|
8136
|
+
setError(null);
|
|
8137
|
+
setStep(next);
|
|
8138
|
+
if (typeof window !== "undefined") window.scrollTo({ top: 0, behavior: "smooth" });
|
|
8139
|
+
}
|
|
8140
|
+
function advance() {
|
|
8141
|
+
if (step === 1 && validateContact()) goToStep(2);
|
|
8142
|
+
else if (step === 2 && validateDelivery()) goToStep(3);
|
|
8143
|
+
}
|
|
8144
|
+
async function confirmWithoutCharge() {
|
|
7616
8145
|
if (!validate()) return;
|
|
7617
8146
|
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
8147
|
try {
|
|
7626
8148
|
const { order, customerId } = await placeStorefrontOrder({
|
|
7627
8149
|
config,
|
|
@@ -7636,31 +8158,67 @@ function CheckoutPage() {
|
|
|
7636
8158
|
complement: form.complement.trim() || void 0,
|
|
7637
8159
|
district: form.district.trim() || void 0,
|
|
7638
8160
|
city: form.city.trim(),
|
|
7639
|
-
state: form.state.trim().toUpperCase()
|
|
8161
|
+
state: form.state.trim().toUpperCase(),
|
|
8162
|
+
phone: form.phone.trim() || void 0,
|
|
8163
|
+
// Viaja no endereço porque a assinatura do shop_place_order não pode
|
|
8164
|
+
// ganhar parâmetro sem DROP (0050); uma trigger o levanta para a
|
|
8165
|
+
// coluna e o tira do jsonb, então nunca vira linha de endereço.
|
|
8166
|
+
document: documentDigits(form.document) || void 0
|
|
7640
8167
|
},
|
|
7641
|
-
// The method the buyer actually chose, which opens the ledger row in
|
|
7642
|
-
// public.transactions with the right kind.
|
|
7643
8168
|
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
8169
|
});
|
|
7649
8170
|
if (customerId) session.setSession({ customerId, email: form.email, name: form.name });
|
|
7650
|
-
|
|
8171
|
+
leaving.current = true;
|
|
7651
8172
|
cart.clear();
|
|
7652
8173
|
navigateTo(`/order/${order.id}`);
|
|
7653
|
-
} catch (
|
|
7654
|
-
setError(
|
|
8174
|
+
} catch (err) {
|
|
8175
|
+
setError(placeOrderMessage(err));
|
|
7655
8176
|
setPlacing(false);
|
|
7656
|
-
} finally {
|
|
7657
|
-
stepTimers.forEach(clearTimeout);
|
|
7658
8177
|
}
|
|
7659
8178
|
}
|
|
7660
|
-
|
|
8179
|
+
function validate() {
|
|
8180
|
+
setError(null);
|
|
8181
|
+
if (requiresAccount && !session.customerId) {
|
|
8182
|
+
setShowSignin(true);
|
|
8183
|
+
return fail("Esta loja pede uma conta para finalizar a compra. Entre ou crie a sua.");
|
|
8184
|
+
}
|
|
8185
|
+
if (!form.email.trim() || !/.+@.+\..+/.test(form.email)) {
|
|
8186
|
+
return fail("Informe um e-mail v\xE1lido.", TID.checkoutEmail);
|
|
8187
|
+
}
|
|
8188
|
+
if (requires("phone") && !form.phone.trim()) {
|
|
8189
|
+
return fail("Informe um telefone de contato.", "checkout-phone");
|
|
8190
|
+
}
|
|
8191
|
+
if (!form.name.trim()) {
|
|
8192
|
+
return fail("Informe seu nome completo.", TID.checkoutName);
|
|
8193
|
+
}
|
|
8194
|
+
if (requires("document") && !isValidDocument(form.document)) {
|
|
8195
|
+
return fail(
|
|
8196
|
+
form.document.trim() ? "CPF ou CNPJ inv\xE1lido \u2014 confira os n\xFAmeros." : "Informe o CPF ou CNPJ do comprador.",
|
|
8197
|
+
TID.checkoutDocument
|
|
8198
|
+
);
|
|
8199
|
+
}
|
|
8200
|
+
if (!form.street.trim() || !form.city.trim() || !form.zip.trim() || !form.district.trim()) {
|
|
8201
|
+
return fail("Preencha ou selecione o endere\xE7o de entrega.", TID.checkoutStreet);
|
|
8202
|
+
}
|
|
8203
|
+
if (!form.number.trim()) {
|
|
8204
|
+
return fail("Falta o n\xFAmero do endere\xE7o.", "checkout-number");
|
|
8205
|
+
}
|
|
8206
|
+
if (requires("complement") && !form.complement.trim()) {
|
|
8207
|
+
return fail("Informe o complemento (apto, bloco).", "checkout-complement");
|
|
8208
|
+
}
|
|
8209
|
+
if (form.state.trim().length !== 2) {
|
|
8210
|
+
return fail("Informe a UF com duas letras (ex.: RJ).", "checkout-state");
|
|
8211
|
+
}
|
|
8212
|
+
if (selectRequiresShipping(cart) && delivery.status === "unserved" && delivery.postalCode === normalizePostalCode(form.zip)) {
|
|
8213
|
+
return fail(
|
|
8214
|
+
delivery.refusedZone ? "Esta loja n\xE3o entrega nesse endere\xE7o. Tente outro." : "Ainda n\xE3o entregamos nesse CEP. Tente outro endere\xE7o.",
|
|
8215
|
+
TID.checkoutZip
|
|
8216
|
+
);
|
|
8217
|
+
}
|
|
8218
|
+
return true;
|
|
8219
|
+
}
|
|
7661
8220
|
return /* @__PURE__ */ jsxs("main", { className: "min-h-screen bg-background", children: [
|
|
7662
8221
|
/* @__PURE__ */ jsx(Slot, { area: "checkout", name: "beforeForm", context: checkoutSlotContext }),
|
|
7663
|
-
placing && /* @__PURE__ */ jsx(ProcessingOverlay, { step: processingStep }),
|
|
7664
8222
|
showSignin && !session.email && /* @__PURE__ */ jsx(
|
|
7665
8223
|
SignInModal,
|
|
7666
8224
|
{
|
|
@@ -7682,132 +8240,281 @@ function CheckoutPage() {
|
|
|
7682
8240
|
] }) }),
|
|
7683
8241
|
/* @__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
8242
|
/* @__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
|
-
|
|
8243
|
+
/* @__PURE__ */ jsx(
|
|
8244
|
+
CheckoutSteps,
|
|
8245
|
+
{
|
|
8246
|
+
current: step,
|
|
8247
|
+
onGoBackTo: step === 4 ? void 0 : (target) => goToStep(target)
|
|
8248
|
+
}
|
|
8249
|
+
),
|
|
8250
|
+
/* @__PURE__ */ jsxs(
|
|
8251
|
+
"form",
|
|
8252
|
+
{
|
|
8253
|
+
className: "space-y-8",
|
|
8254
|
+
onSubmit: (event) => {
|
|
8255
|
+
event.preventDefault();
|
|
8256
|
+
advance();
|
|
8257
|
+
},
|
|
8258
|
+
children: [
|
|
8259
|
+
step === 1 && /* @__PURE__ */ jsxs("section", { children: [
|
|
8260
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-4", children: [
|
|
8261
|
+
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold tracking-tight", children: "Informa\xE7\xF5es de contato" }),
|
|
8262
|
+
session.email ? /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
8263
|
+
"Conectado como ",
|
|
8264
|
+
session.email
|
|
8265
|
+
] }) : /* @__PURE__ */ jsx(
|
|
8266
|
+
"button",
|
|
8267
|
+
{
|
|
8268
|
+
type: "button",
|
|
8269
|
+
onClick: () => setShowSignin((value) => !value),
|
|
8270
|
+
className: "text-sm font-medium text-primary hover:underline",
|
|
8271
|
+
children: "J\xE1 tem conta? Entrar"
|
|
8272
|
+
}
|
|
8273
|
+
)
|
|
8274
|
+
] }),
|
|
8275
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
8276
|
+
field("E-mail", TID.checkoutEmail, form.email, set("email"), {
|
|
8277
|
+
type: "email",
|
|
8278
|
+
placeholder: "E-mail"
|
|
8279
|
+
}),
|
|
8280
|
+
field("Nome completo", TID.checkoutName, form.name, set("name"), {
|
|
8281
|
+
placeholder: "Nome completo"
|
|
8282
|
+
}),
|
|
8283
|
+
shows("phone") && field("Telefone", "checkout-phone", form.phone, set("phone"), {
|
|
8284
|
+
type: "tel",
|
|
8285
|
+
autoComplete: "tel",
|
|
8286
|
+
placeholder: requires("phone") ? "Telefone" : "Telefone (opcional)"
|
|
8287
|
+
}),
|
|
8288
|
+
shows("document") && field(
|
|
8289
|
+
"CPF ou CNPJ",
|
|
8290
|
+
TID.checkoutDocument,
|
|
8291
|
+
form.document,
|
|
8292
|
+
(value) => {
|
|
8293
|
+
setError(null);
|
|
8294
|
+
setForm((current) => ({ ...current, document: formatDocument(value) }));
|
|
8295
|
+
},
|
|
8296
|
+
{
|
|
8297
|
+
// `inputMode` and not `type="number"`: the mask writes dots
|
|
8298
|
+
// and a dash, which a number input silently refuses to hold.
|
|
8299
|
+
inputMode: "numeric",
|
|
8300
|
+
autoComplete: "off",
|
|
8301
|
+
placeholder: requires("document") ? "CPF ou CNPJ" : "CPF ou CNPJ (opcional)"
|
|
8302
|
+
}
|
|
8303
|
+
)
|
|
8304
|
+
] }),
|
|
8305
|
+
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: [
|
|
8306
|
+
"Esta loja pede uma conta para finalizar a compra.",
|
|
8307
|
+
" ",
|
|
8308
|
+
/* @__PURE__ */ jsx(
|
|
8309
|
+
"button",
|
|
8310
|
+
{
|
|
8311
|
+
type: "button",
|
|
8312
|
+
onClick: () => setShowSignin(true),
|
|
8313
|
+
className: "font-semibold text-primary hover:underline",
|
|
8314
|
+
children: "Entrar ou criar conta"
|
|
8315
|
+
}
|
|
8316
|
+
)
|
|
8317
|
+
] })
|
|
8318
|
+
] }),
|
|
8319
|
+
step === 2 && /* @__PURE__ */ jsxs("section", { children: [
|
|
8320
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Endere\xE7o de entrega" }),
|
|
8321
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 grid gap-3", children: [
|
|
8322
|
+
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: [
|
|
8323
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-28" }),
|
|
8324
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3 w-3/4" }),
|
|
8325
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-1/2" })
|
|
8326
|
+
] }, row)) }),
|
|
8327
|
+
savedAddresses.map((address) => /* @__PURE__ */ jsx(
|
|
8328
|
+
"div",
|
|
8329
|
+
{
|
|
8330
|
+
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"}`,
|
|
8331
|
+
children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => selectAddress(address.id), className: "block w-full text-left", children: [
|
|
8332
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
|
|
8333
|
+
address.label,
|
|
8334
|
+
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" })
|
|
8335
|
+
] }),
|
|
8336
|
+
/* @__PURE__ */ jsxs("span", { className: "mt-1 block text-muted-foreground", children: [
|
|
8337
|
+
[address.street, address.number].filter(Boolean).join(", "),
|
|
8338
|
+
address.complement ? ` \u2014 ${address.complement}` : ""
|
|
8339
|
+
] }),
|
|
8340
|
+
/* @__PURE__ */ jsxs("span", { className: "block text-muted-foreground", children: [
|
|
8341
|
+
[address.district, address.city, address.state].filter(Boolean).join(" \xB7 "),
|
|
8342
|
+
" \u2014 ",
|
|
8343
|
+
address.postalCode
|
|
8344
|
+
] })
|
|
8345
|
+
] })
|
|
8346
|
+
},
|
|
8347
|
+
address.id
|
|
8348
|
+
)),
|
|
8349
|
+
/* @__PURE__ */ jsxs(
|
|
8350
|
+
"button",
|
|
8351
|
+
{
|
|
8352
|
+
type: "button",
|
|
8353
|
+
onClick: addNewAddress,
|
|
8354
|
+
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"}`,
|
|
8355
|
+
children: [
|
|
8356
|
+
/* @__PURE__ */ jsx("span", { className: "block font-semibold text-primary", children: savedAddresses.length > 0 ? "+ Adicionar novo endere\xE7o" : "Informe o endere\xE7o de entrega" }),
|
|
8357
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 block text-muted-foreground", children: "Preencher outro endere\xE7o para esta compra" })
|
|
8358
|
+
]
|
|
8359
|
+
}
|
|
8360
|
+
)
|
|
8361
|
+
] }),
|
|
8362
|
+
addressMode === "new" && /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
|
|
8363
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-1 sm:grid-cols-[200px_1fr] sm:items-center", children: [
|
|
8364
|
+
field("CEP", TID.checkoutZip, form.zip, setZip, {
|
|
8365
|
+
placeholder: "CEP",
|
|
8366
|
+
inputMode: "numeric",
|
|
8367
|
+
autoComplete: "postal-code"
|
|
8368
|
+
}),
|
|
8369
|
+
zipLookup === "loading" ? /* @__PURE__ */ jsxs(SkeletonGroup, { label: "Buscando o endere\xE7o deste CEP", className: "grid gap-1.5 px-1", children: [
|
|
8370
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-40" }),
|
|
8371
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-24" })
|
|
8372
|
+
] }) : /* @__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." })
|
|
8373
|
+
] }),
|
|
8374
|
+
field("Endere\xE7o", TID.checkoutStreet, form.street, set("street"), { placeholder: "Rua, avenida\u2026" }),
|
|
8375
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
8376
|
+
field("N\xFAmero", "checkout-number", form.number, set("number"), { placeholder: "N\xFAmero" }),
|
|
8377
|
+
shows("complement") && field("Complemento", "checkout-complement", form.complement, set("complement"), {
|
|
8378
|
+
placeholder: requires("complement") ? "Apto, bloco" : "Apto, bloco (opcional)"
|
|
8379
|
+
})
|
|
8380
|
+
] }),
|
|
8381
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
8382
|
+
field("Bairro", "checkout-district", form.district, set("district"), { placeholder: "Bairro" }),
|
|
8383
|
+
field("UF", "checkout-state", form.state, set("state"), { placeholder: "UF" })
|
|
8384
|
+
] }),
|
|
8385
|
+
field("Cidade", TID.checkoutCity, form.city, set("city"), { placeholder: "Cidade" })
|
|
8386
|
+
] })
|
|
8387
|
+
] }),
|
|
8388
|
+
step === 3 && placing && /* @__PURE__ */ jsxs("section", { children: [
|
|
8389
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Confirmando seu pedido" }),
|
|
8390
|
+
/* @__PURE__ */ jsxs(SkeletonGroup, { label: "Criando seu pedido", className: "rounded-xl border bg-card p-6 sm:p-8", children: [
|
|
8391
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 border-b pb-6 sm:grid-cols-[120px_minmax(0,1fr)]", children: [
|
|
8392
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mx-auto h-24 w-24 rounded-full" }),
|
|
8393
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
8394
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-56 max-w-full" }),
|
|
8395
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3.5 w-full max-w-sm" }),
|
|
8396
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-4 h-5 w-40" })
|
|
7739
8397
|
] })
|
|
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." })
|
|
8398
|
+
] }),
|
|
8399
|
+
/* @__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: [
|
|
8400
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-16" }),
|
|
8401
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-4 w-24" })
|
|
8402
|
+
] }, column)) }),
|
|
8403
|
+
/* @__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: [
|
|
8404
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-12 rounded-lg" }),
|
|
8405
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 flex-1" }),
|
|
8406
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-16" })
|
|
8407
|
+
] }, line.lineId ?? line.productId)) })
|
|
8408
|
+
] }),
|
|
8409
|
+
/* @__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
8410
|
] }),
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
8411
|
+
step === 3 && !placing && /* @__PURE__ */ jsxs("section", { children: [
|
|
8412
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-xl font-semibold tracking-tight", children: "Pagamento" }),
|
|
8413
|
+
/* @__PURE__ */ jsx("div", { className: "mb-3 grid gap-3", role: "radiogroup", "aria-label": "Forma de pagamento", children: paymentMethods.map((method) => {
|
|
8414
|
+
const copy = PAYMENT_LABELS[method];
|
|
8415
|
+
const selected = paymentMethod === method;
|
|
8416
|
+
return (
|
|
8417
|
+
// Cada forma de pagamento abre o que ELA pede, embaixo dela
|
|
8418
|
+
// mesma. Antes o painel do Pix ficava no fim da lista, longe
|
|
8419
|
+
// do cartão escolhido, e escolher parecia não fazer nada.
|
|
8420
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
8421
|
+
/* @__PURE__ */ jsxs(
|
|
8422
|
+
"button",
|
|
8423
|
+
{
|
|
8424
|
+
type: "button",
|
|
8425
|
+
role: "radio",
|
|
8426
|
+
"aria-checked": selected,
|
|
8427
|
+
"data-testid": `checkout-payment-${method}`,
|
|
8428
|
+
onClick: () => setPaymentMethod(method),
|
|
8429
|
+
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"}`,
|
|
8430
|
+
children: [
|
|
8431
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between gap-3 font-semibold", children: [
|
|
8432
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
8433
|
+
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }),
|
|
8434
|
+
copy.label
|
|
8435
|
+
] }),
|
|
8436
|
+
selected && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[11px] font-bold text-primary", children: "Selecionado" })
|
|
8437
|
+
] }),
|
|
8438
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 block text-muted-foreground", children: copy.hint })
|
|
8439
|
+
]
|
|
8440
|
+
}
|
|
8441
|
+
),
|
|
8442
|
+
selected && chargesOnline && /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsx(
|
|
8443
|
+
CheckoutPayment,
|
|
8444
|
+
{
|
|
8445
|
+
input: checkoutSessionInput,
|
|
8446
|
+
money,
|
|
8447
|
+
onBack: () => goToStep(2),
|
|
8448
|
+
onUnavailable: () => setAcquirerMissing(true),
|
|
8449
|
+
onPaid: (orderId) => {
|
|
8450
|
+
leaving.current = true;
|
|
8451
|
+
setPaidOrderId(orderId);
|
|
8452
|
+
goToStep(4);
|
|
8453
|
+
cart.clear();
|
|
8454
|
+
}
|
|
8455
|
+
}
|
|
8456
|
+
) })
|
|
8457
|
+
] }, method)
|
|
8458
|
+
);
|
|
8459
|
+
}) }),
|
|
8460
|
+
/* @__PURE__ */ jsx(Slot, { area: "checkout", name: "afterPaymentMethods", context: checkoutSlotContext }),
|
|
8461
|
+
!chargesOnline && /* @__PURE__ */ jsxs("div", { className: "mt-5", children: [
|
|
8462
|
+
/* @__PURE__ */ jsx(
|
|
8463
|
+
"button",
|
|
8464
|
+
{
|
|
8465
|
+
type: "button",
|
|
8466
|
+
"data-testid": TID.placeOrder,
|
|
8467
|
+
disabled: placing,
|
|
8468
|
+
onClick: () => {
|
|
8469
|
+
void confirmWithoutCharge();
|
|
8470
|
+
},
|
|
8471
|
+
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",
|
|
8472
|
+
children: `Fazer pedido - ${money(total)}`
|
|
8473
|
+
}
|
|
8474
|
+
),
|
|
8475
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-center text-xs text-muted-foreground", children: "O pedido fica aguardando pagamento at\xE9 a loja confirmar." })
|
|
8476
|
+
] })
|
|
7770
8477
|
] }),
|
|
7771
|
-
/* @__PURE__ */ jsxs("
|
|
7772
|
-
|
|
7773
|
-
|
|
8478
|
+
step === 4 && paidOrderId && /* @__PURE__ */ jsxs("section", { "data-testid": TID.checkoutDone, className: "rounded-xl border bg-card p-8 text-center", children: [
|
|
8479
|
+
/* @__PURE__ */ jsx(SuccessMark, {}),
|
|
8480
|
+
/* @__PURE__ */ jsx("h2", { className: "mt-4 text-2xl font-bold tracking-tight", children: "Pagamento confirmado!" }),
|
|
8481
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-muted-foreground", children: "Seu pedido foi criado e a loja j\xE1 foi avisada." }),
|
|
8482
|
+
/* @__PURE__ */ jsx(
|
|
8483
|
+
"button",
|
|
8484
|
+
{
|
|
8485
|
+
type: "button",
|
|
8486
|
+
onClick: () => navigateTo(`/order/${paidOrderId}`),
|
|
8487
|
+
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",
|
|
8488
|
+
children: "Ver meu pedido"
|
|
8489
|
+
}
|
|
8490
|
+
)
|
|
7774
8491
|
] }),
|
|
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
|
-
] }),
|
|
8492
|
+
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 }),
|
|
8493
|
+
(step === 1 || step === 2) && /* @__PURE__ */ jsxs("div", { className: "grid gap-3 border-t pt-5", children: [
|
|
8494
|
+
/* @__PURE__ */ jsx(
|
|
8495
|
+
"button",
|
|
8496
|
+
{
|
|
8497
|
+
type: "submit",
|
|
8498
|
+
"data-testid": TID.checkoutContinue,
|
|
8499
|
+
className: "min-h-12 rounded-lg bg-primary px-6 py-4 text-sm font-bold text-primary-foreground transition hover:opacity-90",
|
|
8500
|
+
children: step === 1 ? "Continuar para a entrega" : `Ir para o pagamento - ${money(total)}`
|
|
8501
|
+
}
|
|
8502
|
+
),
|
|
8503
|
+
step === 2 && /* @__PURE__ */ jsx(
|
|
8504
|
+
"button",
|
|
8505
|
+
{
|
|
8506
|
+
type: "button",
|
|
8507
|
+
"data-testid": TID.checkoutBack,
|
|
8508
|
+
onClick: () => goToStep(1),
|
|
8509
|
+
className: "min-h-11 rounded-lg border px-6 py-3 text-sm font-medium transition hover:bg-muted",
|
|
8510
|
+
children: "Voltar aos dados"
|
|
8511
|
+
}
|
|
8512
|
+
),
|
|
8513
|
+
/* @__PURE__ */ jsx(Link, { to: config.catalogPath, className: "text-center text-sm text-muted-foreground hover:text-foreground", children: "Voltar ao carrinho" })
|
|
8514
|
+
] })
|
|
8515
|
+
]
|
|
8516
|
+
}
|
|
8517
|
+
),
|
|
7811
8518
|
/* @__PURE__ */ jsxs("footer", { className: "mt-10 flex flex-wrap gap-4 border-t pt-5 text-xs text-muted-foreground", children: [
|
|
7812
8519
|
/* @__PURE__ */ jsx(Link, { to: "/privacy", className: "hover:text-foreground", children: "Privacidade" }),
|
|
7813
8520
|
/* @__PURE__ */ jsx(Link, { to: "/terms", className: "hover:text-foreground", children: "Termos" }),
|
|
@@ -7901,21 +8608,7 @@ function CheckoutPage() {
|
|
|
7901
8608
|
/* @__PURE__ */ jsx(ShieldCheck, { className: "h-4 w-4 text-primary" }),
|
|
7902
8609
|
"Compra protegida e dados criptografados."
|
|
7903
8610
|
] }),
|
|
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
|
-
] })
|
|
8611
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6 border-t pt-5", children: /* @__PURE__ */ jsx(Slot, { area: "checkout", name: "afterForm", context: checkoutSlotContext }) })
|
|
7919
8612
|
] })
|
|
7920
8613
|
] })
|
|
7921
8614
|
] });
|
|
@@ -7980,9 +8673,10 @@ function orderTrackingSteps(order) {
|
|
|
7980
8673
|
{
|
|
7981
8674
|
id: "payment",
|
|
7982
8675
|
label: paid ? "Pagamento aprovado" : "Aguardando pagamento",
|
|
7983
|
-
|
|
8676
|
+
// "Processando" descrevia um pagamento que ninguém iniciou.
|
|
8677
|
+
description: paid ? "Compra confirmada" : "Ainda n\xE3o recebemos",
|
|
7984
8678
|
state: paid ? "complete" : "current",
|
|
7985
|
-
icon: Check
|
|
8679
|
+
icon: paid ? Check : Clock
|
|
7986
8680
|
},
|
|
7987
8681
|
{
|
|
7988
8682
|
id: "preparing",
|
|
@@ -8016,7 +8710,9 @@ function OrderTrackingTimeline({ order, compact: compact2 = false }) {
|
|
|
8016
8710
|
children: steps.map((step) => {
|
|
8017
8711
|
const Icon2 = step.icon;
|
|
8018
8712
|
const cancelled = step.state === "cancelled";
|
|
8019
|
-
const
|
|
8713
|
+
const done = step.state === "complete";
|
|
8714
|
+
const current = step.state === "current";
|
|
8715
|
+
const active2 = done || current;
|
|
8020
8716
|
return /* @__PURE__ */ jsx(
|
|
8021
8717
|
"li",
|
|
8022
8718
|
{
|
|
@@ -8033,15 +8729,15 @@ function OrderTrackingTimeline({ order, compact: compact2 = false }) {
|
|
|
8033
8729
|
{
|
|
8034
8730
|
className: [
|
|
8035
8731
|
"grid h-7 w-7 shrink-0 place-items-center rounded-full border",
|
|
8036
|
-
cancelled ? "border-rose-300 bg-rose-500 text-white" :
|
|
8732
|
+
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
8733
|
].join(" "),
|
|
8038
8734
|
"aria-hidden": true,
|
|
8039
8735
|
children: /* @__PURE__ */ jsx(Icon2, { className: "h-3.5 w-3.5" })
|
|
8040
8736
|
}
|
|
8041
8737
|
),
|
|
8042
8738
|
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
8043
|
-
/* @__PURE__ */ jsx("span", { className: "block
|
|
8044
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block
|
|
8739
|
+
/* @__PURE__ */ jsx("span", { className: "block font-semibold leading-snug text-foreground", children: step.label }),
|
|
8740
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block leading-snug text-muted-foreground", children: step.description })
|
|
8045
8741
|
] })
|
|
8046
8742
|
] })
|
|
8047
8743
|
},
|
|
@@ -8051,6 +8747,32 @@ function OrderTrackingTimeline({ order, compact: compact2 = false }) {
|
|
|
8051
8747
|
}
|
|
8052
8748
|
);
|
|
8053
8749
|
}
|
|
8750
|
+
|
|
8751
|
+
// src/components/account/section.ts
|
|
8752
|
+
var ACCOUNT_SECTIONS = [
|
|
8753
|
+
"profile",
|
|
8754
|
+
"orders",
|
|
8755
|
+
"addresses",
|
|
8756
|
+
"payments"
|
|
8757
|
+
];
|
|
8758
|
+
var DEFAULT_ACCOUNT_SECTION = "orders";
|
|
8759
|
+
function isAccountSection(value) {
|
|
8760
|
+
return ACCOUNT_SECTIONS.includes(value);
|
|
8761
|
+
}
|
|
8762
|
+
function parseAccountSection(path) {
|
|
8763
|
+
const query = path.split("?")[1];
|
|
8764
|
+
if (!query) return DEFAULT_ACCOUNT_SECTION;
|
|
8765
|
+
for (const pair of query.split("&")) {
|
|
8766
|
+
const [key, raw = ""] = pair.split("=");
|
|
8767
|
+
if (key !== "section") continue;
|
|
8768
|
+
const value = decodeURIComponent(raw);
|
|
8769
|
+
return isAccountSection(value) ? value : DEFAULT_ACCOUNT_SECTION;
|
|
8770
|
+
}
|
|
8771
|
+
return DEFAULT_ACCOUNT_SECTION;
|
|
8772
|
+
}
|
|
8773
|
+
function accountSectionPath(section) {
|
|
8774
|
+
return `/account?section=${section}`;
|
|
8775
|
+
}
|
|
8054
8776
|
var menuItems = [
|
|
8055
8777
|
{ id: "profile", label: "Perfil", icon: User },
|
|
8056
8778
|
{ id: "orders", label: "Meus pedidos", icon: Package },
|
|
@@ -8084,11 +8806,16 @@ function CustomerAccountShell({ title, subtitle, active: active2, onSelect, chil
|
|
|
8084
8806
|
const Icon2 = item.icon;
|
|
8085
8807
|
const selected = item.id === active2;
|
|
8086
8808
|
return /* @__PURE__ */ jsxs(
|
|
8087
|
-
|
|
8809
|
+
Link,
|
|
8088
8810
|
{
|
|
8089
|
-
|
|
8811
|
+
to: accountSectionPath(item.id),
|
|
8812
|
+
"data-section": item.id,
|
|
8090
8813
|
"aria-current": selected ? "page" : void 0,
|
|
8091
|
-
onClick: () =>
|
|
8814
|
+
onClick: (event) => {
|
|
8815
|
+
if (!onSelect) return;
|
|
8816
|
+
event.preventDefault();
|
|
8817
|
+
onSelect(item.id);
|
|
8818
|
+
},
|
|
8092
8819
|
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
8820
|
children: [
|
|
8094
8821
|
/* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4" }),
|
|
@@ -8123,38 +8850,23 @@ function CustomerAccountShell({ title, subtitle, active: active2, onSelect, chil
|
|
|
8123
8850
|
] })
|
|
8124
8851
|
] });
|
|
8125
8852
|
}
|
|
8126
|
-
|
|
8127
|
-
function SuccessMark() {
|
|
8128
|
-
const reduced = prefersReducedMotion();
|
|
8853
|
+
function PendingMark() {
|
|
8129
8854
|
return /* @__PURE__ */ jsxs("div", { className: "relative mx-auto h-24 w-24", children: [
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
{
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8855
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-0 rounded-full bg-amber-100" }),
|
|
8856
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", className: "absolute inset-0 h-full w-full p-6", "aria-hidden": true, children: [
|
|
8857
|
+
/* @__PURE__ */ jsx("circle", { cx: "24", cy: "24", r: "17", fill: "none", stroke: "rgb(180 83 9)", strokeWidth: "4" }),
|
|
8858
|
+
/* @__PURE__ */ jsx(
|
|
8859
|
+
"path",
|
|
8860
|
+
{
|
|
8861
|
+
d: "M24 14 L24 25 L31 29",
|
|
8862
|
+
fill: "none",
|
|
8863
|
+
stroke: "rgb(180 83 9)",
|
|
8864
|
+
strokeWidth: "4",
|
|
8865
|
+
strokeLinecap: "round",
|
|
8866
|
+
strokeLinejoin: "round"
|
|
8140
8867
|
}
|
|
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
|
-
) })
|
|
8868
|
+
)
|
|
8869
|
+
] })
|
|
8158
8870
|
] });
|
|
8159
8871
|
}
|
|
8160
8872
|
function OrderConfirmationPage({ orderId }) {
|
|
@@ -8163,7 +8875,7 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
8163
8875
|
const [loading, setLoading] = useState(true);
|
|
8164
8876
|
const money = (v) => formatMoney(v, config.currency, config.locale);
|
|
8165
8877
|
useStorefrontHead({ title: order ? `Pedido #${order.orderNumber} \u2014 ${config.name}` : config.name });
|
|
8166
|
-
|
|
8878
|
+
const reload = React4.useCallback(() => {
|
|
8167
8879
|
let cancelled = false;
|
|
8168
8880
|
getShopProvider().getOrder(orderId).then((o) => {
|
|
8169
8881
|
if (!cancelled) setOrder(o);
|
|
@@ -8174,8 +8886,40 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
8174
8886
|
cancelled = true;
|
|
8175
8887
|
};
|
|
8176
8888
|
}, [orderId]);
|
|
8889
|
+
useEffect(() => reload(), [reload]);
|
|
8177
8890
|
if (loading) {
|
|
8178
|
-
return /* @__PURE__ */ jsx("
|
|
8891
|
+
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: [
|
|
8892
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 border-b pb-6 sm:grid-cols-[120px_minmax(0,1fr)]", children: [
|
|
8893
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mx-auto h-24 w-24 rounded-full" }),
|
|
8894
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
8895
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-64 max-w-full" }),
|
|
8896
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3.5 w-full max-w-md" }),
|
|
8897
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-4 h-6 w-48" })
|
|
8898
|
+
] })
|
|
8899
|
+
] }),
|
|
8900
|
+
/* @__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: [
|
|
8901
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-16" }),
|
|
8902
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-4 w-28" }),
|
|
8903
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-20" })
|
|
8904
|
+
] }, column)) }),
|
|
8905
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
|
|
8906
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-32" }),
|
|
8907
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 flex items-center gap-2", children: [0, 1, 2, 3].map((node) => /* @__PURE__ */ jsxs(React4.Fragment, { children: [
|
|
8908
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-7 shrink-0 rounded-full" }),
|
|
8909
|
+
node < 3 && /* @__PURE__ */ jsx(Skeleton, { className: "h-px flex-1" })
|
|
8910
|
+
] }, node)) })
|
|
8911
|
+
] }),
|
|
8912
|
+
/* @__PURE__ */ jsx("div", { className: "mt-8 space-y-3", children: [0, 1].map((row) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
8913
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-12 rounded-lg" }),
|
|
8914
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 flex-1" }),
|
|
8915
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-16" })
|
|
8916
|
+
] }, row)) }),
|
|
8917
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-2 border-t pt-4", children: [
|
|
8918
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-full max-w-xs" }),
|
|
8919
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-full max-w-[14rem]" }),
|
|
8920
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-40" })
|
|
8921
|
+
] })
|
|
8922
|
+
] }) });
|
|
8179
8923
|
}
|
|
8180
8924
|
if (!order) {
|
|
8181
8925
|
return /* @__PURE__ */ jsxs("main", { className: "mx-auto max-w-2xl px-4 py-20 text-center", children: [
|
|
@@ -8183,24 +8927,20 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
8183
8927
|
/* @__PURE__ */ jsx(Link, { to: "/", className: "mt-4 inline-block text-primary underline", children: "Voltar \xE0 loja" })
|
|
8184
8928
|
] });
|
|
8185
8929
|
}
|
|
8930
|
+
const paid = isPaid(order.financialStatus);
|
|
8186
8931
|
return /* @__PURE__ */ jsx(
|
|
8187
8932
|
CustomerAccountShell,
|
|
8188
8933
|
{
|
|
8189
8934
|
title: "Detalhe do pedido",
|
|
8190
|
-
subtitle: "Compra confirmada e vinculada \xE0 sua conta.",
|
|
8935
|
+
subtitle: paid ? "Compra confirmada e vinculada \xE0 sua conta." : "Pedido guardado, aguardando o pagamento.",
|
|
8191
8936
|
active: "orders",
|
|
8937
|
+
onSelect: (section) => navigateTo(`/account?section=${section}`),
|
|
8192
8938
|
children: /* @__PURE__ */ jsxs("article", { "data-testid": TID.orderDetail, className: "animate-fade-up rounded-xl border bg-card p-6 sm:p-8", children: [
|
|
8193
8939
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 border-b pb-6 sm:grid-cols-[120px_minmax(0,1fr)]", children: [
|
|
8194
|
-
/* @__PURE__ */ jsx(SuccessMark, {}),
|
|
8940
|
+
paid ? /* @__PURE__ */ jsx(SuccessMark, {}) : /* @__PURE__ */ jsx(PendingMark, {}),
|
|
8195
8941
|
/* @__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
|
-
] }),
|
|
8942
|
+
/* @__PURE__ */ jsx("h2", { className: "sf-heading text-3xl font-bold tracking-tight", children: paid ? "Pedido confirmado!" : "Falta o pagamento" }),
|
|
8943
|
+
/* @__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
8944
|
/* @__PURE__ */ jsxs("p", { className: "mt-4 text-lg", children: [
|
|
8205
8945
|
"Pedido ",
|
|
8206
8946
|
/* @__PURE__ */ jsxs("span", { "data-testid": TID.orderNumber, className: "font-semibold", children: [
|
|
@@ -8227,18 +8967,39 @@ function OrderConfirmationPage({ orderId }) {
|
|
|
8227
8967
|
] }),
|
|
8228
8968
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
8229
8969
|
/* @__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" }),
|
|
8970
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 font-semibold", children: order.fulfillmentStatus === "fulfilled" ? "Enviado" : paid ? "Preparando envio" : "Aguardando pagamento" }),
|
|
8231
8971
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: order.notes ?? "Endere\xE7o salvo no pedido" })
|
|
8232
8972
|
] }),
|
|
8233
8973
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
8234
8974
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "Pagamento" }),
|
|
8235
8975
|
/* @__PURE__ */ jsx("p", { className: "mt-1 font-semibold", children: financialStatusBadge(order.financialStatus).label }),
|
|
8236
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: "
|
|
8976
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: paid ? "Recebido pela loja" : "Ainda n\xE3o recebido" })
|
|
8237
8977
|
] })
|
|
8238
8978
|
] }),
|
|
8239
8979
|
/* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
|
|
8240
8980
|
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-semibold uppercase text-muted-foreground", children: "Acompanhamento" }),
|
|
8241
|
-
/* @__PURE__ */ jsx(OrderTrackingTimeline, { order })
|
|
8981
|
+
/* @__PURE__ */ jsx(OrderTrackingTimeline, { order }),
|
|
8982
|
+
(order.shipments ?? []).filter((s2) => s2.trackingCode).map((shipment) => /* @__PURE__ */ jsxs(
|
|
8983
|
+
"div",
|
|
8984
|
+
{
|
|
8985
|
+
"data-testid": TID.orderTracking + "-code",
|
|
8986
|
+
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",
|
|
8987
|
+
children: [
|
|
8988
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: shipment.carrier ? `Enviado por ${shipment.carrier}` : "Enviado" }),
|
|
8989
|
+
shipment.trackingUrl ? /* @__PURE__ */ jsx(
|
|
8990
|
+
"a",
|
|
8991
|
+
{
|
|
8992
|
+
href: shipment.trackingUrl,
|
|
8993
|
+
target: "_blank",
|
|
8994
|
+
rel: "noreferrer",
|
|
8995
|
+
className: "font-mono font-medium text-primary hover:underline",
|
|
8996
|
+
children: shipment.trackingCode
|
|
8997
|
+
}
|
|
8998
|
+
) : /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: shipment.trackingCode })
|
|
8999
|
+
]
|
|
9000
|
+
},
|
|
9001
|
+
shipment.id
|
|
9002
|
+
))
|
|
8242
9003
|
] }),
|
|
8243
9004
|
/* @__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
9005
|
item.imageUrl && /* @__PURE__ */ jsx(SmoothImage, { src: item.imageUrl, alt: item.name, className: "h-12 w-12 rounded-lg border object-cover" }),
|
|
@@ -8301,16 +9062,23 @@ function useMyOrders() {
|
|
|
8301
9062
|
const refresh = useCallback(() => setTick((t) => t + 1), []);
|
|
8302
9063
|
useEffect(() => {
|
|
8303
9064
|
let cancelled = false;
|
|
9065
|
+
const local = listLocalOrderIds();
|
|
8304
9066
|
if (!customerId && !email) {
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
9067
|
+
if (local.length === 0) {
|
|
9068
|
+
setOrders([]);
|
|
9069
|
+
setLoading(false);
|
|
9070
|
+
return;
|
|
9071
|
+
}
|
|
8308
9072
|
}
|
|
8309
9073
|
setLoading(true);
|
|
8310
9074
|
const query = customerId ? { customerId, limit: 50 } : { customerEmail: email, limit: 50 };
|
|
8311
9075
|
getShopProvider().listOrders(query).then((data) => {
|
|
8312
9076
|
if (!cancelled) setOrders(data);
|
|
8313
|
-
}).catch(() => {
|
|
9077
|
+
}).catch(async () => {
|
|
9078
|
+
const mine = await Promise.all(
|
|
9079
|
+
listLocalOrderIds().map((id) => getShopProvider().getOrder(id).catch(() => null))
|
|
9080
|
+
);
|
|
9081
|
+
if (!cancelled) setOrders(mine.filter((o) => !!o));
|
|
8314
9082
|
}).finally(() => {
|
|
8315
9083
|
if (!cancelled) setLoading(false);
|
|
8316
9084
|
});
|
|
@@ -8432,7 +9200,27 @@ function OrdersPanel() {
|
|
|
8432
9200
|
const config = useStorefrontConfig();
|
|
8433
9201
|
const { orders, loading } = useMyOrders();
|
|
8434
9202
|
const money = (v) => formatMoney(v, config.currency, config.locale);
|
|
8435
|
-
if (loading)
|
|
9203
|
+
if (loading) {
|
|
9204
|
+
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: [
|
|
9205
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
9206
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-32" }),
|
|
9207
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-24 rounded-full" })
|
|
9208
|
+
] }),
|
|
9209
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3 w-20" }),
|
|
9210
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 flex items-center gap-2", children: [0, 1, 2, 3].map((node) => /* @__PURE__ */ jsxs(React4.Fragment, { children: [
|
|
9211
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-6 shrink-0 rounded-full" }),
|
|
9212
|
+
node < 3 && /* @__PURE__ */ jsx(Skeleton, { className: "h-px flex-1" })
|
|
9213
|
+
] }, node)) }),
|
|
9214
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex flex-wrap gap-2", children: [
|
|
9215
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-32 rounded-lg" }),
|
|
9216
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-24 rounded-lg" })
|
|
9217
|
+
] }),
|
|
9218
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center justify-between border-t pt-3", children: [
|
|
9219
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24" }),
|
|
9220
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20" })
|
|
9221
|
+
] })
|
|
9222
|
+
] }, row)) });
|
|
9223
|
+
}
|
|
8436
9224
|
if (orders.length === 0) {
|
|
8437
9225
|
return /* @__PURE__ */ jsxs("div", { "data-testid": TID.purchasesEmpty, className: "rounded-2xl border bg-card py-16 text-center", children: [
|
|
8438
9226
|
/* @__PURE__ */ jsx(Package, { className: "mx-auto h-10 w-10 text-muted-foreground" }),
|
|
@@ -8479,12 +9267,14 @@ function ProfilePanel() {
|
|
|
8479
9267
|
const [name, setName] = useState(session.name ?? "");
|
|
8480
9268
|
const [saving, setSaving] = useState(false);
|
|
8481
9269
|
const [saved, setSaved] = useState(false);
|
|
9270
|
+
const [error, setError] = useState(null);
|
|
8482
9271
|
async function save(e) {
|
|
8483
9272
|
e.preventDefault();
|
|
8484
9273
|
const trimmed = name.trim();
|
|
8485
9274
|
if (!trimmed) return;
|
|
8486
9275
|
setSaving(true);
|
|
8487
9276
|
setSaved(false);
|
|
9277
|
+
setError(null);
|
|
8488
9278
|
try {
|
|
8489
9279
|
if (session.customerId) {
|
|
8490
9280
|
const parts = trimmed.split(/\s+/).filter(Boolean);
|
|
@@ -8495,6 +9285,8 @@ function ProfilePanel() {
|
|
|
8495
9285
|
}
|
|
8496
9286
|
useSessionStore.setState({ name: trimmed });
|
|
8497
9287
|
setSaved(true);
|
|
9288
|
+
} catch {
|
|
9289
|
+
setError("N\xE3o foi poss\xEDvel salvar seu nome agora. Tente novamente em instantes.");
|
|
8498
9290
|
} finally {
|
|
8499
9291
|
setSaving(false);
|
|
8500
9292
|
}
|
|
@@ -8539,7 +9331,8 @@ function ProfilePanel() {
|
|
|
8539
9331
|
}
|
|
8540
9332
|
),
|
|
8541
9333
|
saved && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-emerald-700", children: "Salvo" })
|
|
8542
|
-
] })
|
|
9334
|
+
] }),
|
|
9335
|
+
error && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-destructive", children: error })
|
|
8543
9336
|
] });
|
|
8544
9337
|
}
|
|
8545
9338
|
function AddressesPanel() {
|
|
@@ -8574,7 +9367,7 @@ var SECTION_SUBTITLE = {
|
|
|
8574
9367
|
function MyPurchasesPage() {
|
|
8575
9368
|
const config = useStorefrontConfig();
|
|
8576
9369
|
const session = useSessionStore();
|
|
8577
|
-
const
|
|
9370
|
+
const section = parseAccountSection(useRoutePath());
|
|
8578
9371
|
useStorefrontHead({ title: `Minha conta \u2014 ${config.name}` });
|
|
8579
9372
|
if (!session.email) {
|
|
8580
9373
|
return /* @__PURE__ */ jsx("main", { className: "mx-auto max-w-3xl px-4 py-12 sm:px-6", children: /* @__PURE__ */ jsx(AuthForm, {}) });
|
|
@@ -8585,7 +9378,6 @@ function MyPurchasesPage() {
|
|
|
8585
9378
|
title: "Minha conta",
|
|
8586
9379
|
subtitle: SECTION_SUBTITLE[section],
|
|
8587
9380
|
active: section,
|
|
8588
|
-
onSelect: setSection,
|
|
8589
9381
|
children: [
|
|
8590
9382
|
section === "profile" && /* @__PURE__ */ jsx(ProfilePanel, {}),
|
|
8591
9383
|
section === "orders" && /* @__PURE__ */ jsx(OrdersPanel, {}),
|
|
@@ -8595,6 +9387,27 @@ function MyPurchasesPage() {
|
|
|
8595
9387
|
}
|
|
8596
9388
|
);
|
|
8597
9389
|
}
|
|
9390
|
+
function initShippingItems() {
|
|
9391
|
+
setShopShippingItemsResolver(
|
|
9392
|
+
() => useCartStore.getState().lines.map((line) => ({
|
|
9393
|
+
quantity: line.quantity,
|
|
9394
|
+
weight: line.weight ?? null,
|
|
9395
|
+
weightUnit: line.weightUnit ?? null,
|
|
9396
|
+
height: line.heightCm ?? null,
|
|
9397
|
+
width: line.widthCm ?? null,
|
|
9398
|
+
length: line.lengthCm ?? null,
|
|
9399
|
+
packageId: line.packageId ?? null,
|
|
9400
|
+
// Undefined is a line added before this existed. Treated as physical:
|
|
9401
|
+
// freight wrongly charged is a complaint, freight wrongly skipped is a
|
|
9402
|
+
// parcel the merchant pays for.
|
|
9403
|
+
isPhysical: line.requiresShipping !== false,
|
|
9404
|
+
productId: line.productId,
|
|
9405
|
+
variantId: line.variantId,
|
|
9406
|
+
name: line.name,
|
|
9407
|
+
sku: line.sku
|
|
9408
|
+
}))
|
|
9409
|
+
);
|
|
9410
|
+
}
|
|
8598
9411
|
function NotFoundPage() {
|
|
8599
9412
|
const config = useStorefrontConfig();
|
|
8600
9413
|
useStorefrontHead({ title: `P\xE1gina n\xE3o encontrada \u2014 ${config.name}` });
|
|
@@ -8827,6 +9640,7 @@ function initStorefrontRuntime(config) {
|
|
|
8827
9640
|
}));
|
|
8828
9641
|
}
|
|
8829
9642
|
setShopProvider(resolveStorefrontProvider(config));
|
|
9643
|
+
initShippingItems();
|
|
8830
9644
|
const reconcileCart = () => {
|
|
8831
9645
|
const provider = getShopProvider();
|
|
8832
9646
|
void useCartStore.getState().reconcile(async (line) => {
|
|
@@ -8879,7 +9693,7 @@ function createStorefrontApp(config) {
|
|
|
8879
9693
|
initStorefrontRuntime(config);
|
|
8880
9694
|
const router = defaultRouterAdapter(resolved.routing ?? "hash");
|
|
8881
9695
|
function StorefrontApp() {
|
|
8882
|
-
return /* @__PURE__ */ jsx(
|
|
9696
|
+
return /* @__PURE__ */ jsx(StorefrontConfigHost, { config: resolved, children: /* @__PURE__ */ jsx(StorefrontShell, { router }) });
|
|
8883
9697
|
}
|
|
8884
9698
|
StorefrontApp.displayName = "StorefrontApp";
|
|
8885
9699
|
return StorefrontApp;
|
|
@@ -8902,7 +9716,7 @@ function createStorefront(options) {
|
|
|
8902
9716
|
// src/document/sections.ts
|
|
8903
9717
|
var STORE_DOCUMENT_KIND = "fayz.store";
|
|
8904
9718
|
var STORE_COMMERCE_MODES = ["checkout", "catalog", "enquiry"];
|
|
8905
|
-
var STORE_PAYMENT_MODES = ["mock", "pix-mercadopago"];
|
|
9719
|
+
var STORE_PAYMENT_MODES = ["mock", "pix-mercadopago", "gateway"];
|
|
8906
9720
|
var STORE_PAYMENT_METHOD_KINDS = [
|
|
8907
9721
|
"pix",
|
|
8908
9722
|
"credit_card",
|
|
@@ -10227,6 +11041,6 @@ ${formatErrors(report.errors)}`;
|
|
|
10227
11041
|
return config;
|
|
10228
11042
|
}
|
|
10229
11043
|
|
|
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-
|
|
11044
|
+
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 };
|
|
11045
|
+
//# sourceMappingURL=chunk-SX3J3QIT.js.map
|
|
11046
|
+
//# sourceMappingURL=chunk-SX3J3QIT.js.map
|