@ecomiq/storefront 2.1.4 → 3.0.1
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/LICENSE +21 -0
- package/README.md +377 -76
- package/dist/account-Dx4e3syi.js +258 -0
- package/dist/account-forms-AVk71ko7.js +39 -0
- package/dist/account-forms.d.ts +13 -0
- package/dist/account.d.ts +240 -0
- package/dist/address-defaults-BqjRhS82.js +14 -0
- package/dist/address-defaults.d.ts +11 -0
- package/dist/analytics/analytics-CG1Qagwl.js +148 -0
- package/dist/analytics/analytics.d.ts +1 -0
- package/dist/analytics/config-DJr-GRhs.js +62 -0
- package/dist/analytics/config.d.ts +22 -0
- package/dist/analytics/consent-B13rBpBs.js +57 -0
- package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
- package/dist/analytics/consent-banner.d.ts +20 -0
- package/dist/analytics/cookies-pLozllyB.js +100 -0
- package/dist/analytics/cookies.d.ts +6 -0
- package/dist/analytics/destinations-iECrlLIr.js +144 -0
- package/dist/analytics/events-Dt81tIp1.js +102 -0
- package/dist/analytics/events.d.ts +51 -0
- package/dist/analytics/tracker-p4yaoOyk.js +51 -0
- package/dist/api-cache-DsuIn5hX.js +159 -0
- package/dist/bank-accounts-DlEczQQ5.js +81 -0
- package/dist/bank-accounts.d.ts +4 -0
- package/dist/captcha-DAqaIx8m.js +117 -0
- package/dist/captcha.d.ts +34 -0
- package/dist/cart-D7DxZ2lD.js +30 -0
- package/dist/cart-actions-Bsub_FFG.js +116 -0
- package/dist/cart-actions.d.ts +15 -0
- package/dist/cart-drawer-B7PPsR69.js +22 -0
- package/dist/cart-drawer.d.ts +9 -0
- package/dist/cart-form-y9P1r_EX.js +81 -0
- package/dist/cart-form.d.ts +24 -0
- package/dist/cart-summary-ur2VWKr-.js +89 -0
- package/dist/cart-summary.d.ts +31 -0
- package/dist/cart.d.ts +4 -0
- package/dist/catalog-7gJ-l5hW.js +67 -0
- package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
- package/dist/catalog-hooks.d.ts +111 -0
- package/dist/catalog.d.ts +5 -0
- package/dist/checkout-DVWSXANi.js +474 -0
- package/dist/checkout-form-CbOIdENC.js +131 -0
- package/dist/checkout-form.d.ts +20 -0
- package/dist/checkout.d.ts +86 -0
- package/dist/complaint-book-BvwLvfx0.js +117 -0
- package/dist/complaint-book.d.ts +62 -0
- package/dist/culqi-authentication-BGr20vAJ.js +204 -0
- package/dist/culqi-authentication.d.ts +15 -0
- package/dist/culqi-checkout-CTXpKgp_.js +55 -0
- package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
- package/dist/culqi-three-ds.d.ts +29 -0
- package/dist/customer-avatar-CJXSDroK.js +12 -0
- package/dist/customer-avatar.d.ts +5 -0
- package/dist/customer-cookies-DHOgFAMV.js +45 -0
- package/dist/customer-csrf-C1oDX-Py.js +17 -0
- package/dist/customer-csrf.d.ts +3 -0
- package/dist/customer-document-fields-C1K-_DAD.js +70 -0
- package/dist/customer-document-fields.d.ts +9 -0
- package/dist/customer-login-form-C7AC6vXd.js +144 -0
- package/dist/customer-login-form.d.ts +17 -0
- package/dist/customer-registration-form-BEm_NEfP.js +93 -0
- package/dist/customer-registration-form.d.ts +8 -0
- package/dist/document-url-CGsAl5Tf.js +9 -0
- package/dist/documents-T1mzc_j8.js +38 -0
- package/dist/documents.d.ts +16 -0
- package/dist/entries/account.d.ts +11 -0
- package/dist/entries/account.js +10 -0
- package/dist/entries/analytics.d.ts +6 -0
- package/dist/entries/analytics.js +5 -0
- package/dist/entries/cart.d.ts +8 -0
- package/dist/entries/cart.js +7 -0
- package/dist/entries/checkout.d.ts +12 -0
- package/dist/entries/checkout.js +11 -0
- package/dist/entries/order.d.ts +11 -0
- package/dist/entries/order.js +11 -0
- package/dist/favicon-YpyhVJ9H.js +28 -0
- package/dist/favicon.d.ts +4 -0
- package/dist/form-BtQcbXlJ.js +18 -0
- package/dist/form-contracts-BNpHaTGJ.js +166 -0
- package/dist/form-contracts.d.ts +42 -0
- package/dist/form.d.ts +3 -0
- package/dist/hidden-CCWtOWKR.js +3 -0
- package/dist/hidden.d.ts +4 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +50 -0
- package/dist/invoice-fields-BG0HjL6T.js +103 -0
- package/dist/invoice-fields.d.ts +39 -0
- package/dist/links-kFxBZfdb.js +96 -0
- package/dist/links.d.ts +21 -0
- package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
- package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
- package/dist/mercadopago-dialog.d.ts +21 -0
- package/dist/money-DtGOtDri.js +32 -0
- package/dist/money.d.ts +1 -0
- package/dist/optimistic-cart-BveVOHuh.js +53 -0
- package/dist/optimistic-cart.d.ts +16 -0
- package/dist/order-display-DTt5WBbc.js +112 -0
- package/dist/order-display.d.ts +20 -0
- package/dist/order-draft-DxV1Uhm3.js +38 -0
- package/dist/order-draft.d.ts +16 -0
- package/dist/order-pages-Dm6l5m0m.js +38 -0
- package/dist/order-pages.d.ts +12 -0
- package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
- package/dist/order-payment-continuation.d.ts +10 -0
- package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
- package/dist/order-payment-in-review.d.ts +5 -0
- package/dist/order-status-BJXLJwxq.js +162 -0
- package/dist/order-status.d.ts +89 -0
- package/dist/order-view-9IlBg5AF.js +62 -0
- package/dist/order-view.d.ts +12 -0
- package/dist/page-action-Ctkf4xC6.js +7 -0
- package/dist/page-seo-a5FJl8x5.js +262 -0
- package/dist/page-seo.d.ts +34 -0
- package/dist/pages-Ca_Gt6mq.js +150 -0
- package/dist/pages.d.ts +45 -0
- package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
- package/dist/payment-logo-BoPaGJc_.js +46 -0
- package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
- package/dist/payment-logo.d.ts +11 -0
- package/dist/payment-logos-BYvdwYyJ.js +23 -0
- package/dist/payment-methods-B8ocTgFZ.js +73 -0
- package/dist/payment-methods.d.ts +58 -0
- package/dist/payment-return-5cjypwIw.js +7 -0
- package/dist/place-CmW4hBWY.js +13 -0
- package/dist/place.d.ts +4 -0
- package/dist/preview-BiIBif3C.js +427 -0
- package/dist/preview-address-Cf0sJ-9p.js +21 -0
- package/dist/preview-cart-Dg2CVktS.js +376 -0
- package/dist/product-preview-BspMAecJ.js +51 -0
- package/dist/product-preview.d.ts +5 -0
- package/dist/product-variants-BNDHvs1y.js +46 -0
- package/dist/product-variants.d.ts +12 -0
- package/dist/public-pages-XtwlaLwt.js +194 -0
- package/dist/public-pages.d.ts +67 -0
- package/dist/registration-CpDcv8ql.js +91 -0
- package/dist/registration.d.ts +12 -0
- package/dist/request-security-CRsRMgwj.js +76 -0
- package/dist/rich-text-M-Ju8RL4.js +9 -0
- package/dist/rich-text.d.ts +5 -0
- package/dist/route-text-DcJw2SsK.js +9 -0
- package/dist/route-text.d.ts +4 -0
- package/dist/section-preview-CmsMl3yu.js +83 -0
- package/dist/section-preview.d.ts +12 -0
- package/dist/sections/account-addresses.d.ts +18 -0
- package/dist/sections/account-addresses.js +375 -0
- package/dist/sections/account-order.d.ts +12 -0
- package/dist/sections/account-order.js +592 -0
- package/dist/sections/account-orders.d.ts +8 -0
- package/dist/sections/account-orders.js +359 -0
- package/dist/sections/account-profile.d.ts +16 -0
- package/dist/sections/account-profile.js +123 -0
- package/dist/sections/checkout.d.ts +24 -0
- package/dist/sections/checkout.js +963 -0
- package/dist/sections/complaint-book.d.ts +8 -0
- package/dist/sections/complaint-book.js +538 -0
- package/dist/sections/design-system.d.ts +1 -0
- package/dist/sections/design-system.js +310 -0
- package/dist/sections/draft-invoice.d.ts +20 -0
- package/dist/sections/draft-invoice.js +158 -0
- package/dist/sections/order-tracking.d.ts +19 -0
- package/dist/sections/order-tracking.js +141 -0
- package/dist/sections-CNUMWTF_.js +77 -0
- package/dist/sections.d.ts +19 -0
- package/dist/seo-GpDRhAAA.js +78 -0
- package/dist/session-CKvmFJyg.js +379 -0
- package/dist/session.d.ts +24 -0
- package/dist/settings-B0NO2A6Y.js +78 -0
- package/dist/settings.d.ts +51 -0
- package/dist/source-marker-DhqSkW7n.js +4 -0
- package/dist/sources-B-3JFJfb.js +46 -0
- package/dist/sources.d.ts +7 -0
- package/dist/store-8Q-_LFrQ.js +152 -0
- package/dist/store-contact--hY6CoFb.js +19 -0
- package/dist/store-contact.d.ts +6 -0
- package/dist/store.d.ts +17 -0
- package/dist/storefront-language-DrMjVbmk.js +29 -0
- package/dist/storefront-language.d.ts +14 -0
- package/dist/storefront-text-D25ae1qf.js +11 -0
- package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
- package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
- package/dist/storefront-text.d.ts +4 -0
- package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
- package/dist/stripe-dialog.d.ts +30 -0
- package/dist/telemetry-HuZXWBPi.js +114 -0
- package/dist/telemetry.d.ts +10 -0
- package/dist/templates-CAqoNyTW.js +40 -0
- package/dist/templates.d.ts +15 -0
- package/dist/urls-CpGQPxvf.js +71 -0
- package/dist/urls.d.ts +18 -0
- package/dist/use-checkout-CZcxXJXi.js +42 -0
- package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
- package/dist/use-checkout-payment.d.ts +18 -0
- package/dist/use-checkout.d.ts +36 -0
- package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
- package/dist/use-order-payment-BKIz_IO0.js +45 -0
- package/dist/use-order-payment.d.ts +18 -0
- package/dist/use-payment-token-CErx-tN-.js +105 -0
- package/dist/vite-payment-logos-kziu7wKb.js +27 -0
- package/dist/vite-payment-logos.d.ts +24 -0
- package/dist/vite-public-assets-DW2fgtVb.js +209 -0
- package/dist/vite-public-assets.d.ts +29 -0
- package/dist/vite-sections-DTBr0-Kg.js +464 -0
- package/dist/vite-sections.d.ts +21 -0
- package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
- package/dist/vite-theme-ui.d.ts +10 -0
- package/dist/vite.d.ts +36 -0
- package/dist/vite.js +127 -0
- package/dist/voucher-payment-CVFDZ1L8.js +66 -0
- package/dist/worker-CNi-gzCu.js +86 -0
- package/dist/worker.d.ts +27 -0
- package/package.json +100 -76
- package/src/sections/account-addresses.tsx +215 -0
- package/src/sections/account-order.tsx +424 -0
- package/src/sections/account-orders.tsx +259 -0
- package/src/sections/account-profile.tsx +70 -0
- package/src/sections/checkout.tsx +586 -0
- package/src/sections/complaint-book.tsx +325 -0
- package/src/sections/design-system.tsx +146 -0
- package/src/sections/draft-invoice.tsx +105 -0
- package/src/sections/order-tracking.tsx +81 -0
- package/src/vite-client.d.ts +4 -0
- package/bin/ecomiq.mjs +0 -179
- package/public/design-bridge.js +0 -318
- package/src/api-proxy.ts +0 -194
- package/src/auth/create-storefront-auth.ts +0 -94
- package/src/auth/ecomiq-oauth-password-client.ts +0 -26
- package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
- package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
- package/src/auth/ecomiq-oauth-password-types.ts +0 -24
- package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
- package/src/auth/env.ts +0 -7
- package/src/auth/secure-cookies.ts +0 -23
- package/src/auth/storefront-auth-base-url.ts +0 -117
- package/src/auth/storefront-auth.ts +0 -7
- package/src/cart-session.ts +0 -239
- package/src/client.ts +0 -48
- package/src/codegen.mjs +0 -801
- package/src/components/account/customer-addresses-panel.tsx +0 -678
- package/src/components/account/customer-order-detail-panel.tsx +0 -285
- package/src/components/account/customer-orders-panel.tsx +0 -192
- package/src/components/account/customer-profile-panel.tsx +0 -379
- package/src/components/checkout/cart-sync.ts +0 -143
- package/src/components/checkout/checkout-contact-section.tsx +0 -76
- package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
- package/src/components/checkout/checkout-form-controls.tsx +0 -139
- package/src/components/checkout/checkout-line-items.tsx +0 -43
- package/src/components/checkout/checkout-order-summary.tsx +0 -165
- package/src/components/checkout/checkout-payment-section.tsx +0 -212
- package/src/components/checkout/checkout-session-view.tsx +0 -564
- package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
- package/src/components/checkout/checkout-shipping-section.tsx +0 -98
- package/src/components/checkout/checkout-states.tsx +0 -73
- package/src/components/checkout/checkout-step-card.tsx +0 -101
- package/src/components/checkout/checkout-steps.ts +0 -111
- package/src/components/checkout/form-schema.ts +0 -281
- package/src/components/checkout/format.ts +0 -15
- package/src/components/checkout/index.ts +0 -5
- package/src/components/checkout/map-customer-to-checkout.ts +0 -188
- package/src/components/checkout/types.ts +0 -41
- package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
- package/src/components/checkout/use-checkout-geo.ts +0 -50
- package/src/components/checkout/use-checkout-session.ts +0 -640
- package/src/components/hero.tsx +0 -291
- package/src/config.d.ts +0 -94
- package/src/config.mjs +0 -146
- package/src/format-money.ts +0 -31
- package/src/health.ts +0 -90
- package/src/hooks/index.ts +0 -9
- package/src/hooks/recently-viewed.ts +0 -125
- package/src/index.ts +0 -349
- package/src/link.tsx +0 -2
- package/src/pages/account/account-page-skeleton.tsx +0 -69
- package/src/pages/account/account-page-view.tsx +0 -270
- package/src/pages/account/account-shell.tsx +0 -101
- package/src/pages/account/parse-account-route.ts +0 -36
- package/src/pages/account.tsx +0 -16
- package/src/pages/api.$.ts +0 -20
- package/src/pages/api.auth.$.ts +0 -16
- package/src/pages/checkout.$checkoutId.tsx +0 -60
- package/src/pages/checkout.tsx +0 -31
- package/src/pages/pages.$pageSlug.tsx +0 -74
- package/src/pages/thank-you.tsx +0 -53
- package/src/providers/auth.tsx +0 -96
- package/src/providers/cart.tsx +0 -275
- package/src/providers/ecomiq.ts +0 -137
- package/src/providers/index.ts +0 -26
- package/src/proxy-api.ts +0 -20
- package/src/public-config.ts +0 -24
- package/src/review-submission.ts +0 -89
- package/src/runtime/context.ts +0 -2
- package/src/runtime/default-layout.tsx +0 -64
- package/src/runtime/head.ts +0 -149
- package/src/runtime/page.tsx +0 -143
- package/src/runtime/postcss-browser-stub.mjs +0 -5
- package/src/server-config-store.ts +0 -35
- package/src/server.ts +0 -286
- package/src/styles.css +0 -76
- package/src/ui/badge.tsx +0 -32
- package/src/ui/button.tsx +0 -31
- package/src/ui/card.tsx +0 -31
- package/src/ui/carousel.tsx +0 -218
- package/src/ui/cn.ts +0 -6
- package/src/ui/index.ts +0 -8
- package/src/ui/separator.tsx +0 -6
- package/src/ui/skeleton.tsx +0 -6
- package/src/ui/storefront-error.tsx +0 -49
- package/src/utils.ts +0 -36
- package/src/vite-inline.mjs +0 -263
- package/templates/__root.tsx +0 -83
- package/templates/router.tsx +0 -19
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useStorefrontText } from "./storefront-language-DrMjVbmk.js";
|
|
2
|
+
import { text } from "./form-BtQcbXlJ.js";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
// src/captcha.tsx
|
|
6
|
+
const CAPTCHA_FIELDS = {
|
|
7
|
+
turnstile: "cf-turnstile-response",
|
|
8
|
+
recaptcha: "g-recaptcha-response"
|
|
9
|
+
};
|
|
10
|
+
const captchaScripts = /* @__PURE__ */ new Map();
|
|
11
|
+
function captchaResponse(form, provider) {
|
|
12
|
+
if (provider === "off") return "";
|
|
13
|
+
return text(form, CAPTCHA_FIELDS[provider]);
|
|
14
|
+
}
|
|
15
|
+
function loadCaptcha(provider) {
|
|
16
|
+
const saved = captchaScripts.get(provider);
|
|
17
|
+
if (saved) return saved;
|
|
18
|
+
const loading = new Promise((resolve, reject) => {
|
|
19
|
+
const script = document.createElement("script");
|
|
20
|
+
script.src = provider === "turnstile" ? "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" : "https://www.google.com/recaptcha/api.js?render=explicit";
|
|
21
|
+
script.async = true;
|
|
22
|
+
script.defer = true;
|
|
23
|
+
script.onload = () => resolve();
|
|
24
|
+
script.onerror = () => {
|
|
25
|
+
captchaScripts.delete(provider);
|
|
26
|
+
script.remove();
|
|
27
|
+
reject(/* @__PURE__ */ new Error("Captcha unavailable"));
|
|
28
|
+
};
|
|
29
|
+
document.head.appendChild(script);
|
|
30
|
+
});
|
|
31
|
+
captchaScripts.set(provider, loading);
|
|
32
|
+
return loading;
|
|
33
|
+
}
|
|
34
|
+
function Captcha({ captcha, action, errorClass, busy }) {
|
|
35
|
+
const storefrontTranslate = useStorefrontText();
|
|
36
|
+
const element = useRef(null);
|
|
37
|
+
const widget = useRef(null);
|
|
38
|
+
const submitted = useRef(false);
|
|
39
|
+
const [token, setToken] = useState("");
|
|
40
|
+
const [failed, setFailed] = useState(false);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (captcha.provider === "off" || !captcha.siteKey || !captcha.isConfigured) return;
|
|
43
|
+
const provider = captcha.provider;
|
|
44
|
+
const siteKey = captcha.siteKey;
|
|
45
|
+
let active = true;
|
|
46
|
+
loadCaptcha(provider).then(async () => {
|
|
47
|
+
if (!active || !element.current) return;
|
|
48
|
+
const api = provider === "turnstile" ? window.turnstile : window.grecaptcha;
|
|
49
|
+
if (!api) throw new Error("Captcha unavailable");
|
|
50
|
+
if (api.ready) await new Promise((resolve) => api.ready(resolve));
|
|
51
|
+
if (!active || !element.current) return;
|
|
52
|
+
const id = api.render(element.current, {
|
|
53
|
+
sitekey: siteKey,
|
|
54
|
+
action: provider === "turnstile" ? action : void 0,
|
|
55
|
+
size: element.current.clientWidth < 304 ? "compact" : "normal",
|
|
56
|
+
callback: (value) => {
|
|
57
|
+
setToken(value);
|
|
58
|
+
setFailed(false);
|
|
59
|
+
},
|
|
60
|
+
"expired-callback": () => setToken(""),
|
|
61
|
+
"error-callback": () => {
|
|
62
|
+
setToken("");
|
|
63
|
+
setFailed(true);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
widget.current = {
|
|
67
|
+
api,
|
|
68
|
+
id
|
|
69
|
+
};
|
|
70
|
+
}).catch(() => {
|
|
71
|
+
if (active) setFailed(true);
|
|
72
|
+
});
|
|
73
|
+
return () => {
|
|
74
|
+
active = false;
|
|
75
|
+
const rendered = widget.current;
|
|
76
|
+
if (rendered?.api.remove) rendered.api.remove(rendered.id);
|
|
77
|
+
else if (rendered) rendered.api.reset(rendered.id);
|
|
78
|
+
widget.current = null;
|
|
79
|
+
};
|
|
80
|
+
}, [
|
|
81
|
+
captcha.provider,
|
|
82
|
+
captcha.siteKey,
|
|
83
|
+
captcha.isConfigured,
|
|
84
|
+
action
|
|
85
|
+
]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (busy) {
|
|
88
|
+
submitted.current = true;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!submitted.current) return;
|
|
92
|
+
submitted.current = false;
|
|
93
|
+
setToken("");
|
|
94
|
+
const rendered = widget.current;
|
|
95
|
+
if (rendered) rendered.api.reset(rendered.id);
|
|
96
|
+
}, [busy]);
|
|
97
|
+
if (captcha.provider === "off") return null;
|
|
98
|
+
if (!captcha.isConfigured) return /* @__PURE__ */ jsx("p", {
|
|
99
|
+
className: errorClass,
|
|
100
|
+
role: "alert",
|
|
101
|
+
children: storefrontTranslate("La tienda debe completar la configuración de verificación. Intenta más tarde.")
|
|
102
|
+
});
|
|
103
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
104
|
+
/* @__PURE__ */ jsx("div", { ref: element }),
|
|
105
|
+
/* @__PURE__ */ jsx("input", {
|
|
106
|
+
type: "hidden",
|
|
107
|
+
name: CAPTCHA_FIELDS[captcha.provider],
|
|
108
|
+
value: token
|
|
109
|
+
}),
|
|
110
|
+
failed ? /* @__PURE__ */ jsx("p", {
|
|
111
|
+
className: errorClass,
|
|
112
|
+
role: "alert",
|
|
113
|
+
children: storefrontTranslate("No se pudo cargar la verificación. Recarga la página e intenta de nuevo.")
|
|
114
|
+
}) : null
|
|
115
|
+
] });
|
|
116
|
+
}
|
|
117
|
+
export { Captcha, captchaResponse };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type CaptchaProvider = "off" | "turnstile" | "recaptcha";
|
|
2
|
+
export type CaptchaConfiguration = {
|
|
3
|
+
provider: CaptchaProvider;
|
|
4
|
+
siteKey: string | null;
|
|
5
|
+
isConfigured: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type CaptchaProps = {
|
|
8
|
+
captcha: CaptchaConfiguration;
|
|
9
|
+
action: string;
|
|
10
|
+
errorClass: string;
|
|
11
|
+
busy: boolean;
|
|
12
|
+
};
|
|
13
|
+
type CaptchaOptions = {
|
|
14
|
+
sitekey: string;
|
|
15
|
+
action?: string;
|
|
16
|
+
size: "normal" | "compact";
|
|
17
|
+
callback: (token: string) => void;
|
|
18
|
+
"expired-callback": () => void;
|
|
19
|
+
"error-callback": () => void;
|
|
20
|
+
};
|
|
21
|
+
type CaptchaApi = {
|
|
22
|
+
ready?: (callback: () => void) => void;
|
|
23
|
+
render: (container: HTMLElement, options: CaptchaOptions) => string | number;
|
|
24
|
+
remove?: (id: string | number) => void;
|
|
25
|
+
reset: (id: string | number) => void;
|
|
26
|
+
};
|
|
27
|
+
declare global {
|
|
28
|
+
interface Window {
|
|
29
|
+
turnstile?: CaptchaApi;
|
|
30
|
+
grecaptcha?: CaptchaApi;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export declare function Captcha({ captcha, action, errorClass, busy }: CaptchaProps): import("react").JSX.Element | null;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createCookie } from "react-router";
|
|
2
|
+
// src/cart.ts
|
|
3
|
+
const cartCookie = createCookie("__Host-ecomiq-cart", {
|
|
4
|
+
httpOnly: true,
|
|
5
|
+
sameSite: "lax",
|
|
6
|
+
secure: true,
|
|
7
|
+
path: "/",
|
|
8
|
+
maxAge: 2592e3
|
|
9
|
+
});
|
|
10
|
+
async function cartToken(request) {
|
|
11
|
+
const value = await cartCookie.parse(request.headers.get("Cookie"));
|
|
12
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
13
|
+
}
|
|
14
|
+
const CLOSED = /* @__PURE__ */ new Set([
|
|
15
|
+
"completed",
|
|
16
|
+
"cancelled",
|
|
17
|
+
"expired",
|
|
18
|
+
"failed"
|
|
19
|
+
]);
|
|
20
|
+
async function readCart(request, client) {
|
|
21
|
+
const token = await cartToken(request);
|
|
22
|
+
if (token === null) return null;
|
|
23
|
+
try {
|
|
24
|
+
const cart = await client.getCart({ cartToken: token });
|
|
25
|
+
return CLOSED.has(cart.status) ? null : cart;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export { cartCookie, cartToken, readCart };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { cartCookie, cartToken, readCart } from "./cart-D7DxZ2lD.js";
|
|
2
|
+
import { text } from "./form-BtQcbXlJ.js";
|
|
3
|
+
import { CART_INTENTS, isIntent } from "./form-contracts-BNpHaTGJ.js";
|
|
4
|
+
import { store } from "./store-8Q-_LFrQ.js";
|
|
5
|
+
import { data, redirect } from "react-router";
|
|
6
|
+
import { ApiError } from "@ecomiq/core";
|
|
7
|
+
// src/cart-actions.ts
|
|
8
|
+
const MAX_CART_QUANTITY = 999;
|
|
9
|
+
const MAX_REORDER_LINES = 20;
|
|
10
|
+
const ERRORS = {
|
|
11
|
+
"Catalog.InsufficientStock": "No hay stock suficiente para esa cantidad.",
|
|
12
|
+
"Inventory.InsufficientStock": "No hay stock suficiente para esa cantidad.",
|
|
13
|
+
"Catalog.VariantNotFound": "Este producto ya no está disponible.",
|
|
14
|
+
"Carts.QuantityOutOfRange": "Esa cantidad supera el máximo por pedido.",
|
|
15
|
+
"Carts.ItemNotFound": "Ese producto ya no está en tu carrito.",
|
|
16
|
+
"Checkout.InProgress": "Tu compra se está pagando. Termina el pago antes de cambiar el carrito."
|
|
17
|
+
};
|
|
18
|
+
const CLOSED = /* @__PURE__ */ new Set(["Carts.NotActive", "Carts.NotFound"]);
|
|
19
|
+
function failure(message, headers) {
|
|
20
|
+
return data({
|
|
21
|
+
ok: false,
|
|
22
|
+
message
|
|
23
|
+
}, {
|
|
24
|
+
status: 400,
|
|
25
|
+
headers
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async function apiFailure(error) {
|
|
29
|
+
if (error.code !== void 0 && CLOSED.has(error.code)) return failure("Tu carrito anterior ya se cerró. Vuelve a agregar el producto.", { "Set-Cookie": await cartCookie.serialize("", { maxAge: 0 }) });
|
|
30
|
+
if (error.code?.startsWith("Modifiers.")) return failure("Revisa las opciones elegidas.");
|
|
31
|
+
return failure((error.code === void 0 ? void 0 : ERRORS[error.code]) ?? "No pudimos actualizar el carrito. Intenta de nuevo.");
|
|
32
|
+
}
|
|
33
|
+
function quantity(form, minimum) {
|
|
34
|
+
const value = Math.floor(Number(text(form, "quantity")));
|
|
35
|
+
if (!Number.isFinite(value) || value < minimum) return minimum;
|
|
36
|
+
return Math.min(999, value);
|
|
37
|
+
}
|
|
38
|
+
function reorderLines(form) {
|
|
39
|
+
const quantities = form.getAll("quantity");
|
|
40
|
+
return form.getAll("variantId").map((value, index) => {
|
|
41
|
+
const amount = Math.floor(Number(quantities[index]));
|
|
42
|
+
return {
|
|
43
|
+
variantId: typeof value === "string" ? value.trim() : "",
|
|
44
|
+
quantity: Number.isFinite(amount) && amount > 0 ? Math.min(999, amount) : 1
|
|
45
|
+
};
|
|
46
|
+
}).filter((line) => line.variantId !== "").slice(0, MAX_REORDER_LINES);
|
|
47
|
+
}
|
|
48
|
+
async function availableVariant(client, slug) {
|
|
49
|
+
if (!slug) return null;
|
|
50
|
+
return (await client.getProductBySlug(slug)).variants.find((candidate) => candidate.id !== null && candidate.isAvailable !== false)?.id ?? null;
|
|
51
|
+
}
|
|
52
|
+
function goesToCheckout(intent, form) {
|
|
53
|
+
if (intent === "buy") return true;
|
|
54
|
+
if (intent !== "add" && intent !== "quick") return false;
|
|
55
|
+
const next = text(form, "next");
|
|
56
|
+
return next === "/checkout" || next === "checkout";
|
|
57
|
+
}
|
|
58
|
+
async function loadCart(request, context) {
|
|
59
|
+
return { cart: await readCart(request, store(context)) };
|
|
60
|
+
}
|
|
61
|
+
async function cartAction(request, context) {
|
|
62
|
+
const client = store(context);
|
|
63
|
+
const form = await request.formData();
|
|
64
|
+
const intent = text(form, "intent");
|
|
65
|
+
if (!isIntent(CART_INTENTS, intent)) return failure("Acción no válida.");
|
|
66
|
+
try {
|
|
67
|
+
let cart = await readCart(request, client);
|
|
68
|
+
if (cart === null) {
|
|
69
|
+
const settings = await client.getSettings();
|
|
70
|
+
cart = await client.createCart({ currencyCode: settings.store.currency });
|
|
71
|
+
}
|
|
72
|
+
if (intent === "add" || intent === "buy" || intent === "quick") {
|
|
73
|
+
const variantId = text(form, "variantId") || await availableVariant(client, text(form, "product"));
|
|
74
|
+
if (!variantId) return failure("Este producto no está disponible.");
|
|
75
|
+
const added = intent === "quick" ? 1 : quantity(form, 1);
|
|
76
|
+
cart = await client.addCartItem({
|
|
77
|
+
cartToken: cart.token,
|
|
78
|
+
variantId,
|
|
79
|
+
quantity: added
|
|
80
|
+
});
|
|
81
|
+
} else if (intent === "reorder") {
|
|
82
|
+
let added = 0;
|
|
83
|
+
for (const line of reorderLines(form)) try {
|
|
84
|
+
cart = await client.addCartItem({
|
|
85
|
+
cartToken: cart.token,
|
|
86
|
+
variantId: line.variantId,
|
|
87
|
+
quantity: line.quantity
|
|
88
|
+
});
|
|
89
|
+
added += 1;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (!(error instanceof ApiError) || error.status < 400 || error.status >= 500 || error.code !== void 0 && CLOSED.has(error.code)) throw error;
|
|
92
|
+
}
|
|
93
|
+
if (added === 0) return failure("Estos productos ya no están disponibles.");
|
|
94
|
+
} else if (intent === "update") cart = await client.updateCartItemQuantity({
|
|
95
|
+
cartToken: cart.token,
|
|
96
|
+
cartItemId: text(form, "itemId"),
|
|
97
|
+
quantity: quantity(form, 0)
|
|
98
|
+
});
|
|
99
|
+
else cart = await client.removeCartItem({
|
|
100
|
+
cartToken: cart.token,
|
|
101
|
+
cartItemId: text(form, "itemId")
|
|
102
|
+
});
|
|
103
|
+
let headers;
|
|
104
|
+
if (await cartToken(request) !== cart.token) headers = { "Set-Cookie": await cartCookie.serialize(cart.token) };
|
|
105
|
+
if (goesToCheckout(intent, form)) return redirect("/checkout", { headers });
|
|
106
|
+
if (intent === "reorder") return redirect("/cart", { headers });
|
|
107
|
+
return data({
|
|
108
|
+
ok: true,
|
|
109
|
+
message: ""
|
|
110
|
+
}, { headers });
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (error instanceof ApiError && error.status >= 400 && error.status < 500) return apiFailure(error);
|
|
113
|
+
return failure("No pudimos actualizar el carrito. Intenta de nuevo.");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export { MAX_CART_QUANTITY, cartAction, loadCart };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RouterContextProvider } from "react-router";
|
|
2
|
+
import { StorefrontCart } from "@ecomiq/core";
|
|
3
|
+
export declare const MAX_CART_QUANTITY = 999;
|
|
4
|
+
export type CartData = {
|
|
5
|
+
cart: StorefrontCart | null;
|
|
6
|
+
};
|
|
7
|
+
export type CartActionData = {
|
|
8
|
+
ok: true;
|
|
9
|
+
message: "";
|
|
10
|
+
} | {
|
|
11
|
+
ok: false;
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function loadCart(request: Request, context: Readonly<RouterContextProvider>): Promise<CartData>;
|
|
15
|
+
export declare function cartAction(request: Request, context: Readonly<RouterContextProvider>): Promise<import("react-router").UNSAFE_DataWithResponseInit<CartActionData> | Response>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
// src/cart-drawer.tsx
|
|
4
|
+
const CartDrawerContext = createContext({
|
|
5
|
+
open: false,
|
|
6
|
+
setOpen: () => void 0
|
|
7
|
+
});
|
|
8
|
+
function CartDrawerProvider({ children }) {
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
|
+
const drawer = useMemo(() => ({
|
|
11
|
+
open,
|
|
12
|
+
setOpen
|
|
13
|
+
}), [open]);
|
|
14
|
+
return /* @__PURE__ */ jsx(CartDrawerContext.Provider, {
|
|
15
|
+
value: drawer,
|
|
16
|
+
children
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function useCartDrawer() {
|
|
20
|
+
return useContext(CartDrawerContext);
|
|
21
|
+
}
|
|
22
|
+
export { CartDrawerProvider, useCartDrawer };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
export type CartDrawer = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
setOpen: (open: boolean) => void;
|
|
5
|
+
};
|
|
6
|
+
export declare function CartDrawerProvider({ children }: {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
export declare function useCartDrawer(): CartDrawer;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { CustomerCsrfField, useCustomerCsrfToken } from "./customer-csrf-C1oDX-Py.js";
|
|
2
|
+
import { CUSTOMER_CSRF_FIELD } from "./form-contracts-BNpHaTGJ.js";
|
|
3
|
+
import { Form, useFetcher } from "react-router";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
// src/cart-form.tsx
|
|
6
|
+
const CART_ACTION = "/cart";
|
|
7
|
+
function Hidden({ name, value }) {
|
|
8
|
+
if (value === void 0) return null;
|
|
9
|
+
return /* @__PURE__ */ jsx("input", {
|
|
10
|
+
type: "hidden",
|
|
11
|
+
name,
|
|
12
|
+
value
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function CartForm({ intent, itemId, variantId, product, quantity, next, fetcher, children, ...props }) {
|
|
16
|
+
const Component = fetcher ? fetcher.Form : Form;
|
|
17
|
+
return /* @__PURE__ */ jsxs(Component, {
|
|
18
|
+
method: "post",
|
|
19
|
+
action: CART_ACTION,
|
|
20
|
+
...props,
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ jsx(CustomerCsrfField, {}),
|
|
23
|
+
/* @__PURE__ */ jsx(Hidden, {
|
|
24
|
+
name: "intent",
|
|
25
|
+
value: intent
|
|
26
|
+
}),
|
|
27
|
+
/* @__PURE__ */ jsx(Hidden, {
|
|
28
|
+
name: "itemId",
|
|
29
|
+
value: itemId
|
|
30
|
+
}),
|
|
31
|
+
/* @__PURE__ */ jsx(Hidden, {
|
|
32
|
+
name: "variantId",
|
|
33
|
+
value: variantId
|
|
34
|
+
}),
|
|
35
|
+
/* @__PURE__ */ jsx(Hidden, {
|
|
36
|
+
name: "product",
|
|
37
|
+
value: product
|
|
38
|
+
}),
|
|
39
|
+
/* @__PURE__ */ jsx(Hidden, {
|
|
40
|
+
name: "quantity",
|
|
41
|
+
value: quantity
|
|
42
|
+
}),
|
|
43
|
+
/* @__PURE__ */ jsx(Hidden, {
|
|
44
|
+
name: "next",
|
|
45
|
+
value: next
|
|
46
|
+
}),
|
|
47
|
+
children
|
|
48
|
+
]
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function useCart() {
|
|
52
|
+
const fetcher = useFetcher();
|
|
53
|
+
const csrf = useCustomerCsrfToken();
|
|
54
|
+
function submit(intent, fields) {
|
|
55
|
+
const form = new FormData();
|
|
56
|
+
form.set("intent", intent);
|
|
57
|
+
for (const [name, value] of Object.entries(fields)) form.set(name, value);
|
|
58
|
+
if (csrf) form.set(CUSTOMER_CSRF_FIELD, csrf);
|
|
59
|
+
fetcher.submit(form, {
|
|
60
|
+
method: "post",
|
|
61
|
+
action: CART_ACTION
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
let error = null;
|
|
65
|
+
if (fetcher.state === "idle" && fetcher.data?.ok === false) error = fetcher.data.message;
|
|
66
|
+
return {
|
|
67
|
+
fetcher,
|
|
68
|
+
busy: fetcher.state !== "idle",
|
|
69
|
+
error,
|
|
70
|
+
add: (variantId, quantity = 1) => submit("add", {
|
|
71
|
+
variantId,
|
|
72
|
+
quantity: String(Math.max(1, quantity))
|
|
73
|
+
}),
|
|
74
|
+
update: (itemId, quantity) => submit("update", {
|
|
75
|
+
itemId,
|
|
76
|
+
quantity: String(Math.max(0, quantity))
|
|
77
|
+
}),
|
|
78
|
+
remove: (itemId) => submit("remove", { itemId })
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export { CART_ACTION, CartForm, useCart };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CartActionData } from "./cart-actions.js";
|
|
2
|
+
import { CartIntent } from "./form-contracts.js";
|
|
3
|
+
import { FetcherFormProps, FetcherWithComponents } from "react-router";
|
|
4
|
+
import { Ref } from "react";
|
|
5
|
+
export type CartFormProps = Omit<FetcherFormProps, "action" | "method" | "encType"> & {
|
|
6
|
+
ref?: Ref<HTMLFormElement>;
|
|
7
|
+
intent?: CartIntent;
|
|
8
|
+
itemId?: string;
|
|
9
|
+
variantId?: string;
|
|
10
|
+
product?: string;
|
|
11
|
+
quantity?: number;
|
|
12
|
+
next?: string;
|
|
13
|
+
fetcher?: FetcherWithComponents<CartActionData>;
|
|
14
|
+
};
|
|
15
|
+
export type CartControls = {
|
|
16
|
+
fetcher: FetcherWithComponents<CartActionData>;
|
|
17
|
+
busy: boolean;
|
|
18
|
+
error: string | null;
|
|
19
|
+
add: (variantId: string, quantity?: number) => void;
|
|
20
|
+
update: (itemId: string, quantity: number) => void;
|
|
21
|
+
remove: (itemId: string) => void;
|
|
22
|
+
};
|
|
23
|
+
export declare function CartForm({ intent, itemId, variantId, product, quantity, next, fetcher, children, ...props }: CartFormProps): import("react").JSX.Element;
|
|
24
|
+
export declare function useCart(): CartControls;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/cart-summary.ts
|
|
2
|
+
const LABELS = {
|
|
3
|
+
subtotal: "Subtotal",
|
|
4
|
+
discount: "Descuentos",
|
|
5
|
+
shipping: "Envío",
|
|
6
|
+
tax: "Impuestos",
|
|
7
|
+
total: "Total",
|
|
8
|
+
free: "Gratis",
|
|
9
|
+
pending: "Por calcular"
|
|
10
|
+
};
|
|
11
|
+
function lineSubtotal(items) {
|
|
12
|
+
return items.reduce((sum, item) => sum + item.unitPrice * item.quantity, 0);
|
|
13
|
+
}
|
|
14
|
+
function groupBySeller(items, marketplace) {
|
|
15
|
+
if (!marketplace) return [{
|
|
16
|
+
seller: null,
|
|
17
|
+
items: [...items],
|
|
18
|
+
subtotal: lineSubtotal(items)
|
|
19
|
+
}];
|
|
20
|
+
const groups = /* @__PURE__ */ new Map();
|
|
21
|
+
for (const item of items) groups.set(item.sellerName, [...groups.get(item.sellerName) ?? [], item]);
|
|
22
|
+
return [...groups.entries()].map(([seller, list]) => ({
|
|
23
|
+
seller,
|
|
24
|
+
items: list,
|
|
25
|
+
subtotal: lineSubtotal(list)
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
function amountLine(key, label, amount) {
|
|
29
|
+
return {
|
|
30
|
+
key,
|
|
31
|
+
label,
|
|
32
|
+
amount,
|
|
33
|
+
text: null,
|
|
34
|
+
pending: false
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function lines(totals, shipping, labels) {
|
|
38
|
+
const result = [amountLine("subtotal", labels.subtotal, totals.subtotal)];
|
|
39
|
+
if (totals.discount > 0) result.push(amountLine("discount", labels.discount, -totals.discount));
|
|
40
|
+
if (shipping) result.push(shipping);
|
|
41
|
+
if (totals.tax > 0) result.push(amountLine("tax", labels.tax, totals.tax));
|
|
42
|
+
result.push(amountLine("total", labels.total, totals.total));
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function shippingLine(amount, labels) {
|
|
46
|
+
if (amount === null) return {
|
|
47
|
+
key: "shipping",
|
|
48
|
+
label: labels.shipping,
|
|
49
|
+
amount: null,
|
|
50
|
+
text: labels.pending,
|
|
51
|
+
pending: true
|
|
52
|
+
};
|
|
53
|
+
if (amount === 0) return {
|
|
54
|
+
key: "shipping",
|
|
55
|
+
label: labels.shipping,
|
|
56
|
+
amount: null,
|
|
57
|
+
text: labels.free,
|
|
58
|
+
pending: false
|
|
59
|
+
};
|
|
60
|
+
return amountLine("shipping", labels.shipping, amount);
|
|
61
|
+
}
|
|
62
|
+
function summaryLines(cart, labels = {}) {
|
|
63
|
+
const text = {
|
|
64
|
+
...LABELS,
|
|
65
|
+
...labels
|
|
66
|
+
};
|
|
67
|
+
let shipping = null;
|
|
68
|
+
if (cart.items.some((item) => item.requiresShipping)) shipping = shippingLine(cart.shippingMethods.length > 0 ? cart.totals.shippingTotal : null, text);
|
|
69
|
+
const { subtotal, discountTotal, taxTotal, total } = cart.totals;
|
|
70
|
+
return lines({
|
|
71
|
+
subtotal,
|
|
72
|
+
discount: discountTotal,
|
|
73
|
+
tax: taxTotal,
|
|
74
|
+
total
|
|
75
|
+
}, shipping, text);
|
|
76
|
+
}
|
|
77
|
+
function orderSummaryLines(order, labels = {}) {
|
|
78
|
+
const text = {
|
|
79
|
+
...LABELS,
|
|
80
|
+
...labels
|
|
81
|
+
};
|
|
82
|
+
return lines({
|
|
83
|
+
subtotal: order.subTotal,
|
|
84
|
+
discount: order.discount,
|
|
85
|
+
tax: order.tax,
|
|
86
|
+
total: order.total
|
|
87
|
+
}, shippingLine(order.shipping, text), text);
|
|
88
|
+
}
|
|
89
|
+
export { groupBySeller, orderSummaryLines, summaryLines };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { StorefrontCart } from "@ecomiq/core";
|
|
2
|
+
export type SellerLine = {
|
|
3
|
+
sellerName: string | null;
|
|
4
|
+
unitPrice: number;
|
|
5
|
+
quantity: number;
|
|
6
|
+
};
|
|
7
|
+
export type SellerGroup<T extends SellerLine> = {
|
|
8
|
+
seller: string | null;
|
|
9
|
+
items: T[];
|
|
10
|
+
subtotal: number;
|
|
11
|
+
};
|
|
12
|
+
export type SummaryLineKey = "subtotal" | "discount" | "shipping" | "tax" | "total";
|
|
13
|
+
export type SummaryLine = {
|
|
14
|
+
key: SummaryLineKey;
|
|
15
|
+
label: string;
|
|
16
|
+
amount: number | null;
|
|
17
|
+
text: string | null;
|
|
18
|
+
pending: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type SummaryLabels = Partial<Record<SummaryLineKey | "free" | "pending", string>>;
|
|
21
|
+
export type SummaryCart = Pick<StorefrontCart, "items" | "totals" | "shippingMethods">;
|
|
22
|
+
export type SummaryOrder = {
|
|
23
|
+
subTotal: number;
|
|
24
|
+
discount: number;
|
|
25
|
+
shipping: number;
|
|
26
|
+
tax: number;
|
|
27
|
+
total: number;
|
|
28
|
+
};
|
|
29
|
+
export declare function groupBySeller<T extends SellerLine>(items: readonly T[], marketplace: boolean): SellerGroup<T>[];
|
|
30
|
+
export declare function summaryLines(cart: SummaryCart, labels?: SummaryLabels): SummaryLine[];
|
|
31
|
+
export declare function orderSummaryLines(order: SummaryOrder, labels?: SummaryLabels): SummaryLine[];
|
package/dist/cart.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { StorefrontCart, StorefrontClient } from "@ecomiq/core";
|
|
2
|
+
export declare const cartCookie: import("react-router").Cookie;
|
|
3
|
+
export declare function cartToken(request: Request): Promise<string | null>;
|
|
4
|
+
export declare function readCart(request: Request, client: StorefrontClient): Promise<StorefrontCart | null>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/catalog.ts
|
|
2
|
+
const LISTS = {
|
|
3
|
+
category: "categories",
|
|
4
|
+
brand: "brands",
|
|
5
|
+
collection: "collections",
|
|
6
|
+
productType: "productTypes",
|
|
7
|
+
tag: "tags",
|
|
8
|
+
seller: "sellers"
|
|
9
|
+
};
|
|
10
|
+
const SORTS = [
|
|
11
|
+
"newest",
|
|
12
|
+
"price_asc",
|
|
13
|
+
"price_desc",
|
|
14
|
+
"name_asc",
|
|
15
|
+
"name_desc"
|
|
16
|
+
];
|
|
17
|
+
const CATALOG_PARAMETERS = /* @__PURE__ */ new Set([
|
|
18
|
+
"q",
|
|
19
|
+
"sort",
|
|
20
|
+
"page",
|
|
21
|
+
"availability",
|
|
22
|
+
"minPrice",
|
|
23
|
+
"maxPrice",
|
|
24
|
+
...Object.keys(LISTS)
|
|
25
|
+
]);
|
|
26
|
+
/** Menus, customer and cart data do not depend on filters in the current catalog. */
|
|
27
|
+
function shouldRevalidateCatalogRoot(args) {
|
|
28
|
+
const { currentUrl, nextUrl, defaultShouldRevalidate, formMethod, actionResult, actionStatus } = args;
|
|
29
|
+
if (!defaultShouldRevalidate || formMethod !== void 0 && formMethod.toUpperCase() !== "GET" || actionResult !== void 0 || actionStatus !== void 0 || currentUrl.origin !== nextUrl.origin || currentUrl.pathname !== nextUrl.pathname || currentUrl.search === nextUrl.search) return defaultShouldRevalidate;
|
|
30
|
+
for (const params of [currentUrl.searchParams, nextUrl.searchParams]) for (const name of params.keys()) if (!CATALOG_PARAMETERS.has(name) && !/^(?:option|attribute):.+$/.test(name)) return defaultShouldRevalidate;
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function isSort(value) {
|
|
34
|
+
return SORTS.some((sort) => sort === value);
|
|
35
|
+
}
|
|
36
|
+
function price(value) {
|
|
37
|
+
if (value === null || value.trim() === "") return void 0;
|
|
38
|
+
const number = Number(value);
|
|
39
|
+
return Number.isFinite(number) && number >= 0 ? number : void 0;
|
|
40
|
+
}
|
|
41
|
+
function readCatalog(url, base = {}) {
|
|
42
|
+
const params = url.searchParams;
|
|
43
|
+
const query = {
|
|
44
|
+
...base,
|
|
45
|
+
pageSize: 24,
|
|
46
|
+
page: Math.max(1, Number(params.get("page") ?? "1") || 1)
|
|
47
|
+
};
|
|
48
|
+
const q = params.get("q")?.trim();
|
|
49
|
+
if (q) query.search = q;
|
|
50
|
+
const sort = params.get("sort");
|
|
51
|
+
if (isSort(sort)) query.sort = sort;
|
|
52
|
+
for (const [id, key] of Object.entries(LISTS)) {
|
|
53
|
+
const values = params.getAll(id).filter((value) => value !== "");
|
|
54
|
+
if (values.length > 0) query[key] = values;
|
|
55
|
+
}
|
|
56
|
+
const filters = [...params.entries()].filter(([key]) => key.startsWith("option:") || key.startsWith("attribute:")).map(([key, value]) => `${key}:${value}`);
|
|
57
|
+
if (filters.length > 0) query.filters = filters;
|
|
58
|
+
const availability = params.get("availability");
|
|
59
|
+
if (availability === "in_stock") query.inStock = true;
|
|
60
|
+
if (availability === "out_of_stock") query.inStock = false;
|
|
61
|
+
const minPrice = price(params.get("minPrice"));
|
|
62
|
+
const maxPrice = price(params.get("maxPrice"));
|
|
63
|
+
if (minPrice !== void 0) query.minPrice = minPrice;
|
|
64
|
+
if (maxPrice !== void 0) query.maxPrice = maxPrice;
|
|
65
|
+
return query;
|
|
66
|
+
}
|
|
67
|
+
export { readCatalog, shouldRevalidateCatalogRoot };
|