@ecomiq/storefront 1.1.4 → 1.1.5
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/{chunk-6TMU4OXP.js → chunk-3QGB55R6.js} +3 -3
- package/dist/{chunk-6TMU4OXP.js.map → chunk-3QGB55R6.js.map} +1 -1
- package/dist/{chunk-EADDSNJS.js → chunk-64GLOWMJ.js} +14 -4
- package/dist/chunk-64GLOWMJ.js.map +1 -0
- package/dist/chunk-Q4U5OEUT.js +21 -0
- package/dist/chunk-Q4U5OEUT.js.map +1 -0
- package/dist/{chunk-3URTJY6X.js → chunk-QRR7EPCH.js} +14 -12
- package/dist/chunk-QRR7EPCH.js.map +1 -0
- package/dist/{chunk-YSD62KQM.js → chunk-ZG2XUYGZ.js} +23 -21
- package/dist/chunk-ZG2XUYGZ.js.map +1 -0
- package/dist/chunks/client.css +1 -1
- package/dist/chunks/client.css.map +1 -1
- package/dist/chunks/client.d.ts +535 -25
- package/dist/chunks/client.js +517 -193
- package/dist/chunks/client.js.map +1 -1
- package/dist/chunks/config.js +3 -2
- package/dist/chunks/core-public.d.ts +2 -2
- package/dist/chunks/core-public.js +4 -10
- package/dist/chunks/core-public.js.map +1 -1
- package/dist/chunks/server-nextjs.d.ts +3 -3
- package/dist/chunks/server-nextjs.js +27 -13
- package/dist/chunks/server-nextjs.js.map +1 -1
- package/dist/chunks/server-tanstack.d.ts +3 -3
- package/dist/chunks/server-tanstack.js +6 -5
- package/dist/chunks/server-tanstack.js.map +1 -1
- package/dist/{collection-BHD4hhQa.d.ts → collection-C6bWDLiw.d.ts} +318 -1
- package/dist/{ecomiq-oauth-password-plugin-gw2dPyYb.d.ts → ecomiq-oauth-password-plugin-J5wDFqzL.d.ts} +2 -2
- package/dist/nextjs/api.js +2 -1
- package/dist/nextjs/api.js.map +1 -1
- package/dist/react/client.css +1 -1
- package/dist/tanstack/api.js +3 -2
- package/dist/tanstack/api.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-3URTJY6X.js.map +0 -1
- package/dist/chunk-EADDSNJS.js.map +0 -1
- package/dist/chunk-YSD62KQM.js.map +0 -1
package/dist/chunks/client.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { QueryClient, hydrate, QueryClientProvider,
|
|
3
|
+
import { QueryClient, hydrate, QueryClientProvider, useQueryClient, useQuery, useMutation, useInfiniteQuery } from '@tanstack/react-query';
|
|
4
4
|
import React2, { createContext, forwardRef, useRef, useState, useCallback, useMemo, useImperativeHandle, useEffect, useContext } from 'react';
|
|
5
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
import { toast } from 'sonner';
|
|
7
7
|
import { create } from 'zustand';
|
|
8
8
|
import { persist } from 'zustand/middleware';
|
|
@@ -18,6 +18,22 @@ function isCartNotActiveApiError(error) {
|
|
|
18
18
|
return keys.includes("Carts.NotActive");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// src/core/constants/currency.ts
|
|
22
|
+
var DEFAULT_COUNTRY_CODE = "PE";
|
|
23
|
+
var DEFAULT_CURRENCY_CODE = "PEN";
|
|
24
|
+
var STOREFRONT_HEADERS = {
|
|
25
|
+
storeId: "X-Store-Id",
|
|
26
|
+
storeDomain: "X-Store-Domain",
|
|
27
|
+
identityScope: "x-Identity-Scope",
|
|
28
|
+
tenantId: "x-Tenant-Id",
|
|
29
|
+
storeIdLower: "x-store-id"};
|
|
30
|
+
var countryCurrency = {
|
|
31
|
+
"PEN": "es-PE",
|
|
32
|
+
"USD": "en-US",
|
|
33
|
+
"COP": "es-CO",
|
|
34
|
+
"MXN": "es-MX"
|
|
35
|
+
};
|
|
36
|
+
|
|
21
37
|
// src/core/client/store-api-client.ts
|
|
22
38
|
var STORE_API_PATH = "/api/store";
|
|
23
39
|
var serverRequestOriginGetter = null;
|
|
@@ -65,11 +81,11 @@ var StoreApiClient = class {
|
|
|
65
81
|
headers.set("Accept-Encoding", "identity");
|
|
66
82
|
const explicitStoreId = options?.xStoreId?.trim();
|
|
67
83
|
const runtimeStoreId = explicitStoreId || (typeof window === "undefined" ? serverStoreIdGetter : clientStoreIdGetter);
|
|
68
|
-
if (runtimeStoreId && !headers.has(
|
|
69
|
-
headers.set(
|
|
84
|
+
if (runtimeStoreId && !headers.has(STOREFRONT_HEADERS.storeIdLower)) {
|
|
85
|
+
headers.set(STOREFRONT_HEADERS.storeId, runtimeStoreId);
|
|
70
86
|
}
|
|
71
87
|
if (options?.xStoreDomain) {
|
|
72
|
-
headers.set(
|
|
88
|
+
headers.set(STOREFRONT_HEADERS.storeDomain, options.xStoreDomain);
|
|
73
89
|
}
|
|
74
90
|
if (options?.xCartId) {
|
|
75
91
|
headers.set("X-Cart-Token", options.xCartId);
|
|
@@ -352,14 +368,15 @@ async function getStorefrontProducts(params, cacheKey) {
|
|
|
352
368
|
const path = `products?${searchParams.toString()}`;
|
|
353
369
|
const client = await getStoreApiClient();
|
|
354
370
|
const response = await client.get(path, {
|
|
355
|
-
cacheKey
|
|
371
|
+
cacheKey,
|
|
372
|
+
noStore: true
|
|
356
373
|
});
|
|
357
374
|
return StorefrontProductsResponseSchema.parse(response);
|
|
358
375
|
}
|
|
359
376
|
async function getStorefrontProductBySlug(slug) {
|
|
360
377
|
const path = `products/${encodeURIComponent(slug)}`;
|
|
361
378
|
const client = await getStoreApiClient();
|
|
362
|
-
const response = await client.get(path);
|
|
379
|
+
const response = await client.get(path, { noStore: true });
|
|
363
380
|
return StorefrontProductDetailSchema.parse(response);
|
|
364
381
|
}
|
|
365
382
|
var StorefrontCollectionSchema = z.object({
|
|
@@ -455,7 +472,7 @@ var CartAddressSchema = z.object({
|
|
|
455
472
|
name: z.string().min(1),
|
|
456
473
|
address1: z.string().min(1),
|
|
457
474
|
city: z.string().min(1),
|
|
458
|
-
countryCode: z.string().min(2),
|
|
475
|
+
countryCode: z.string().min(2).max(2),
|
|
459
476
|
company: z.string().nullable().optional(),
|
|
460
477
|
address2: z.string().nullable().optional(),
|
|
461
478
|
province: z.string().nullable().optional(),
|
|
@@ -500,7 +517,7 @@ z.object({
|
|
|
500
517
|
name: z.string().nullable().optional(),
|
|
501
518
|
address1: z.string(),
|
|
502
519
|
city: z.string(),
|
|
503
|
-
countryCode: z.string(),
|
|
520
|
+
countryCode: z.string().min(2).max(2),
|
|
504
521
|
company: z.string().nullable().optional(),
|
|
505
522
|
address2: z.string().nullable().optional(),
|
|
506
523
|
province: z.string().nullable().optional(),
|
|
@@ -914,7 +931,7 @@ function customerMeToProfilePatch(me) {
|
|
|
914
931
|
companyName: me.companyName,
|
|
915
932
|
dateOfBirth: me.dateOfBirth,
|
|
916
933
|
consent: me.consent,
|
|
917
|
-
countryIsoCode: me.countryIsoCode ||
|
|
934
|
+
countryIsoCode: me.countryIsoCode || DEFAULT_COUNTRY_CODE
|
|
918
935
|
};
|
|
919
936
|
}
|
|
920
937
|
function customerAuthOptions(accessToken) {
|
|
@@ -977,7 +994,7 @@ var CustomerOrderShippingAddressSchema = z.object({
|
|
|
977
994
|
address1: z.string(),
|
|
978
995
|
address2: z.string().nullable().optional(),
|
|
979
996
|
city: z.string().nullable().optional(),
|
|
980
|
-
countryCode: z.string(),
|
|
997
|
+
countryCode: z.string().min(2).max(2),
|
|
981
998
|
province: z.string().nullable().optional(),
|
|
982
999
|
postalCode: z.string().nullable().optional(),
|
|
983
1000
|
district: z.string().nullable().optional(),
|
|
@@ -1142,7 +1159,7 @@ var OrderDraftSchema = z.object({
|
|
|
1142
1159
|
customerId: z.coerce.string().nullable().optional(),
|
|
1143
1160
|
cartId: z.string().nullable().optional(),
|
|
1144
1161
|
draftNumber: z.string(),
|
|
1145
|
-
currencyCode: z.string().optional().default(
|
|
1162
|
+
currencyCode: z.string().optional().default(DEFAULT_CURRENCY_CODE),
|
|
1146
1163
|
source: z.string().nullable().optional(),
|
|
1147
1164
|
notes: z.string().nullable().optional(),
|
|
1148
1165
|
email: z.string().nullable().optional(),
|
|
@@ -1290,7 +1307,8 @@ function useStoreProducts() {
|
|
|
1290
1307
|
const useStorefrontProductsList = (params) => {
|
|
1291
1308
|
return useQuery({
|
|
1292
1309
|
queryKey: ["storefront", "products", params],
|
|
1293
|
-
queryFn: () => getStorefrontProducts(params)
|
|
1310
|
+
queryFn: () => getStorefrontProducts(params),
|
|
1311
|
+
staleTime: 0
|
|
1294
1312
|
});
|
|
1295
1313
|
};
|
|
1296
1314
|
const useStorefrontInfiniteProducts = (params) => {
|
|
@@ -1298,14 +1316,16 @@ function useStoreProducts() {
|
|
|
1298
1316
|
queryKey: ["storefront", "products", "infinite", params],
|
|
1299
1317
|
queryFn: ({ pageParam = 1 }) => getStorefrontProducts({ ...params, page: pageParam }),
|
|
1300
1318
|
getNextPageParam: (lastPage) => lastPage.pageIndex < lastPage.totalPages ? lastPage.pageIndex + 1 : void 0,
|
|
1301
|
-
initialPageParam: 1
|
|
1319
|
+
initialPageParam: 1,
|
|
1320
|
+
staleTime: 0
|
|
1302
1321
|
});
|
|
1303
1322
|
};
|
|
1304
1323
|
const useProductBySlug = (slug) => {
|
|
1305
1324
|
return useQuery({
|
|
1306
1325
|
queryKey: ["storefront", "product", "slug", slug],
|
|
1307
1326
|
queryFn: () => getStorefrontProductBySlug(slug),
|
|
1308
|
-
enabled: !!slug
|
|
1327
|
+
enabled: !!slug,
|
|
1328
|
+
staleTime: 0
|
|
1309
1329
|
});
|
|
1310
1330
|
};
|
|
1311
1331
|
const useRefreshStorefrontProducts = () => {
|
|
@@ -1757,8 +1777,17 @@ var StorefrontCmsCustomScriptsSchema = z.object({
|
|
|
1757
1777
|
head: z.string().nullable().optional(),
|
|
1758
1778
|
bodyEnd: z.string().nullable().optional()
|
|
1759
1779
|
});
|
|
1780
|
+
var StorefrontCmsChannelsSchema = z.object({
|
|
1781
|
+
storefront: z.object({
|
|
1782
|
+
enabled: z.boolean().optional()
|
|
1783
|
+
}).optional(),
|
|
1784
|
+
pos: z.object({
|
|
1785
|
+
enabled: z.boolean().optional()
|
|
1786
|
+
}).optional()
|
|
1787
|
+
});
|
|
1760
1788
|
var StorefrontCmsSettingsSchema = z.object({
|
|
1761
1789
|
themeKey: z.string().optional(),
|
|
1790
|
+
channels: StorefrontCmsChannelsSchema.optional(),
|
|
1762
1791
|
branding: StorefrontCmsBrandingSchema.optional(),
|
|
1763
1792
|
seo: StorefrontCmsSeoSchema.optional(),
|
|
1764
1793
|
contact: StorefrontCmsContactSchema.optional(),
|
|
@@ -1770,6 +1799,7 @@ var StorefrontCmsPageSchema = z.object({
|
|
|
1770
1799
|
id: z.string(),
|
|
1771
1800
|
name: z.string(),
|
|
1772
1801
|
slug: z.string(),
|
|
1802
|
+
channel: z.string().transform((value) => value.toLowerCase()).pipe(z.enum(["storefront", "pos"])).default("storefront"),
|
|
1773
1803
|
layout: z.string().optional(),
|
|
1774
1804
|
version: z.number().optional(),
|
|
1775
1805
|
settings: z.record(z.string(), z.unknown()).optional().nullable(),
|
|
@@ -1821,7 +1851,7 @@ function normalizeStorefrontSettingsPayload(v) {
|
|
|
1821
1851
|
const parsed = StorefrontSettingsLegacyFlatSchema.safeParse(raw);
|
|
1822
1852
|
if (!parsed.success) return body;
|
|
1823
1853
|
const l = parsed.data;
|
|
1824
|
-
const currency = l.currency?.trim() ? l.currency :
|
|
1854
|
+
const currency = l.currency?.trim() ? l.currency : DEFAULT_CURRENCY_CODE;
|
|
1825
1855
|
const language = l.language?.trim() ? l.language : "es";
|
|
1826
1856
|
const slug = l.slug?.trim() ? l.slug : l.id;
|
|
1827
1857
|
return {
|
|
@@ -1916,10 +1946,10 @@ function buildEcomiqOAuthIdentityHeaders(settings) {
|
|
|
1916
1946
|
const checkout = record.checkoutSettings;
|
|
1917
1947
|
const identityScope = checkout?.identityScope === "tenant" ? "tenant" : "store";
|
|
1918
1948
|
const headers = {
|
|
1919
|
-
|
|
1920
|
-
|
|
1949
|
+
[STOREFRONT_HEADERS.identityScope]: identityScope,
|
|
1950
|
+
[STOREFRONT_HEADERS.storeId]: record.id
|
|
1921
1951
|
};
|
|
1922
|
-
if (record.tenantId) headers[
|
|
1952
|
+
if (record.tenantId) headers[STOREFRONT_HEADERS.tenantId] = record.tenantId;
|
|
1923
1953
|
return headers;
|
|
1924
1954
|
}
|
|
1925
1955
|
|
|
@@ -1935,6 +1965,175 @@ async function signInWithEcomiqCredentials(authClient, settings, credentials) {
|
|
|
1935
1965
|
}
|
|
1936
1966
|
return response;
|
|
1937
1967
|
}
|
|
1968
|
+
|
|
1969
|
+
// src/react/pages/checkout-page/lib/cart-mappers.ts
|
|
1970
|
+
function mapApiItemsToCartItems(items) {
|
|
1971
|
+
if (!items?.length) return [];
|
|
1972
|
+
return items.map((raw) => {
|
|
1973
|
+
const item = raw;
|
|
1974
|
+
const price = Number(item.unitPrice);
|
|
1975
|
+
const quantity = Number(item.quantity);
|
|
1976
|
+
const variantId = String(item.variantId ?? "");
|
|
1977
|
+
if (!Number.isFinite(price) || !Number.isFinite(quantity) || !variantId) return null;
|
|
1978
|
+
return {
|
|
1979
|
+
id: item.id,
|
|
1980
|
+
title: item.title,
|
|
1981
|
+
image: String(item.thumbnail ?? ""),
|
|
1982
|
+
size: String(item.variantLabel ?? "\u2014"),
|
|
1983
|
+
price,
|
|
1984
|
+
quantity,
|
|
1985
|
+
variantId,
|
|
1986
|
+
variantSku: item.variantSku ?? void 0,
|
|
1987
|
+
variantLabel: item.variantLabel ?? void 0
|
|
1988
|
+
};
|
|
1989
|
+
}).filter(Boolean);
|
|
1990
|
+
}
|
|
1991
|
+
function shippingValuesFromCartAddress(cartData) {
|
|
1992
|
+
const addr = cartData.shippingAddress;
|
|
1993
|
+
if (!addr) return void 0;
|
|
1994
|
+
const geo = addr.geo;
|
|
1995
|
+
return {
|
|
1996
|
+
address: addr.address1,
|
|
1997
|
+
zipCode: addr.postalCode ?? "",
|
|
1998
|
+
countryCode: addr.countryCode ?? DEFAULT_COUNTRY_CODE,
|
|
1999
|
+
departmentCode: geo?.departmentCode ?? "",
|
|
2000
|
+
departmentName: "",
|
|
2001
|
+
provinceCode: geo?.provinceCode ?? "",
|
|
2002
|
+
provinceName: addr.province ?? "",
|
|
2003
|
+
districtCode: geo?.districtCode ?? "",
|
|
2004
|
+
districtName: addr.district ?? addr.city ?? ""
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
// src/react/pages/checkout-page/lib/cart-sync.ts
|
|
2009
|
+
var checkoutReadQueryKey = (cartId) => ["storefront", "cart", cartId, "checkout"];
|
|
2010
|
+
function cartItemsSignature(items) {
|
|
2011
|
+
return items.map((i) => `${i.variantId ?? i.id}:${i.quantity}`).sort().join("|");
|
|
2012
|
+
}
|
|
2013
|
+
function apiCartItemsFromResponse(cart) {
|
|
2014
|
+
return mapApiItemsToCartItems(cart?.items);
|
|
2015
|
+
}
|
|
2016
|
+
function groupCartQuantitiesByVariant(items) {
|
|
2017
|
+
const map = /* @__PURE__ */ new Map();
|
|
2018
|
+
for (const item of items) {
|
|
2019
|
+
const variantId = item.variantId ?? item.id;
|
|
2020
|
+
map.set(variantId, (map.get(variantId) ?? 0) + Math.max(0, item.quantity));
|
|
2021
|
+
}
|
|
2022
|
+
return map;
|
|
2023
|
+
}
|
|
2024
|
+
function groupApiLinesByVariant(items) {
|
|
2025
|
+
const map = /* @__PURE__ */ new Map();
|
|
2026
|
+
for (const item of items) {
|
|
2027
|
+
const variantId = item.variantId ?? item.id;
|
|
2028
|
+
const lines = map.get(variantId) ?? [];
|
|
2029
|
+
lines.push(item);
|
|
2030
|
+
map.set(variantId, lines);
|
|
2031
|
+
}
|
|
2032
|
+
return map;
|
|
2033
|
+
}
|
|
2034
|
+
function findCartReconciliationActions(local, api) {
|
|
2035
|
+
const actions = [];
|
|
2036
|
+
const localQty = groupCartQuantitiesByVariant(local);
|
|
2037
|
+
const apiLinesByVariant = groupApiLinesByVariant(api);
|
|
2038
|
+
const variantIds = /* @__PURE__ */ new Set([
|
|
2039
|
+
...localQty.keys(),
|
|
2040
|
+
...apiLinesByVariant.keys()
|
|
2041
|
+
]);
|
|
2042
|
+
for (const variantId of variantIds) {
|
|
2043
|
+
const targetQty = localQty.get(variantId) ?? 0;
|
|
2044
|
+
const apiLines = apiLinesByVariant.get(variantId) ?? [];
|
|
2045
|
+
const apiQty = apiLines.reduce((sum, line) => sum + line.quantity, 0);
|
|
2046
|
+
if (targetQty > apiQty) {
|
|
2047
|
+
actions.push({
|
|
2048
|
+
action: "add",
|
|
2049
|
+
variantId,
|
|
2050
|
+
quantity: targetQty - apiQty
|
|
2051
|
+
});
|
|
2052
|
+
continue;
|
|
2053
|
+
}
|
|
2054
|
+
if (targetQty >= apiQty) continue;
|
|
2055
|
+
let excess = apiQty - targetQty;
|
|
2056
|
+
for (const line of [...apiLines].reverse()) {
|
|
2057
|
+
if (excess <= 0) break;
|
|
2058
|
+
if (line.quantity <= excess) {
|
|
2059
|
+
actions.push({ action: "remove", itemId: line.id });
|
|
2060
|
+
excess -= line.quantity;
|
|
2061
|
+
} else {
|
|
2062
|
+
actions.push({
|
|
2063
|
+
action: "update",
|
|
2064
|
+
itemId: line.id,
|
|
2065
|
+
quantity: line.quantity - excess
|
|
2066
|
+
});
|
|
2067
|
+
excess = 0;
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
return actions;
|
|
2072
|
+
}
|
|
2073
|
+
function areCartSignaturesEqual(a, b) {
|
|
2074
|
+
return cartItemsSignature(a) === cartItemsSignature(b);
|
|
2075
|
+
}
|
|
2076
|
+
function cartSessionSignature(cartTokenId, items) {
|
|
2077
|
+
return `${cartTokenId ?? ""}|${cartItemsSignature(items)}`;
|
|
2078
|
+
}
|
|
2079
|
+
function readCartCheckoutStatus(cart) {
|
|
2080
|
+
if (!cart) return null;
|
|
2081
|
+
const status = cart.status ?? null;
|
|
2082
|
+
return typeof status === "string" && status.trim() ? status.trim() : null;
|
|
2083
|
+
}
|
|
2084
|
+
function isCheckoutCartAlreadyPurchased(cart, localCartItemCount) {
|
|
2085
|
+
if (localCartItemCount > 0) return false;
|
|
2086
|
+
const status = readCartCheckoutStatus(cart);
|
|
2087
|
+
if (!status) return false;
|
|
2088
|
+
return status.toLowerCase() === "inprogress";
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
// src/react/lib/reconcile-local-cart-to-api.ts
|
|
2092
|
+
async function reconcileLocalCartWithApi(cartId, localCart, options) {
|
|
2093
|
+
let cart = await getCart(cartId);
|
|
2094
|
+
if (localCart.length === 0) return cart;
|
|
2095
|
+
let apiItems = apiCartItemsFromResponse(cart);
|
|
2096
|
+
if (areCartSignaturesEqual(localCart, apiItems)) return cart;
|
|
2097
|
+
const actions = findCartReconciliationActions(localCart, apiItems);
|
|
2098
|
+
const reconciliationErrors = [];
|
|
2099
|
+
for (const action of actions) {
|
|
2100
|
+
if (action.action !== "remove" && action.action !== "update") continue;
|
|
2101
|
+
try {
|
|
2102
|
+
cart = action.action === "remove" ? await deleteCartItem(cartId, action.itemId) : await updateCartItem(cartId, action.itemId, {
|
|
2103
|
+
quantity: action.quantity
|
|
2104
|
+
});
|
|
2105
|
+
apiItems = apiCartItemsFromResponse(cart);
|
|
2106
|
+
} catch (error) {
|
|
2107
|
+
{
|
|
2108
|
+
reconciliationErrors.push(error);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
for (const action of actions) {
|
|
2113
|
+
if (action.action !== "add") continue;
|
|
2114
|
+
try {
|
|
2115
|
+
cart = await addCartItem(
|
|
2116
|
+
{ variantId: action.variantId, quantity: action.quantity },
|
|
2117
|
+
cartId
|
|
2118
|
+
);
|
|
2119
|
+
apiItems = apiCartItemsFromResponse(cart);
|
|
2120
|
+
} catch (error) {
|
|
2121
|
+
{
|
|
2122
|
+
reconciliationErrors.push(error);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
if (reconciliationErrors.length > 0) {
|
|
2127
|
+
const first = reconciliationErrors[0];
|
|
2128
|
+
throw first instanceof Error ? first : new Error("No se pudo sincronizar el carrito con el servidor.");
|
|
2129
|
+
}
|
|
2130
|
+
return cart;
|
|
2131
|
+
}
|
|
2132
|
+
function isCartNotFoundError(error) {
|
|
2133
|
+
return error.status === 404;
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// src/react/hooks/use-proceed-to-checkout.ts
|
|
1938
2137
|
function buildCheckoutPath(cartId, pathPrefix = "/checkout") {
|
|
1939
2138
|
const base = pathPrefix.replace(/\/$/, "");
|
|
1940
2139
|
return `${base}/${encodeURIComponent(cartId.trim())}`;
|
|
@@ -1945,10 +2144,11 @@ function useProceedToCheckout(options = {}) {
|
|
|
1945
2144
|
currencyCode: currencyCodeOption,
|
|
1946
2145
|
salesChannelId = "web",
|
|
1947
2146
|
guestEmail: guestEmailOption,
|
|
2147
|
+
syncLocalCart = true,
|
|
1948
2148
|
navigate: navigateOption,
|
|
1949
2149
|
onError
|
|
1950
2150
|
} = options;
|
|
1951
|
-
const storeCurrency = useStore((s) => s.data?.store?.currency ??
|
|
2151
|
+
const storeCurrency = useStore((s) => s.data?.store?.currency ?? DEFAULT_CURRENCY_CODE);
|
|
1952
2152
|
const currencyCode = currencyCodeOption ?? storeCurrency;
|
|
1953
2153
|
const { useCreateCart } = useStoreCart();
|
|
1954
2154
|
const createCartMutation = useCreateCart();
|
|
@@ -1959,39 +2159,68 @@ function useProceedToCheckout(options = {}) {
|
|
|
1959
2159
|
}))
|
|
1960
2160
|
);
|
|
1961
2161
|
const [isProceedingToCheckout, setIsProceedingToCheckout] = useState(false);
|
|
2162
|
+
const inFlightRef = useRef(false);
|
|
1962
2163
|
const navigate = navigateOption ?? (typeof window !== "undefined" ? (path) => window.location.assign(path) : () => {
|
|
1963
2164
|
});
|
|
1964
2165
|
const proceedToCheckout = useCallback(async () => {
|
|
1965
|
-
if (
|
|
1966
|
-
|
|
1967
|
-
}
|
|
2166
|
+
if (inFlightRef.current) return null;
|
|
2167
|
+
inFlightRef.current = true;
|
|
1968
2168
|
setIsProceedingToCheckout(true);
|
|
2169
|
+
const fail = (error) => {
|
|
2170
|
+
console.error("Error starting checkout", error);
|
|
2171
|
+
onError?.(error);
|
|
2172
|
+
return null;
|
|
2173
|
+
};
|
|
1969
2174
|
try {
|
|
1970
2175
|
let checkoutCartId = cartTokenId;
|
|
2176
|
+
if (checkoutCartId) {
|
|
2177
|
+
try {
|
|
2178
|
+
await getCart(checkoutCartId);
|
|
2179
|
+
} catch (error) {
|
|
2180
|
+
if (isCartNotFoundError(error)) {
|
|
2181
|
+
checkoutCartId = null;
|
|
2182
|
+
setCartTokenId(null);
|
|
2183
|
+
} else {
|
|
2184
|
+
return fail(error);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
1971
2188
|
if (!checkoutCartId) {
|
|
1972
2189
|
const guestEmail = guestEmailOption ?? useCart.getState().checkoutCustomerLocal?.customer.email ?? "cambiar@ecomiq.pe";
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2190
|
+
let created;
|
|
2191
|
+
try {
|
|
2192
|
+
created = await createCartMutation.mutateAsync({
|
|
2193
|
+
currencyCode,
|
|
2194
|
+
email: guestEmail,
|
|
2195
|
+
salesChannelId
|
|
2196
|
+
});
|
|
2197
|
+
} catch (error) {
|
|
2198
|
+
return fail(error);
|
|
2199
|
+
}
|
|
1978
2200
|
checkoutCartId = created.id;
|
|
1979
2201
|
if (!checkoutCartId) {
|
|
1980
|
-
|
|
2202
|
+
return fail(new Error("Cart created without id"));
|
|
1981
2203
|
}
|
|
1982
2204
|
setCartTokenId(checkoutCartId);
|
|
1983
2205
|
}
|
|
2206
|
+
if (syncLocalCart) {
|
|
2207
|
+
const localCart = useCart.getState().cart;
|
|
2208
|
+
if (localCart.length > 0) {
|
|
2209
|
+
try {
|
|
2210
|
+
await reconcileLocalCartWithApi(checkoutCartId, localCart, {
|
|
2211
|
+
strict: true
|
|
2212
|
+
});
|
|
2213
|
+
} catch (error) {
|
|
2214
|
+
return fail(error);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
1984
2218
|
navigate(buildCheckoutPath(checkoutCartId, checkoutPathPrefix));
|
|
1985
2219
|
return checkoutCartId;
|
|
1986
2220
|
} catch (error) {
|
|
1987
|
-
|
|
1988
|
-
if (onError) {
|
|
1989
|
-
onError(error);
|
|
1990
|
-
} else {
|
|
1991
|
-
console.error("Error starting checkout", error);
|
|
1992
|
-
}
|
|
1993
|
-
return null;
|
|
2221
|
+
return fail(error);
|
|
1994
2222
|
} finally {
|
|
2223
|
+
inFlightRef.current = false;
|
|
1995
2224
|
setIsProceedingToCheckout(false);
|
|
1996
2225
|
}
|
|
1997
2226
|
}, [
|
|
@@ -2000,11 +2229,11 @@ function useProceedToCheckout(options = {}) {
|
|
|
2000
2229
|
createCartMutation,
|
|
2001
2230
|
currencyCode,
|
|
2002
2231
|
guestEmailOption,
|
|
2003
|
-
isProceedingToCheckout,
|
|
2004
2232
|
navigate,
|
|
2005
2233
|
onError,
|
|
2006
2234
|
salesChannelId,
|
|
2007
|
-
setCartTokenId
|
|
2235
|
+
setCartTokenId,
|
|
2236
|
+
syncLocalCart
|
|
2008
2237
|
]);
|
|
2009
2238
|
return {
|
|
2010
2239
|
proceedToCheckout,
|
|
@@ -2013,14 +2242,6 @@ function useProceedToCheckout(options = {}) {
|
|
|
2013
2242
|
};
|
|
2014
2243
|
}
|
|
2015
2244
|
|
|
2016
|
-
// src/core/constants/currency.ts
|
|
2017
|
-
var countryCurrency = {
|
|
2018
|
-
"PEN": "es-PE",
|
|
2019
|
-
"USD": "en-US",
|
|
2020
|
-
"COP": "es-CO",
|
|
2021
|
-
"MXN": "es-MX"
|
|
2022
|
-
};
|
|
2023
|
-
|
|
2024
2245
|
// src/core/utils/format-currency.ts
|
|
2025
2246
|
function formatCurrency(amount, currency = "USD") {
|
|
2026
2247
|
const locale = countryCurrency[currency] ?? "en-US";
|
|
@@ -2060,12 +2281,12 @@ var zodPathToFieldId = {
|
|
|
2060
2281
|
selectedPaymentCode: "checkout-payment-method"
|
|
2061
2282
|
};
|
|
2062
2283
|
function buildCheckoutFormValidationSchema(currency) {
|
|
2063
|
-
const phoneDigits = currency ===
|
|
2284
|
+
const phoneDigits = currency === DEFAULT_CURRENCY_CODE ? 9 : 10;
|
|
2064
2285
|
return z.object({
|
|
2065
2286
|
email: z.string().trim().min(1, "El email es requerido").email("Ingresa un email v\xE1lido"),
|
|
2066
2287
|
firstName: z.string().trim().min(1, "El nombre es requerido").min(2, "El nombre debe tener al menos 2 caracteres"),
|
|
2067
2288
|
lastName: z.string().trim().min(1, "El apellido es requerido").min(2, "El apellido debe tener al menos 2 caracteres"),
|
|
2068
|
-
countryCode: z.string().trim().min(2, "Selecciona un pa\xEDs"),
|
|
2289
|
+
countryCode: z.string().trim().min(2, "Selecciona un pa\xEDs").max(2),
|
|
2069
2290
|
address: z.string().trim().min(1, "La direcci\xF3n es requerida").min(5, "La direcci\xF3n debe tener al menos 5 caracteres"),
|
|
2070
2291
|
phone: z.string().trim().min(1, "El tel\xE9fono es requerido").regex(/^\d+$/, "El tel\xE9fono solo debe contener n\xFAmeros").length(
|
|
2071
2292
|
phoneDigits,
|
|
@@ -2082,7 +2303,7 @@ function buildCheckoutFormValidationSchema(currency) {
|
|
|
2082
2303
|
paymentMethodsCount: z.number().int().nonnegative()
|
|
2083
2304
|
}).superRefine((data, ctx) => {
|
|
2084
2305
|
const country = data.countryCode.toUpperCase();
|
|
2085
|
-
if (country ===
|
|
2306
|
+
if (country === DEFAULT_COUNTRY_CODE) {
|
|
2086
2307
|
if (!data.departmentCode.trim()) {
|
|
2087
2308
|
ctx.addIssue({
|
|
2088
2309
|
code: z.ZodIssueCode.custom,
|
|
@@ -2199,7 +2420,7 @@ function focusCheckoutField(fieldId) {
|
|
|
2199
2420
|
}
|
|
2200
2421
|
|
|
2201
2422
|
// src/react/pages/checkout-page/hooks/use-checkout-form-validation.ts
|
|
2202
|
-
function useCheckoutFormValidation(currency =
|
|
2423
|
+
function useCheckoutFormValidation(currency = DEFAULT_CURRENCY_CODE) {
|
|
2203
2424
|
const schema = useMemo(
|
|
2204
2425
|
() => buildCheckoutFormValidationSchema(currency),
|
|
2205
2426
|
[currency]
|
|
@@ -2270,7 +2491,9 @@ function loadStorefrontGeoDepartments(headers) {
|
|
|
2270
2491
|
if (departmentsCache) return Promise.resolve(departmentsCache);
|
|
2271
2492
|
if (departmentsRequest) return departmentsRequest;
|
|
2272
2493
|
departmentsRequest = fetch(
|
|
2273
|
-
`/api/store/geo/departments?countryCode
|
|
2494
|
+
`/api/store/geo/departments?countryCode=${encodeURIComponent(
|
|
2495
|
+
DEFAULT_COUNTRY_CODE
|
|
2496
|
+
)}&source=inei&limit=200`,
|
|
2274
2497
|
{ cache: "no-store", headers }
|
|
2275
2498
|
).then((r) => r.json()).then((data) => {
|
|
2276
2499
|
const items = toGeoItems(data);
|
|
@@ -2304,10 +2527,10 @@ async function fetchStorefrontGeoDistricts(headers, countryCode, provinceCode) {
|
|
|
2304
2527
|
const data = await res.json();
|
|
2305
2528
|
return toGeoItems(data);
|
|
2306
2529
|
}
|
|
2307
|
-
async function resolveIneiCodesFromLabels(storeId, labels, countryCode =
|
|
2530
|
+
async function resolveIneiCodesFromLabels(storeId, labels, countryCode = DEFAULT_COUNTRY_CODE) {
|
|
2308
2531
|
const id = storeId.trim();
|
|
2309
2532
|
if (!id) return null;
|
|
2310
|
-
const headers = {
|
|
2533
|
+
const headers = { [STOREFRONT_HEADERS.storeId]: id };
|
|
2311
2534
|
const departments = await loadStorefrontGeoDepartments(headers);
|
|
2312
2535
|
const dept = departments.find((d) => d.name.trim() === labels.department.trim());
|
|
2313
2536
|
if (!dept) return null;
|
|
@@ -2345,12 +2568,12 @@ function useCheckoutDeliveryForm({
|
|
|
2345
2568
|
const patchContact = useCallback((patch) => {
|
|
2346
2569
|
onChangeRef.current(patch);
|
|
2347
2570
|
}, []);
|
|
2348
|
-
const countryCode = (contact.countryCode ||
|
|
2349
|
-
const isPeru = countryCode ===
|
|
2571
|
+
const countryCode = (contact.countryCode || DEFAULT_COUNTRY_CODE).toUpperCase();
|
|
2572
|
+
const isPeru = countryCode === DEFAULT_COUNTRY_CODE;
|
|
2350
2573
|
const storeRequestHeaders = useMemo(() => {
|
|
2351
2574
|
const id = storeId.trim();
|
|
2352
2575
|
if (!id) return null;
|
|
2353
|
-
return {
|
|
2576
|
+
return { [STOREFRONT_HEADERS.storeId]: id };
|
|
2354
2577
|
}, [storeId]);
|
|
2355
2578
|
useEffect(() => {
|
|
2356
2579
|
if (!isPeru || !storeRequestHeaders) {
|
|
@@ -2698,13 +2921,13 @@ function CheckoutDeliveryForm({
|
|
|
2698
2921
|
CheckoutSelectField,
|
|
2699
2922
|
{
|
|
2700
2923
|
id: "countryCode",
|
|
2701
|
-
label: "
|
|
2924
|
+
label: "Pa\xEDs/Regi\xF3n",
|
|
2702
2925
|
value: contact.countryCode,
|
|
2703
2926
|
error: getFieldError("countryCode"),
|
|
2704
2927
|
onClearError: () => clearFieldError("countryCode"),
|
|
2705
2928
|
onChange: (v) => onContactChange({ countryCode: v }),
|
|
2706
2929
|
children: [
|
|
2707
|
-
/* @__PURE__ */ jsx("option", { value:
|
|
2930
|
+
/* @__PURE__ */ jsx("option", { value: DEFAULT_COUNTRY_CODE, children: "Per\xFA" }),
|
|
2708
2931
|
/* @__PURE__ */ jsx("option", { value: "US", children: "United States" }),
|
|
2709
2932
|
/* @__PURE__ */ jsx("option", { value: "MX", children: "Mexico" }),
|
|
2710
2933
|
/* @__PURE__ */ jsx("option", { value: "CO", children: "Colombia" })
|
|
@@ -3147,7 +3370,7 @@ function StripePaymentProvider({ checkout }) {
|
|
|
3147
3370
|
return;
|
|
3148
3371
|
}
|
|
3149
3372
|
let cancelled = false;
|
|
3150
|
-
const
|
|
3373
|
+
const loadStripeScript = () => new Promise((resolve, reject) => {
|
|
3151
3374
|
const maybeStripe = globalThis.Stripe;
|
|
3152
3375
|
if (maybeStripe) return resolve();
|
|
3153
3376
|
const existing = document.querySelector('script[src="https://js.stripe.com/v3/"]');
|
|
@@ -3172,7 +3395,7 @@ function StripePaymentProvider({ checkout }) {
|
|
|
3172
3395
|
document.head.appendChild(script);
|
|
3173
3396
|
});
|
|
3174
3397
|
const mountStripePaymentElement = async () => {
|
|
3175
|
-
await
|
|
3398
|
+
await loadStripeScript();
|
|
3176
3399
|
if (cancelled) return;
|
|
3177
3400
|
const StripeCtor = globalThis.Stripe;
|
|
3178
3401
|
if (!StripeCtor) throw new Error("Stripe is not available");
|
|
@@ -3312,21 +3535,21 @@ function useCheckoutPayment({
|
|
|
3312
3535
|
const requiresProviderUi = Boolean(
|
|
3313
3536
|
selectedMethod && methodUsesProviderCheckoutUi(selectedMethod)
|
|
3314
3537
|
);
|
|
3315
|
-
const
|
|
3538
|
+
const activeCheckout = checkoutMethodCode && checkoutMethodCode === selectedMethod?.code ? checkoutResponse ?? null : null;
|
|
3316
3539
|
const activeLoadingCheckout = !!checkoutMethodCode && checkoutMethodCode === selectedMethod?.code && loadingCheckout;
|
|
3317
3540
|
const activeCheckoutError = checkoutMethodCode && checkoutMethodCode === selectedMethod?.code ? checkoutError : null;
|
|
3318
3541
|
const manualPaymentDetails = useMemo(() => {
|
|
3319
3542
|
if (!selectedMethod?.isManual) return null;
|
|
3320
|
-
const action = getPaymentFromCheckout(
|
|
3543
|
+
const action = getPaymentFromCheckout(activeCheckout)?.action ?? null;
|
|
3321
3544
|
const publicConfiguration = action?.publicConfiguration ?? {};
|
|
3322
3545
|
const phoneNumber = selectedMethod.phoneNumber ?? publicConfiguration.phoneNumber ?? null;
|
|
3323
3546
|
const accountHolder = selectedMethod.accountHolder ?? publicConfiguration.accountHolder ?? null;
|
|
3324
3547
|
const qrImageUrl = action?.qrCode ?? selectedMethod.qrImageUrl ?? publicConfiguration.qrImageUrl ?? null;
|
|
3325
3548
|
if (!phoneNumber && !accountHolder && !qrImageUrl) return null;
|
|
3326
3549
|
return { phoneNumber, accountHolder, qrImageUrl };
|
|
3327
|
-
}, [selectedMethod,
|
|
3550
|
+
}, [selectedMethod, activeCheckout]);
|
|
3328
3551
|
const needsReceipt = Boolean(selectedMethod?.isManual && manualPaymentDetails);
|
|
3329
|
-
const showProviderPanel = requiresProviderUi && !!selectedMethod && !!
|
|
3552
|
+
const showProviderPanel = requiresProviderUi && !!selectedMethod && !!activeCheckout && !activeLoadingCheckout && !activeCheckoutError;
|
|
3330
3553
|
const [receiptFile, setReceiptFile] = useState(null);
|
|
3331
3554
|
const [receiptUrl, setReceiptUrl] = useState(null);
|
|
3332
3555
|
const [receiptError, setReceiptError] = useState(null);
|
|
@@ -3464,7 +3687,7 @@ function useCheckoutPayment({
|
|
|
3464
3687
|
return {
|
|
3465
3688
|
activeMethods,
|
|
3466
3689
|
selectedMethod,
|
|
3467
|
-
|
|
3690
|
+
activeCheckout,
|
|
3468
3691
|
activeLoadingCheckout,
|
|
3469
3692
|
activeCheckoutError,
|
|
3470
3693
|
manualPaymentDetails,
|
|
@@ -3592,8 +3815,8 @@ function CheckoutPaymentSectionInner({
|
|
|
3592
3815
|
] })
|
|
3593
3816
|
] })
|
|
3594
3817
|
] }) }) : null,
|
|
3595
|
-
payment.showProviderPanel && payment.
|
|
3596
|
-
/* @__PURE__ */ jsx(PaymentProviderRouter, { checkout: payment.
|
|
3818
|
+
payment.showProviderPanel && payment.activeCheckout ? /* @__PURE__ */ jsxs("div", { className: "ecomiq-checkout-page__payment-provider", children: [
|
|
3819
|
+
/* @__PURE__ */ jsx(PaymentProviderRouter, { checkout: payment.activeCheckout }),
|
|
3597
3820
|
!payment.providerPaymentCompleted ? /* @__PURE__ */ jsx(
|
|
3598
3821
|
"button",
|
|
3599
3822
|
{
|
|
@@ -4006,128 +4229,6 @@ function CheckoutPageSkeleton({ header }) {
|
|
|
4006
4229
|
}
|
|
4007
4230
|
);
|
|
4008
4231
|
}
|
|
4009
|
-
|
|
4010
|
-
// src/react/pages/checkout-page/lib/cart-mappers.ts
|
|
4011
|
-
function mapApiItemsToCartItems(items) {
|
|
4012
|
-
if (!items?.length) return [];
|
|
4013
|
-
return items.map((raw) => {
|
|
4014
|
-
const item = raw;
|
|
4015
|
-
const price = Number(item.unitPrice);
|
|
4016
|
-
const quantity = Number(item.quantity);
|
|
4017
|
-
const variantId = String(item.variantId ?? "");
|
|
4018
|
-
if (!Number.isFinite(price) || !Number.isFinite(quantity) || !variantId) return null;
|
|
4019
|
-
return {
|
|
4020
|
-
id: item.id,
|
|
4021
|
-
title: item.title,
|
|
4022
|
-
image: String(item.thumbnail ?? ""),
|
|
4023
|
-
size: String(item.variantLabel ?? "\u2014"),
|
|
4024
|
-
price,
|
|
4025
|
-
quantity,
|
|
4026
|
-
variantId,
|
|
4027
|
-
variantSku: item.variantSku ?? void 0,
|
|
4028
|
-
variantLabel: item.variantLabel ?? void 0
|
|
4029
|
-
};
|
|
4030
|
-
}).filter(Boolean);
|
|
4031
|
-
}
|
|
4032
|
-
function shippingValuesFromCartAddress(cartData) {
|
|
4033
|
-
const addr = cartData.shippingAddress;
|
|
4034
|
-
if (!addr) return void 0;
|
|
4035
|
-
const geo = addr.geo;
|
|
4036
|
-
return {
|
|
4037
|
-
address: addr.address1,
|
|
4038
|
-
zipCode: addr.postalCode ?? "",
|
|
4039
|
-
countryCode: addr.countryCode ?? "PE",
|
|
4040
|
-
departmentCode: geo?.departmentCode ?? "",
|
|
4041
|
-
departmentName: "",
|
|
4042
|
-
provinceCode: geo?.provinceCode ?? "",
|
|
4043
|
-
provinceName: addr.province ?? "",
|
|
4044
|
-
districtCode: geo?.districtCode ?? "",
|
|
4045
|
-
districtName: addr.district ?? addr.city ?? ""
|
|
4046
|
-
};
|
|
4047
|
-
}
|
|
4048
|
-
|
|
4049
|
-
// src/react/pages/checkout-page/lib/cart-sync.ts
|
|
4050
|
-
var checkoutReadQueryKey = (cartId) => ["storefront", "cart", cartId, "checkout"];
|
|
4051
|
-
function cartItemsSignature(items) {
|
|
4052
|
-
return items.map((i) => `${i.variantId ?? i.id}:${i.quantity}`).sort().join("|");
|
|
4053
|
-
}
|
|
4054
|
-
function apiCartItemsFromResponse(cart) {
|
|
4055
|
-
return mapApiItemsToCartItems(cart?.items);
|
|
4056
|
-
}
|
|
4057
|
-
function groupCartQuantitiesByVariant(items) {
|
|
4058
|
-
const map = /* @__PURE__ */ new Map();
|
|
4059
|
-
for (const item of items) {
|
|
4060
|
-
const variantId = item.variantId ?? item.id;
|
|
4061
|
-
map.set(variantId, (map.get(variantId) ?? 0) + Math.max(0, item.quantity));
|
|
4062
|
-
}
|
|
4063
|
-
return map;
|
|
4064
|
-
}
|
|
4065
|
-
function groupApiLinesByVariant(items) {
|
|
4066
|
-
const map = /* @__PURE__ */ new Map();
|
|
4067
|
-
for (const item of items) {
|
|
4068
|
-
const variantId = item.variantId ?? item.id;
|
|
4069
|
-
const lines = map.get(variantId) ?? [];
|
|
4070
|
-
lines.push(item);
|
|
4071
|
-
map.set(variantId, lines);
|
|
4072
|
-
}
|
|
4073
|
-
return map;
|
|
4074
|
-
}
|
|
4075
|
-
function findCartReconciliationActions(local, api) {
|
|
4076
|
-
const actions = [];
|
|
4077
|
-
const localQty = groupCartQuantitiesByVariant(local);
|
|
4078
|
-
const apiLinesByVariant = groupApiLinesByVariant(api);
|
|
4079
|
-
const variantIds = /* @__PURE__ */ new Set([
|
|
4080
|
-
...localQty.keys(),
|
|
4081
|
-
...apiLinesByVariant.keys()
|
|
4082
|
-
]);
|
|
4083
|
-
for (const variantId of variantIds) {
|
|
4084
|
-
const targetQty = localQty.get(variantId) ?? 0;
|
|
4085
|
-
const apiLines = apiLinesByVariant.get(variantId) ?? [];
|
|
4086
|
-
const apiQty = apiLines.reduce((sum, line) => sum + line.quantity, 0);
|
|
4087
|
-
if (targetQty > apiQty) {
|
|
4088
|
-
actions.push({
|
|
4089
|
-
action: "add",
|
|
4090
|
-
variantId,
|
|
4091
|
-
quantity: targetQty - apiQty
|
|
4092
|
-
});
|
|
4093
|
-
continue;
|
|
4094
|
-
}
|
|
4095
|
-
if (targetQty >= apiQty) continue;
|
|
4096
|
-
let excess = apiQty - targetQty;
|
|
4097
|
-
for (const line of [...apiLines].reverse()) {
|
|
4098
|
-
if (excess <= 0) break;
|
|
4099
|
-
if (line.quantity <= excess) {
|
|
4100
|
-
actions.push({ action: "remove", itemId: line.id });
|
|
4101
|
-
excess -= line.quantity;
|
|
4102
|
-
} else {
|
|
4103
|
-
actions.push({
|
|
4104
|
-
action: "update",
|
|
4105
|
-
itemId: line.id,
|
|
4106
|
-
quantity: line.quantity - excess
|
|
4107
|
-
});
|
|
4108
|
-
excess = 0;
|
|
4109
|
-
}
|
|
4110
|
-
}
|
|
4111
|
-
}
|
|
4112
|
-
return actions;
|
|
4113
|
-
}
|
|
4114
|
-
function areCartSignaturesEqual(a, b) {
|
|
4115
|
-
return cartItemsSignature(a) === cartItemsSignature(b);
|
|
4116
|
-
}
|
|
4117
|
-
function cartSessionSignature(cartTokenId, items) {
|
|
4118
|
-
return `${cartTokenId ?? ""}|${cartItemsSignature(items)}`;
|
|
4119
|
-
}
|
|
4120
|
-
function readCartCheckoutStatus(cart) {
|
|
4121
|
-
if (!cart) return null;
|
|
4122
|
-
const status = cart.status ?? null;
|
|
4123
|
-
return typeof status === "string" && status.trim() ? status.trim() : null;
|
|
4124
|
-
}
|
|
4125
|
-
function isCheckoutCartAlreadyPurchased(cart, localCartItemCount) {
|
|
4126
|
-
if (localCartItemCount > 0) return false;
|
|
4127
|
-
const status = readCartCheckoutStatus(cart);
|
|
4128
|
-
if (!status) return false;
|
|
4129
|
-
return status.toLowerCase() === "inprogress";
|
|
4130
|
-
}
|
|
4131
4232
|
function useCartListener({
|
|
4132
4233
|
cartId,
|
|
4133
4234
|
enabled,
|
|
@@ -4175,7 +4276,7 @@ function useCartListener({
|
|
|
4175
4276
|
function useCheckoutSession(cartId) {
|
|
4176
4277
|
const queryClient = useQueryClient();
|
|
4177
4278
|
const storeId = useStore((s) => s.data?.store?.id ?? "");
|
|
4178
|
-
const currency = useStore((s) => s.data?.store?.currency ??
|
|
4279
|
+
const currency = useStore((s) => s.data?.store?.currency ?? DEFAULT_CURRENCY_CODE);
|
|
4179
4280
|
const { setCartTokenId, clearCartSession, cart: localCart } = useCart(
|
|
4180
4281
|
useShallow((s) => ({
|
|
4181
4282
|
setCartTokenId: s.setCartTokenId,
|
|
@@ -4245,7 +4346,7 @@ function useCheckoutSession(cartId) {
|
|
|
4245
4346
|
province: "",
|
|
4246
4347
|
postalCode: "",
|
|
4247
4348
|
phone: "",
|
|
4248
|
-
countryCode:
|
|
4349
|
+
countryCode: DEFAULT_COUNTRY_CODE,
|
|
4249
4350
|
departmentCode: "",
|
|
4250
4351
|
departmentName: "",
|
|
4251
4352
|
provinceCode: "",
|
|
@@ -4414,7 +4515,7 @@ function useCheckoutSession(cartId) {
|
|
|
4414
4515
|
}
|
|
4415
4516
|
});
|
|
4416
4517
|
mergeCartIntoCheckoutCache(buyerCart);
|
|
4417
|
-
const countryCode = (contact.countryCode ||
|
|
4518
|
+
const countryCode = (contact.countryCode || DEFAULT_COUNTRY_CODE).toUpperCase();
|
|
4418
4519
|
const districtName = contact.districtName || contact.city;
|
|
4419
4520
|
const provinceName = contact.provinceName || contact.province;
|
|
4420
4521
|
const baseAddress = {
|
|
@@ -4925,6 +5026,8 @@ function thankYouParamsFromCheckoutResult(result, overrides) {
|
|
|
4925
5026
|
transactionId: overrides?.transactionId ?? result.payment?.transactionId ?? void 0
|
|
4926
5027
|
};
|
|
4927
5028
|
}
|
|
5029
|
+
|
|
5030
|
+
// src/react/shell/components/product-variant-selector/product-variant-selection.ts
|
|
4928
5031
|
function buildOptionGroups(variants) {
|
|
4929
5032
|
const map = /* @__PURE__ */ new Map();
|
|
4930
5033
|
for (const v of variants) {
|
|
@@ -4964,6 +5067,54 @@ function getInitialOptions(variants, optionGroups) {
|
|
|
4964
5067
|
}
|
|
4965
5068
|
return selected;
|
|
4966
5069
|
}
|
|
5070
|
+
function buildVariantLabel(variant) {
|
|
5071
|
+
if (!variant?.options?.length) return void 0;
|
|
5072
|
+
return variant.options.map((o) => `${o.key}: ${o.value}`).join(" \u2022 ");
|
|
5073
|
+
}
|
|
5074
|
+
function getPriceTiersForVariant(product, variantId) {
|
|
5075
|
+
if (!variantId) return [];
|
|
5076
|
+
return (product.priceTiers ?? []).filter((tier) => tier.variantId === variantId).sort((a, b) => a.minQuantity - b.minQuantity);
|
|
5077
|
+
}
|
|
5078
|
+
function resolveProductImageUrl(product, selectedVariant) {
|
|
5079
|
+
return product.imageUrl ?? selectedVariant?.images?.[0]?.url ?? product.images?.[0]?.url ?? "";
|
|
5080
|
+
}
|
|
5081
|
+
function buildVariantSelectionPayload(product, selectedVariant, displayPrice) {
|
|
5082
|
+
if (!selectedVariant?.id) return null;
|
|
5083
|
+
return {
|
|
5084
|
+
productId: product.id,
|
|
5085
|
+
productName: product.name,
|
|
5086
|
+
variant: selectedVariant,
|
|
5087
|
+
displayPrice,
|
|
5088
|
+
compareAtPrice: selectedVariant.compareAtPrice ?? product.compareAtPrice,
|
|
5089
|
+
variantLabel: buildVariantLabel(selectedVariant),
|
|
5090
|
+
variantSku: selectedVariant.sku,
|
|
5091
|
+
imageUrl: resolveProductImageUrl(product, selectedVariant)
|
|
5092
|
+
};
|
|
5093
|
+
}
|
|
5094
|
+
function buildCartItemFromVariant(product, selectedVariant, displayPrice) {
|
|
5095
|
+
if (!selectedVariant?.id) return null;
|
|
5096
|
+
const sizeOption = selectedVariant.options?.find(
|
|
5097
|
+
(o) => o.key.toLowerCase() === "size"
|
|
5098
|
+
)?.value;
|
|
5099
|
+
const variantLabel = buildVariantLabel(selectedVariant);
|
|
5100
|
+
return {
|
|
5101
|
+
id: selectedVariant.id,
|
|
5102
|
+
title: product.name,
|
|
5103
|
+
price: displayPrice,
|
|
5104
|
+
quantity: 1,
|
|
5105
|
+
size: sizeOption ?? "\u2014",
|
|
5106
|
+
image: resolveProductImageUrl(product, selectedVariant),
|
|
5107
|
+
variantId: selectedVariant.id,
|
|
5108
|
+
variantSku: selectedVariant.sku,
|
|
5109
|
+
variantLabel
|
|
5110
|
+
};
|
|
5111
|
+
}
|
|
5112
|
+
function isVariantSelectionReady(selectedVariant, isAvailable) {
|
|
5113
|
+
return Boolean(selectedVariant?.id && isAvailable);
|
|
5114
|
+
}
|
|
5115
|
+
function canAddVariantToCart(selectedVariant, isAvailable) {
|
|
5116
|
+
return isVariantSelectionReady(selectedVariant, isAvailable);
|
|
5117
|
+
}
|
|
4967
5118
|
function ProductVariantSelector({
|
|
4968
5119
|
product,
|
|
4969
5120
|
currency = "USD",
|
|
@@ -5033,7 +5184,180 @@ function ProductVariantSelector({
|
|
|
5033
5184
|
})
|
|
5034
5185
|
] });
|
|
5035
5186
|
}
|
|
5187
|
+
function DefaultVariantPrice({
|
|
5188
|
+
displayPrice,
|
|
5189
|
+
compareAtPrice,
|
|
5190
|
+
currency
|
|
5191
|
+
}) {
|
|
5192
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 flex-wrap", children: [
|
|
5193
|
+
/* @__PURE__ */ jsx(
|
|
5194
|
+
"span",
|
|
5195
|
+
{
|
|
5196
|
+
className: "inline-flex items-center rounded-md bg-primary/10 px-2.5 py-0.5 text-base font-semibold text-primary",
|
|
5197
|
+
"data-testid": "product-display-price",
|
|
5198
|
+
children: formatCurrency(displayPrice, currency)
|
|
5199
|
+
}
|
|
5200
|
+
),
|
|
5201
|
+
compareAtPrice != null && compareAtPrice > displayPrice && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground line-through", children: formatCurrency(compareAtPrice, currency) })
|
|
5202
|
+
] });
|
|
5203
|
+
}
|
|
5204
|
+
function DefaultVariantOptions(slot) {
|
|
5205
|
+
if (!slot.hasVariants) return null;
|
|
5206
|
+
return /* @__PURE__ */ jsx(Fragment, { children: slot.optionGroups.map((group) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
5207
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: group.name }),
|
|
5208
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: group.values.map((value) => {
|
|
5209
|
+
const isSelected = slot.selectedOptions[group.key] === value;
|
|
5210
|
+
const isColorLike = group.key.toLowerCase().includes("color") || group.key.toLowerCase().includes("colour");
|
|
5211
|
+
return /* @__PURE__ */ jsx(
|
|
5212
|
+
"button",
|
|
5213
|
+
{
|
|
5214
|
+
type: "button",
|
|
5215
|
+
onClick: () => slot.selectOption(group.key, value),
|
|
5216
|
+
className: isColorLike ? `min-w-[72px] rounded-md border-2 px-3 py-2 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${isSelected ? "border-primary bg-primary text-foreground" : "border-border bg-muted/30 text-foreground hover:border-muted-foreground/50"}` : `rounded-md border px-3 py-2 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${isSelected ? "border-primary bg-muted ring-1 ring-primary" : "border-border bg-transparent text-muted-foreground hover:border-muted-foreground/50 hover:text-foreground"}`,
|
|
5217
|
+
style: isColorLike && isSelected && value.toLowerCase() === "black" ? { backgroundColor: "#1a1a1a", borderColor: "#1a1a1a", color: "#fff" } : isColorLike && value.toLowerCase() === "white" ? { backgroundColor: "#fafafa", color: "#1a1a1a", borderColor: "#e5e5e5" } : void 0,
|
|
5218
|
+
"data-testid": `variant-option-${group.key}-${value}`,
|
|
5219
|
+
children: value
|
|
5220
|
+
},
|
|
5221
|
+
value
|
|
5222
|
+
);
|
|
5223
|
+
}) })
|
|
5224
|
+
] }, group.key)) });
|
|
5225
|
+
}
|
|
5226
|
+
function DefaultPriceTiers({
|
|
5227
|
+
priceTiers,
|
|
5228
|
+
currency
|
|
5229
|
+
}) {
|
|
5230
|
+
if (priceTiers.length === 0) return null;
|
|
5231
|
+
return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-md border border-border text-sm", children: [
|
|
5232
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 bg-muted/40 px-3 py-2 text-xs font-semibold uppercase text-muted-foreground", children: [
|
|
5233
|
+
/* @__PURE__ */ jsx("span", { children: "Cantidad" }),
|
|
5234
|
+
/* @__PURE__ */ jsx("span", { className: "text-right", children: "Precio" })
|
|
5235
|
+
] }),
|
|
5236
|
+
priceTiers.map((priceTier) => /* @__PURE__ */ jsxs(
|
|
5237
|
+
"div",
|
|
5238
|
+
{
|
|
5239
|
+
className: "grid grid-cols-2 border-t border-border px-3 py-2",
|
|
5240
|
+
children: [
|
|
5241
|
+
/* @__PURE__ */ jsx("span", { children: priceTier.maxQuantity ? `${priceTier.minQuantity} - ${priceTier.maxQuantity}` : `${priceTier.minQuantity}+` }),
|
|
5242
|
+
/* @__PURE__ */ jsx("span", { className: "text-right font-medium", children: formatCurrency(priceTier.unitPrice, currency) })
|
|
5243
|
+
]
|
|
5244
|
+
},
|
|
5245
|
+
`${priceTier.variantId}-${priceTier.minQuantity}`
|
|
5246
|
+
))
|
|
5247
|
+
] });
|
|
5248
|
+
}
|
|
5249
|
+
function DefaultProductFeatures({ features }) {
|
|
5250
|
+
if (features.length === 0) return null;
|
|
5251
|
+
return /* @__PURE__ */ jsx("ul", { className: "list-disc list-inside space-y-1 text-sm text-muted-foreground", children: features.map((feature, index) => /* @__PURE__ */ jsx("li", { children: feature }, index)) });
|
|
5252
|
+
}
|
|
5253
|
+
function useProductVariantSelection({
|
|
5254
|
+
product,
|
|
5255
|
+
onVariantChange,
|
|
5256
|
+
onSelectionChange
|
|
5257
|
+
}) {
|
|
5258
|
+
const variants = useMemo(() => product.variants ?? [], [product.variants]);
|
|
5259
|
+
const optionGroups = useMemo(() => buildOptionGroups(variants), [variants]);
|
|
5260
|
+
const hasVariants = optionGroups.length > 0;
|
|
5261
|
+
const [selectedOptions, setSelectedOptions] = useState(
|
|
5262
|
+
() => getInitialOptions(variants, optionGroups)
|
|
5263
|
+
);
|
|
5264
|
+
const selectedVariant = useMemo(
|
|
5265
|
+
() => findVariant(variants, selectedOptions, hasVariants),
|
|
5266
|
+
[variants, selectedOptions, hasVariants]
|
|
5267
|
+
);
|
|
5268
|
+
const displayPrice = selectedVariant?.price ?? product.price ?? 0;
|
|
5269
|
+
const compareAtPrice = selectedVariant?.compareAtPrice ?? product.compareAtPrice;
|
|
5270
|
+
const isAvailable = (selectedVariant ? (selectedVariant.quantity ?? 0) > 0 : product.isAvailable) ?? true;
|
|
5271
|
+
const selection = useMemo(
|
|
5272
|
+
() => buildVariantSelectionPayload(product, selectedVariant, displayPrice),
|
|
5273
|
+
[product, selectedVariant, displayPrice]
|
|
5274
|
+
);
|
|
5275
|
+
const priceTiers = useMemo(
|
|
5276
|
+
() => getPriceTiersForVariant(product, selectedVariant?.id),
|
|
5277
|
+
[product, selectedVariant?.id]
|
|
5278
|
+
);
|
|
5279
|
+
const isReady = isVariantSelectionReady(selectedVariant, isAvailable);
|
|
5280
|
+
useEffect(() => {
|
|
5281
|
+
onVariantChange?.(selectedVariant, displayPrice);
|
|
5282
|
+
}, [selectedVariant, displayPrice, onVariantChange]);
|
|
5283
|
+
useEffect(() => {
|
|
5284
|
+
onSelectionChange?.(selection);
|
|
5285
|
+
}, [onSelectionChange, selection]);
|
|
5286
|
+
const selectOption = useCallback((key, value) => {
|
|
5287
|
+
setSelectedOptions((prev) => ({ ...prev, [key]: value }));
|
|
5288
|
+
}, []);
|
|
5289
|
+
return {
|
|
5290
|
+
product,
|
|
5291
|
+
variants,
|
|
5292
|
+
optionGroups,
|
|
5293
|
+
selectedOptions,
|
|
5294
|
+
selectOption,
|
|
5295
|
+
hasVariants,
|
|
5296
|
+
selectedVariant,
|
|
5297
|
+
displayPrice,
|
|
5298
|
+
compareAtPrice,
|
|
5299
|
+
isAvailable,
|
|
5300
|
+
isReady,
|
|
5301
|
+
selection,
|
|
5302
|
+
priceTiers
|
|
5303
|
+
};
|
|
5304
|
+
}
|
|
5305
|
+
function ProductVariantSelectorComplete({
|
|
5306
|
+
product,
|
|
5307
|
+
currency = "PEN",
|
|
5308
|
+
features = [],
|
|
5309
|
+
className,
|
|
5310
|
+
onVariantChange,
|
|
5311
|
+
onSelectionChange,
|
|
5312
|
+
renderPrice,
|
|
5313
|
+
renderVariants,
|
|
5314
|
+
renderPriceTiers,
|
|
5315
|
+
renderAction,
|
|
5316
|
+
renderFeatures
|
|
5317
|
+
}) {
|
|
5318
|
+
const state = useProductVariantSelection({
|
|
5319
|
+
product,
|
|
5320
|
+
onVariantChange,
|
|
5321
|
+
onSelectionChange
|
|
5322
|
+
});
|
|
5323
|
+
const priceSlot = {
|
|
5324
|
+
displayPrice: state.displayPrice,
|
|
5325
|
+
compareAtPrice: state.compareAtPrice,
|
|
5326
|
+
currency,
|
|
5327
|
+
selectedVariant: state.selectedVariant
|
|
5328
|
+
};
|
|
5329
|
+
const variantsSlot = {
|
|
5330
|
+
optionGroups: state.optionGroups,
|
|
5331
|
+
selectedOptions: state.selectedOptions,
|
|
5332
|
+
selectOption: state.selectOption,
|
|
5333
|
+
hasVariants: state.hasVariants,
|
|
5334
|
+
selectedVariant: state.selectedVariant,
|
|
5335
|
+
isAvailable: state.isAvailable
|
|
5336
|
+
};
|
|
5337
|
+
const priceTiersSlot = {
|
|
5338
|
+
priceTiers: state.priceTiers,
|
|
5339
|
+
currency,
|
|
5340
|
+
selectedVariant: state.selectedVariant
|
|
5341
|
+
};
|
|
5342
|
+
const actionSlot = {
|
|
5343
|
+
product: state.product,
|
|
5344
|
+
selectedVariant: state.selectedVariant,
|
|
5345
|
+
displayPrice: state.displayPrice,
|
|
5346
|
+
compareAtPrice: state.compareAtPrice,
|
|
5347
|
+
currency,
|
|
5348
|
+
isAvailable: state.isAvailable,
|
|
5349
|
+
isReady: state.isReady,
|
|
5350
|
+
selection: state.selection
|
|
5351
|
+
};
|
|
5352
|
+
return /* @__PURE__ */ jsxs("div", { className: className ?? "w-full max-w-[320px] space-y-5", children: [
|
|
5353
|
+
renderPrice?.(priceSlot) ?? /* @__PURE__ */ jsx(DefaultVariantPrice, { ...priceSlot }),
|
|
5354
|
+
renderVariants?.(variantsSlot) ?? /* @__PURE__ */ jsx(DefaultVariantOptions, { ...variantsSlot }),
|
|
5355
|
+
renderFeatures?.(features) ?? /* @__PURE__ */ jsx(DefaultProductFeatures, { features }),
|
|
5356
|
+
state.priceTiers.length > 0 && (renderPriceTiers?.(priceTiersSlot) ?? /* @__PURE__ */ jsx(DefaultPriceTiers, { ...priceTiersSlot })),
|
|
5357
|
+
renderAction?.(actionSlot)
|
|
5358
|
+
] });
|
|
5359
|
+
}
|
|
5036
5360
|
|
|
5037
|
-
export { CheckoutPage, CustomerAddressSchema, CustomerAddressUpsertSchema, CustomerMeSchema, CustomerOrderDetailSchema, CustomerOrderListItemSchema, CustomerProfilePatchSchema, CustomerRegisterRequestSchema, EcomiqProvider, ProductVariantSelector, QueryProvider, StoreApiClient, StorefrontAuthProvider, ThankYouPage, addCartCoupon, buildCheckoutPath, buildCheckoutThankYouUrl, checkoutCart, createCart, createCustomerAddress, createStoreApiClient, customerMeToProfilePatch, deleteCustomerAddress, ecomiqOAuthPasswordClient, emailFromCheckoutResult, getCart, getCartCheckout, getCartPaymentMethods, getCustomerAddresses, getCustomerMe, getCustomerOrder, getCustomerOrders, getSharedOrderDraft, getShippingOptions, getStoreApiBaseUrl, getStoreApiClient, getStorefrontCollectionBySlug, getStorefrontCollections, getStorefrontPageBySlug, getStorefrontPagePreview, getStorefrontPages, getStorefrontProductBySlug, getStorefrontProducts, isCartNotActiveApiError, orderIdFromCheckoutResult, patchCartAddresses, patchCartBuyer, patchCustomer, putCartShippingMethod, registerCustomer, removeCartCoupon, setClientStoreId, setServerRequestOrigin, setServerStoreId, signInWithEcomiqCredentials, storeApiClient, storefrontAccessTokenNeedsRefresh, storefrontSessionEndsSoon, thankYouParamsFromCheckoutResult, updateCustomerAddress, uploadPaymentReceipt, useCart, useCheckoutSession, useProceedToCheckout, useStore, useStoreCart, useStoreCollections, useStorePages, useStoreProducts, useStorefrontAuthClient, useStorefrontSession };
|
|
5361
|
+
export { CheckoutPage, CustomerAddressSchema, CustomerAddressUpsertSchema, CustomerMeSchema, CustomerOrderDetailSchema, CustomerOrderListItemSchema, CustomerProfilePatchSchema, CustomerRegisterRequestSchema, EcomiqProvider, ProductVariantSelector, ProductVariantSelectorComplete, QueryProvider, StoreApiClient, StorefrontAuthProvider, ThankYouPage, addCartCoupon, buildCartItemFromVariant, buildCheckoutPath, buildCheckoutThankYouUrl, buildOptionGroups, buildVariantLabel, buildVariantSelectionPayload, canAddVariantToCart, checkoutCart, createCart, createCustomerAddress, createStoreApiClient, customerMeToProfilePatch, deleteCustomerAddress, ecomiqOAuthPasswordClient, emailFromCheckoutResult, findVariant, getCart, getCartCheckout, getCartPaymentMethods, getCustomerAddresses, getCustomerMe, getCustomerOrder, getCustomerOrders, getInitialOptions, getPriceTiersForVariant, getSharedOrderDraft, getShippingOptions, getStoreApiBaseUrl, getStoreApiClient, getStorefrontCollectionBySlug, getStorefrontCollections, getStorefrontPageBySlug, getStorefrontPagePreview, getStorefrontPages, getStorefrontProductBySlug, getStorefrontProducts, isCartNotActiveApiError, isVariantSelectionReady, orderIdFromCheckoutResult, patchCartAddresses, patchCartBuyer, patchCustomer, putCartShippingMethod, registerCustomer, removeCartCoupon, setClientStoreId, setServerRequestOrigin, setServerStoreId, signInWithEcomiqCredentials, storeApiClient, storefrontAccessTokenNeedsRefresh, storefrontSessionEndsSoon, thankYouParamsFromCheckoutResult, updateCustomerAddress, uploadPaymentReceipt, useCart, useCheckoutSession, useProceedToCheckout, useProductVariantSelection, useStore, useStoreCart, useStoreCollections, useStorePages, useStoreProducts, useStorefrontAuthClient, useStorefrontSession };
|
|
5038
5362
|
//# sourceMappingURL=client.js.map
|
|
5039
5363
|
//# sourceMappingURL=client.js.map
|