@ecomiq/storefront 2.1.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +150 -103
- 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,586 @@
|
|
|
1
|
+
import type { ComponentProps, CSSProperties, ReactNode, SelectHTMLAttributes } from "react";
|
|
2
|
+
import { Link, useRouteLoaderData } from "react-router";
|
|
3
|
+
import { Button } from "@theme/ui/button";
|
|
4
|
+
import { BankAccountList, useStorefrontText, PaymentLogo, money, section, setting, type SectionCatalog, type SectionProps, type SectionRoot } from "../index";
|
|
5
|
+
import { placeValue, useGeoCascade } from "../entries/account";
|
|
6
|
+
import {
|
|
7
|
+
CheckoutForm,
|
|
8
|
+
CulqiAuthentication,
|
|
9
|
+
InvoiceFields,
|
|
10
|
+
MercadoPagoDialog,
|
|
11
|
+
OrderBumpForm,
|
|
12
|
+
PaymentMethods,
|
|
13
|
+
PromoCodeForm,
|
|
14
|
+
StripeAuthentication,
|
|
15
|
+
StripeDialog,
|
|
16
|
+
findStripeWallet,
|
|
17
|
+
summaryLines,
|
|
18
|
+
useAvailableStripeWallets,
|
|
19
|
+
useCheckout,
|
|
20
|
+
useCheckoutPayment,
|
|
21
|
+
type CheckoutActionData,
|
|
22
|
+
type CheckoutAddressDefaults,
|
|
23
|
+
type CheckoutData,
|
|
24
|
+
type CheckoutStep,
|
|
25
|
+
} from "../entries/checkout";
|
|
26
|
+
|
|
27
|
+
export const catalog: SectionCatalog = {
|
|
28
|
+
name: "Finalizar compra",
|
|
29
|
+
description: "Checkout de la tienda: contacto, entrega, envío y pago, con el resumen del pedido.",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const settings = {
|
|
33
|
+
checkout: {
|
|
34
|
+
layout: setting.select("steps", "Tipo de checkout", { steps: "Por pasos", single: "Una página" }),
|
|
35
|
+
summary: setting.select("right", "Resumen del pedido", { right: "A la derecha", left: "A la izquierda" }),
|
|
36
|
+
main: setting.scheme("claro", "Esquema del formulario"),
|
|
37
|
+
aside: setting.scheme("claro", "Esquema del resumen"),
|
|
38
|
+
logo: setting.select("left", "Logo", { left: "A la izquierda", center: "Centrado" }),
|
|
39
|
+
secure: setting.toggle(true, "«Compra segura» en la cabecera"),
|
|
40
|
+
marks: setting.toggle(true, "Logos de medios de pago"),
|
|
41
|
+
encrypted: setting.toggle(true, "Aviso de datos cifrados"),
|
|
42
|
+
fields: setting.select("outline", "Campos", { outline: "Con borde", filled: "Con fondo" }),
|
|
43
|
+
corners: setting.select("soft", "Esquinas", { square: "Rectas", soft: "Suaves", round: "Redondeadas" }),
|
|
44
|
+
button: setting.select("full", "Botón de pagar", { full: "A todo el ancho", fit: "Al contenido" }),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
type Corners = (typeof settings.checkout)["corners"];
|
|
49
|
+
type Shop = { name: string; logo: string | null; language: string };
|
|
50
|
+
|
|
51
|
+
const RADIUS: { [corner in Corners]: string } = { square: "0px", soft: "0.5rem", round: "1rem" };
|
|
52
|
+
const FIELD =
|
|
53
|
+
"peer w-full rounded-md border border-input bg-background px-3.5 pt-5 pb-1.5 text-base text-foreground md:text-sm outline-none transition-colors placeholder:text-transparent focus:border-ring focus:ring-1 focus:ring-ring disabled:opacity-60 group-data-[fields=filled]/checkout:border-transparent group-data-[fields=filled]/checkout:bg-muted";
|
|
54
|
+
const FLOAT =
|
|
55
|
+
"pointer-events-none absolute top-1/2 left-3.5 -translate-y-1/2 text-sm text-muted-foreground transition-all peer-focus:top-3 peer-focus:text-[11px] peer-[:not(:placeholder-shown)]:top-3 peer-[:not(:placeholder-shown)]:text-[11px]";
|
|
56
|
+
const GROUP = "overflow-hidden rounded-md border border-input bg-background";
|
|
57
|
+
const ROW = "flex cursor-pointer items-center gap-3 px-4 py-3.5 text-sm has-[:checked]:bg-muted/60";
|
|
58
|
+
const STEPS: readonly { step: CheckoutStep; label: string }[] = [
|
|
59
|
+
{ step: "contact", label: "Contacto" },
|
|
60
|
+
{ step: "delivery", label: "Entrega" },
|
|
61
|
+
{ step: "shipping", label: "Envío" },
|
|
62
|
+
{ step: "payment", label: "Pago" },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
function shopOf(data: unknown): Shop {
|
|
66
|
+
const shop = typeof data === "object" && data !== null ? (data as { shop?: Partial<Shop> }).shop : undefined;
|
|
67
|
+
return { name: shop?.name ?? "", logo: shop?.logo ?? null, language: shop?.language ?? "es" };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function Lock() {
|
|
71
|
+
return (
|
|
72
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" className="size-4" fill="none" stroke="currentColor" strokeWidth={1.8}>
|
|
73
|
+
<rect x="5" y="11" width="14" height="9" rx="2" />
|
|
74
|
+
<path d="M8 11V8a4 4 0 0 1 8 0v3" />
|
|
75
|
+
</svg>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function Bag() {
|
|
80
|
+
return (
|
|
81
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" className="size-5" fill="none" stroke="currentColor" strokeWidth={1.6}>
|
|
82
|
+
<path d="M6 8h12l-1 12H7L6 8Z" />
|
|
83
|
+
<path d="M9 8a3 3 0 0 1 6 0" />
|
|
84
|
+
</svg>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function Field({ label, className, ...props }: ComponentProps<"input"> & { label: string; name: string }) {
|
|
89
|
+
return (
|
|
90
|
+
<label className={`relative block ${className ?? ""}`}>
|
|
91
|
+
<input id={props.id ?? props.name} placeholder={label} className={FIELD} {...props} />
|
|
92
|
+
<span className={FLOAT}>{label}</span>
|
|
93
|
+
</label>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function Choice({ label, className, children, ...props }: SelectHTMLAttributes<HTMLSelectElement> & { label: string; name: string }) {
|
|
98
|
+
return (
|
|
99
|
+
<label className={`relative block ${className ?? ""}`}>
|
|
100
|
+
<select id={props.id ?? props.name} className={`${FIELD} appearance-none pr-9`} {...props}>
|
|
101
|
+
{children}
|
|
102
|
+
</select>
|
|
103
|
+
<span className="pointer-events-none absolute top-3 left-3.5 -translate-y-1/2 text-[11px] text-muted-foreground">{label}</span>
|
|
104
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" className="pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
105
|
+
<path d="m6 9 6 6 6-6" />
|
|
106
|
+
</svg>
|
|
107
|
+
</label>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function Address({ countries, defaults }: { countries: { code: string; name: string }[]; defaults: CheckoutAddressDefaults }) {
|
|
112
|
+
const storefrontTranslate = useStorefrontText();
|
|
113
|
+
const geo = useGeoCascade({ ...defaults, country: countries[0]?.code });
|
|
114
|
+
const { country, department, province, district } = geo;
|
|
115
|
+
return (
|
|
116
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
117
|
+
<Choice label={storefrontTranslate("País")} name="country" value={country} onChange={(event) => geo.setCountry(event.target.value)} className="sm:col-span-2">
|
|
118
|
+
{countries.map((item) => (
|
|
119
|
+
<option key={item.code} value={item.code}>
|
|
120
|
+
{item.name}
|
|
121
|
+
</option>
|
|
122
|
+
))}
|
|
123
|
+
</Choice>
|
|
124
|
+
{geo.structured ? (
|
|
125
|
+
<>
|
|
126
|
+
<Choice label={storefrontTranslate("Departamento")} name="department" required value={placeValue(department)} onChange={(event) => geo.setDepartmentValue(event.target.value)}>
|
|
127
|
+
<option value="">{storefrontTranslate("Elige…")}</option>
|
|
128
|
+
{geo.places.departments.map((item) => (
|
|
129
|
+
<option key={item.code} value={placeValue(item)}>
|
|
130
|
+
{item.name}
|
|
131
|
+
</option>
|
|
132
|
+
))}
|
|
133
|
+
</Choice>
|
|
134
|
+
<Choice label={storefrontTranslate("Provincia")} name="province" required disabled={department === null} value={placeValue(province)} onChange={(event) => geo.setProvinceValue(event.target.value)}>
|
|
135
|
+
<option value="">{storefrontTranslate("Elige…")}</option>
|
|
136
|
+
{geo.places.provinces.map((item) => (
|
|
137
|
+
<option key={item.code} value={placeValue(item)}>
|
|
138
|
+
{item.name}
|
|
139
|
+
</option>
|
|
140
|
+
))}
|
|
141
|
+
</Choice>
|
|
142
|
+
<Choice
|
|
143
|
+
label={storefrontTranslate("Distrito")}
|
|
144
|
+
name="district"
|
|
145
|
+
required
|
|
146
|
+
disabled={province === null}
|
|
147
|
+
value={placeValue(district)}
|
|
148
|
+
onChange={(event) => geo.setDistrictValue(event.target.value)}
|
|
149
|
+
className="sm:col-span-2"
|
|
150
|
+
>
|
|
151
|
+
<option value="">{storefrontTranslate("Elige…")}</option>
|
|
152
|
+
{geo.places.districts.map((item) => (
|
|
153
|
+
<option key={item.code} value={placeValue(item)}>
|
|
154
|
+
{item.name}
|
|
155
|
+
</option>
|
|
156
|
+
))}
|
|
157
|
+
</Choice>
|
|
158
|
+
</>
|
|
159
|
+
) : (
|
|
160
|
+
<>
|
|
161
|
+
<Field label={storefrontTranslate("Región")} name="department" defaultValue={defaults.department ?? ""} required />
|
|
162
|
+
<Field label={storefrontTranslate("Ciudad")} name="district" defaultValue={defaults.district ?? ""} required />
|
|
163
|
+
</>
|
|
164
|
+
)}
|
|
165
|
+
<Field label={storefrontTranslate("Dirección")} name="line" autoComplete="street-address" defaultValue={defaults.line ?? ""} required className="sm:col-span-2" />
|
|
166
|
+
<Field label={storefrontTranslate("Referencia (opcional)")} name="reference" defaultValue={defaults.reference ?? ""} />
|
|
167
|
+
<Field label={storefrontTranslate("Celular")} name="phone" type="tel" autoComplete="tel" defaultValue={defaults.phone ?? ""} required />
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function Heading({ children, aside }: { children: ReactNode; aside?: ReactNode }) {
|
|
173
|
+
return (
|
|
174
|
+
<div className="mb-3 flex items-baseline justify-between gap-3">
|
|
175
|
+
<h2 className="text-lg font-semibold">{children}</h2>
|
|
176
|
+
{aside}
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function Review({ rows }: { rows: { step: CheckoutStep; label: string; value: string }[] }) {
|
|
182
|
+
const storefrontTranslate = useStorefrontText();
|
|
183
|
+
if (rows.length === 0) return null;
|
|
184
|
+
return (
|
|
185
|
+
<dl className={`${GROUP} divide-y divide-input text-sm`}>
|
|
186
|
+
{rows.map((row) => (
|
|
187
|
+
<div key={row.step} className="flex items-start gap-4 px-4 py-3">
|
|
188
|
+
<dt className="w-24 shrink-0 text-muted-foreground">{storefrontTranslate(row.label)}</dt>
|
|
189
|
+
<dd className="min-w-0 flex-1 truncate">{row.value}</dd>
|
|
190
|
+
<Link to={`?edit=${row.step}`} className="shrink-0 text-xs underline underline-offset-4">
|
|
191
|
+
{storefrontTranslate("Cambiar")}</Link>
|
|
192
|
+
</div>
|
|
193
|
+
))}
|
|
194
|
+
</dl>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const MOBILE_LOGOS = 3;
|
|
199
|
+
|
|
200
|
+
export function Checkout({ data, result, ...root }: { data: CheckoutData; result?: CheckoutActionData } & SectionProps<typeof settings.checkout> & SectionRoot) {
|
|
201
|
+
const storefrontTranslate = useStorefrontText();
|
|
202
|
+
const checkout = section(settings.checkout, root);
|
|
203
|
+
const shop = shopOf(useRouteLoaderData("root"));
|
|
204
|
+
const { cart, methods, bumps, me, signedIn, countries, shipping, step, done, needsDelivery, defaults, paymentMethod, choosePaymentMethod, error, promoError, busy, single } =
|
|
205
|
+
useCheckout(data, result, checkout.layout);
|
|
206
|
+
const payment = useCheckoutPayment(methods, cart, shop.name);
|
|
207
|
+
const wallets = useAvailableStripeWallets(methods, cart.totals.total, cart.currencyCode);
|
|
208
|
+
const offeredMethods = methods.filter((method) => {
|
|
209
|
+
const wallet = findStripeWallet(method);
|
|
210
|
+
return wallet === null || wallets.has(wallet);
|
|
211
|
+
});
|
|
212
|
+
if (result && "authentication" in result) {
|
|
213
|
+
if (result.authentication.payment.providerId === "stripe") return <StripeAuthentication {...result.authentication} />;
|
|
214
|
+
return <CulqiAuthentication {...result.authentication} email={cart.email} source={payment.source.current} />;
|
|
215
|
+
}
|
|
216
|
+
const alert = payment.error ?? error;
|
|
217
|
+
const paying = busy || payment.loading;
|
|
218
|
+
const format = (value: number) => money(value, cart.currencyCode, `${data.language}-${data.countryCode}`);
|
|
219
|
+
const attributes = Object.fromEntries(Object.entries(root).filter(([key]) => key.startsWith("data-")));
|
|
220
|
+
const style = { "--radius": RADIUS[checkout.corners] } as CSSProperties;
|
|
221
|
+
const total = summaryLines(cart).find((line) => line.key === "total")?.amount ?? cart.totals.total;
|
|
222
|
+
const open = (current: CheckoutStep) => single || step === current;
|
|
223
|
+
const saved = cart.shippingAddress;
|
|
224
|
+
const current = STEPS.findIndex((item) => item.step === step);
|
|
225
|
+
const rows: { step: CheckoutStep; label: string; value: string }[] = [];
|
|
226
|
+
if (!single && current > 0 && done.contact) rows.push({ step: "contact", label: storefrontTranslate("Contacto"), value: `${cart.email ?? ""} · ${cart.phone ?? ""}` });
|
|
227
|
+
if (!single && current > 1 && saved) rows.push({ step: "delivery", label: storefrontTranslate("Enviar a"), value: [saved.address1, saved.district, saved.province].filter(Boolean).join(", ") });
|
|
228
|
+
if (!single && current > 2 && done.shipping)
|
|
229
|
+
rows.push({ step: "shipping", label: storefrontTranslate("Envío"), value: cart.shippingMethods.map((method) => `${method.name} · ${method.total === 0 ? storefrontTranslate("Gratis") : format(method.total)}`).join(" · ") });
|
|
230
|
+
const lines = (
|
|
231
|
+
<ul className="flex flex-col gap-4">
|
|
232
|
+
{cart.items.map((item) => (
|
|
233
|
+
<li key={item.id} className="flex items-center gap-4 text-sm">
|
|
234
|
+
<span className="relative size-16 shrink-0 rounded-md border border-input bg-background">
|
|
235
|
+
<span className="block size-full overflow-hidden rounded-md">
|
|
236
|
+
{item.thumbnail ? <img src={item.thumbnail} alt="" width={64} height={64} loading="lazy" decoding="async" className="size-full object-cover" /> : null}
|
|
237
|
+
</span>
|
|
238
|
+
<span className="absolute -top-2 -right-2 flex size-5 items-center justify-center rounded-full bg-muted-foreground text-[11px] font-medium text-background">{item.quantity}</span>
|
|
239
|
+
</span>
|
|
240
|
+
<span className="flex min-w-0 flex-1 flex-col">
|
|
241
|
+
<span className="font-medium">{item.title}</span>
|
|
242
|
+
{item.variantLabel && item.variantLabel !== item.title ? <span className="text-xs text-muted-foreground">{item.variantLabel}</span> : null}
|
|
243
|
+
{item.sellerName ? <span className="text-xs text-muted-foreground">{storefrontTranslate("Vendido por")} {item.sellerName}</span> : null}
|
|
244
|
+
</span>
|
|
245
|
+
<span className="tabular-nums">{format(item.unitPrice * item.quantity)}</span>
|
|
246
|
+
</li>
|
|
247
|
+
))}
|
|
248
|
+
</ul>
|
|
249
|
+
);
|
|
250
|
+
const totals = (
|
|
251
|
+
<>
|
|
252
|
+
<PromoCodeForm className="flex gap-2">
|
|
253
|
+
<Field label={storefrontTranslate("Código de descuento")} name="code" className="flex-1" />
|
|
254
|
+
<Button type="submit" disabled={busy}>
|
|
255
|
+
{storefrontTranslate("Aplicar")}</Button>
|
|
256
|
+
</PromoCodeForm>
|
|
257
|
+
{promoError ? <p className="-mt-3 text-sm text-destructive">{storefrontTranslate(promoError)}</p> : null}
|
|
258
|
+
{cart.promotionCodes.map((code) => (
|
|
259
|
+
<PromoCodeForm key={code} remove={code} className="flex items-center justify-between text-sm">
|
|
260
|
+
<span className="rounded-md bg-muted px-2 py-1 font-medium">{code}</span>
|
|
261
|
+
<button type="submit" className="text-xs underline underline-offset-4">
|
|
262
|
+
{storefrontTranslate("Quitar")}</button>
|
|
263
|
+
</PromoCodeForm>
|
|
264
|
+
))}
|
|
265
|
+
<dl className="flex flex-col gap-2 text-sm">
|
|
266
|
+
{summaryLines(cart).map((line) => {
|
|
267
|
+
if (line.key === "total")
|
|
268
|
+
return (
|
|
269
|
+
<div key={line.key} className="flex items-baseline justify-between pt-2">
|
|
270
|
+
<dt className="text-base font-semibold">{storefrontTranslate(line.label)}</dt>
|
|
271
|
+
<dd className="flex items-baseline gap-2">
|
|
272
|
+
<span className="text-xs text-muted-foreground">{cart.currencyCode}</span>
|
|
273
|
+
<span className="text-xl font-semibold tabular-nums">{format(line.amount ?? 0)}</span>
|
|
274
|
+
</dd>
|
|
275
|
+
</div>
|
|
276
|
+
);
|
|
277
|
+
return (
|
|
278
|
+
<div key={line.key} className="flex justify-between">
|
|
279
|
+
<dt className="text-muted-foreground">{storefrontTranslate(line.label)}</dt>
|
|
280
|
+
<dd className="tabular-nums">{line.amount === null ? storefrontTranslate(line.text ?? "") : format(line.amount)}</dd>
|
|
281
|
+
</div>
|
|
282
|
+
);
|
|
283
|
+
})}
|
|
284
|
+
</dl>
|
|
285
|
+
</>
|
|
286
|
+
);
|
|
287
|
+
return (
|
|
288
|
+
<div
|
|
289
|
+
{...attributes}
|
|
290
|
+
data-layout={checkout.layout}
|
|
291
|
+
data-fields={checkout.fields}
|
|
292
|
+
data-summary={checkout.summary}
|
|
293
|
+
style={style}
|
|
294
|
+
className="group/checkout flex min-h-screen flex-col bg-background text-foreground"
|
|
295
|
+
>
|
|
296
|
+
<header className="border-b border-input" data-scheme={checkout.main}>
|
|
297
|
+
<div className={`mx-auto flex h-16 w-full max-w-6xl items-center gap-4 px-4 lg:px-8 ${checkout.logo === "center" ? "justify-center" : "justify-between"}`}>
|
|
298
|
+
<Link to="/" className="flex items-center">
|
|
299
|
+
{shop.logo ? <img src={shop.logo} alt={shop.name} width={144} height={36} decoding="async" className="h-9 w-auto max-w-40 object-contain" /> : <span className="font-heading text-xl">{shop.name}</span>}
|
|
300
|
+
</Link>
|
|
301
|
+
{checkout.logo === "center" ? null : (
|
|
302
|
+
<div className="flex items-center gap-4 text-sm">
|
|
303
|
+
{checkout.secure ? (
|
|
304
|
+
<span className="flex items-center gap-1.5 text-muted-foreground">
|
|
305
|
+
<Lock />
|
|
306
|
+
{storefrontTranslate("Compra segura")}</span>
|
|
307
|
+
) : null}
|
|
308
|
+
<Link to="/cart" aria-label={storefrontTranslate("Volver al carrito")} className="text-foreground">
|
|
309
|
+
<Bag />
|
|
310
|
+
</Link>
|
|
311
|
+
</div>
|
|
312
|
+
)}
|
|
313
|
+
</div>
|
|
314
|
+
</header>
|
|
315
|
+
<details className="group/summary border-b border-input bg-muted/50 lg:hidden" data-scheme={checkout.aside}>
|
|
316
|
+
<summary className="flex cursor-pointer list-none items-center justify-between px-4 py-4 text-sm">
|
|
317
|
+
<span className="flex items-center gap-1.5 font-medium">
|
|
318
|
+
<span className="group-open/summary:hidden">{storefrontTranslate("Mostrar resumen del pedido")}</span>
|
|
319
|
+
<span className="hidden group-open/summary:inline">{storefrontTranslate("Ocultar resumen del pedido")}</span>
|
|
320
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" className="size-4 transition-transform group-open/summary:rotate-180" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
321
|
+
<path d="m6 9 6 6 6-6" />
|
|
322
|
+
</svg>
|
|
323
|
+
</span>
|
|
324
|
+
<span className="text-base font-semibold tabular-nums">{format(total)}</span>
|
|
325
|
+
</summary>
|
|
326
|
+
<div className="flex flex-col gap-5 px-4 pb-6">
|
|
327
|
+
{lines}
|
|
328
|
+
{totals}
|
|
329
|
+
</div>
|
|
330
|
+
</details>
|
|
331
|
+
<div className="mx-auto grid w-full max-w-6xl flex-1 grid-cols-1 lg:grid-cols-[minmax(0,1fr)_minmax(0,26rem)] group-data-[summary=left]/checkout:lg:grid-cols-[minmax(0,26rem)_minmax(0,1fr)]">
|
|
332
|
+
<main className="flex flex-col gap-8 px-4 py-8 lg:px-10 lg:py-10 group-data-[summary=left]/checkout:lg:order-2" data-scheme={checkout.main}>
|
|
333
|
+
{single ? null : (
|
|
334
|
+
<nav aria-label={storefrontTranslate("Pasos")} className="flex flex-wrap items-center gap-2 text-xs">
|
|
335
|
+
<Link to="/cart" className="underline-offset-4 hover:underline">
|
|
336
|
+
{storefrontTranslate("Carrito")}</Link>
|
|
337
|
+
{STEPS.filter((item) => needsDelivery || (item.step !== "delivery" && item.step !== "shipping")).map((item) => (
|
|
338
|
+
<span key={item.step} className="flex items-center gap-2">
|
|
339
|
+
<span aria-hidden="true" className="text-muted-foreground">
|
|
340
|
+
›
|
|
341
|
+
</span>
|
|
342
|
+
<span className={item.step === step ? "font-semibold" : "text-muted-foreground"} aria-current={item.step === step ? "step" : undefined}>
|
|
343
|
+
{storefrontTranslate(item.label)}
|
|
344
|
+
</span>
|
|
345
|
+
</span>
|
|
346
|
+
))}
|
|
347
|
+
</nav>
|
|
348
|
+
)}
|
|
349
|
+
{alert ? (
|
|
350
|
+
<p role="alert" className="rounded-md bg-destructive/10 px-4 py-3 text-sm text-destructive">
|
|
351
|
+
{storefrontTranslate(alert)}
|
|
352
|
+
</p>
|
|
353
|
+
) : null}
|
|
354
|
+
<Review rows={rows} />
|
|
355
|
+
{open("contact") ? (
|
|
356
|
+
<section>
|
|
357
|
+
<Heading
|
|
358
|
+
aside={
|
|
359
|
+
signedIn ? null : (
|
|
360
|
+
<Link to="/account/login?next=/checkout" className="text-sm underline underline-offset-4">
|
|
361
|
+
{storefrontTranslate("Iniciar sesión")}</Link>
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
>
|
|
365
|
+
{storefrontTranslate("Contacto")}</Heading>
|
|
366
|
+
<CheckoutForm single={single} intent="contact" className="grid gap-3 sm:grid-cols-2">
|
|
367
|
+
<Field label={storefrontTranslate("Correo")} name="email" type="email" autoComplete="email" defaultValue={cart.email ?? me?.email ?? ""} required className="sm:col-span-2" />
|
|
368
|
+
<Field label={storefrontTranslate("Nombre")} name="firstName" autoComplete="given-name" defaultValue={cart.firstName ?? me?.firstName ?? ""} required />
|
|
369
|
+
<Field label={storefrontTranslate("Apellidos")} name="lastName" autoComplete="family-name" defaultValue={cart.lastName ?? me?.lastName ?? ""} required />
|
|
370
|
+
<Field label={storefrontTranslate("Celular")} name="phone" type="tel" autoComplete="tel" defaultValue={cart.phone ?? me?.phone ?? ""} required className="sm:col-span-2" />
|
|
371
|
+
<InvoiceFields cart={cart}>
|
|
372
|
+
{(invoice) => (
|
|
373
|
+
<div className="flex flex-col gap-3 sm:col-span-2">
|
|
374
|
+
<div role="radiogroup" aria-label={storefrontTranslate("Comprobante")} className={`${GROUP} grid grid-cols-2 divide-x divide-input`}>
|
|
375
|
+
{invoice.options.map((option) => (
|
|
376
|
+
<label key={option.value} className={ROW}>
|
|
377
|
+
<input type="radio" name="invoiceType" value={option.value} checked={invoice.type === option.value} onChange={() => invoice.setType(option.value)} className="accent-primary" />
|
|
378
|
+
{storefrontTranslate(option.label)}
|
|
379
|
+
</label>
|
|
380
|
+
))}
|
|
381
|
+
</div>
|
|
382
|
+
<Choice label={storefrontTranslate("País emisor del documento")} {...invoice.documentCountry}>
|
|
383
|
+
<option value="">{storefrontTranslate("Elige…")}</option>
|
|
384
|
+
{data.documentCountries.map((country) => <option key={country.code} value={country.code}>{country.name}</option>)}
|
|
385
|
+
</Choice>
|
|
386
|
+
{invoice.type === "invoice" ? (
|
|
387
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
388
|
+
<Field label={storefrontTranslate("Identificación fiscal")} {...invoice.taxId} />
|
|
389
|
+
<Field label={storefrontTranslate("Razón social")} {...invoice.entityName} />
|
|
390
|
+
{invoice.taxIdError ? <p className="text-sm text-destructive sm:col-span-2">{storefrontTranslate(invoice.taxIdError)}</p> : null}
|
|
391
|
+
</div>
|
|
392
|
+
) : (
|
|
393
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
394
|
+
<Choice label={storefrontTranslate("Tipo de documento")} {...invoice.documentType}>
|
|
395
|
+
{invoice.documentTypes.map((option) => (
|
|
396
|
+
<option key={option.value} value={option.value}>
|
|
397
|
+
{storefrontTranslate(option.label)}
|
|
398
|
+
</option>
|
|
399
|
+
))}
|
|
400
|
+
</Choice>
|
|
401
|
+
<Field label={storefrontTranslate(invoice.documentLabel)} {...invoice.document} />
|
|
402
|
+
{invoice.documentError ? <p className="text-sm text-destructive sm:col-span-2">{storefrontTranslate(invoice.documentError)}</p> : null}
|
|
403
|
+
</div>
|
|
404
|
+
)}
|
|
405
|
+
</div>
|
|
406
|
+
)}
|
|
407
|
+
</InvoiceFields>
|
|
408
|
+
{single ? null : (
|
|
409
|
+
<Button type="submit" size="lg" disabled={busy} className="mt-2 sm:col-span-2 sm:w-fit">
|
|
410
|
+
{needsDelivery ? storefrontTranslate("Continuar con la entrega") : storefrontTranslate("Continuar con el pago")}
|
|
411
|
+
</Button>
|
|
412
|
+
)}
|
|
413
|
+
</CheckoutForm>
|
|
414
|
+
</section>
|
|
415
|
+
) : null}
|
|
416
|
+
{needsDelivery && open("delivery") ? (
|
|
417
|
+
<section>
|
|
418
|
+
<Heading>{storefrontTranslate("Entrega")}</Heading>
|
|
419
|
+
<CheckoutForm single={single} intent="delivery" className="flex flex-col gap-3">
|
|
420
|
+
<Address countries={countries} defaults={defaults} />
|
|
421
|
+
{single ? null : (
|
|
422
|
+
<Button type="submit" size="lg" disabled={busy} className="mt-2 sm:w-fit">
|
|
423
|
+
{storefrontTranslate("Continuar con el envío")}</Button>
|
|
424
|
+
)}
|
|
425
|
+
</CheckoutForm>
|
|
426
|
+
</section>
|
|
427
|
+
) : null}
|
|
428
|
+
{needsDelivery && open("shipping") ? (
|
|
429
|
+
<section>
|
|
430
|
+
<Heading>{storefrontTranslate("Método de envío")}</Heading>
|
|
431
|
+
{shipping.length === 0 ? (
|
|
432
|
+
<p className="rounded-md bg-muted/60 px-4 py-3 text-sm text-muted-foreground">
|
|
433
|
+
{done.delivery ? storefrontTranslate("No hay métodos de envío para esta dirección. Prueba con otra dirección.") : storefrontTranslate("Completa la dirección para ver las opciones de envío.")}
|
|
434
|
+
</p>
|
|
435
|
+
) : (
|
|
436
|
+
<CheckoutForm single={single} intent="shipping" className="flex flex-col gap-4">
|
|
437
|
+
{shipping.map((group, index) => (
|
|
438
|
+
<fieldset key={`${group.sellerId ?? "store"}-${index}`} className="flex flex-col gap-2">
|
|
439
|
+
{shipping.length > 1 ? <legend className="mb-2 text-sm font-medium">{group.sellerName ? `${storefrontTranslate("Paquete de")} ${group.sellerName}` : `${storefrontTranslate("Paquete")} ${index + 1}`}</legend> : null}
|
|
440
|
+
<div className={`${GROUP} divide-y divide-input`}>
|
|
441
|
+
{group.rates.map((rate, position) => (
|
|
442
|
+
<label key={rate.value} className={ROW}>
|
|
443
|
+
<input type="radio" name={group.field} value={rate.value} defaultChecked={position === 0} required className="accent-primary" />
|
|
444
|
+
<span className="flex flex-1 flex-col">
|
|
445
|
+
<span>{rate.displayName}</span>
|
|
446
|
+
{rate.estimatedDeliveryDays != null ? (
|
|
447
|
+
<span className="text-xs text-muted-foreground">
|
|
448
|
+
{storefrontTranslate("Llega en")} {rate.estimatedDeliveryDays} {rate.estimatedDeliveryDays === 1 ? storefrontTranslate("día") : storefrontTranslate("días")}
|
|
449
|
+
</span>
|
|
450
|
+
) : null}
|
|
451
|
+
{rate.estimatedDeliveryDays == null && rate.description ? <span className="text-xs text-muted-foreground">{rate.description}</span> : null}
|
|
452
|
+
</span>
|
|
453
|
+
<span className="font-medium tabular-nums">{rate.price === 0 ? storefrontTranslate("Gratis") : format(rate.price)}</span>
|
|
454
|
+
</label>
|
|
455
|
+
))}
|
|
456
|
+
</div>
|
|
457
|
+
</fieldset>
|
|
458
|
+
))}
|
|
459
|
+
{single ? null : (
|
|
460
|
+
<Button type="submit" size="lg" disabled={busy} className="mt-2 sm:w-fit">
|
|
461
|
+
{storefrontTranslate("Continuar con el pago")}</Button>
|
|
462
|
+
)}
|
|
463
|
+
</CheckoutForm>
|
|
464
|
+
)}
|
|
465
|
+
</section>
|
|
466
|
+
) : null}
|
|
467
|
+
{open("payment") ? (
|
|
468
|
+
<section>
|
|
469
|
+
<Heading>{storefrontTranslate("Pago")}</Heading>
|
|
470
|
+
{checkout.encrypted ? <p className="-mt-2 mb-3 text-sm text-muted-foreground">{storefrontTranslate("Todas las transacciones son seguras y están cifradas.")}</p> : null}
|
|
471
|
+
{bumps.length > 0 ? (
|
|
472
|
+
<div className="mb-4 flex flex-col gap-2">
|
|
473
|
+
{bumps.map((bump) => (
|
|
474
|
+
<OrderBumpForm key={bump.id} bump={bump} className={`${GROUP} flex items-center gap-3 p-3`}>
|
|
475
|
+
<span className="size-12 shrink-0 overflow-hidden rounded-md bg-muted">
|
|
476
|
+
{bump.imageUrl ? <img src={bump.imageUrl} alt="" width={48} height={48} loading="lazy" decoding="async" className="size-full object-cover" /> : null}
|
|
477
|
+
</span>
|
|
478
|
+
<span className="flex flex-1 flex-col text-sm">
|
|
479
|
+
<span className="font-medium">{bump.productName}</span>
|
|
480
|
+
<span className="text-xs text-muted-foreground">{format(bump.offerPrice)}</span>
|
|
481
|
+
</span>
|
|
482
|
+
<Button type="submit" size="sm" disabled={busy}>
|
|
483
|
+
{bump.callToAction || storefrontTranslate("Agregar")}
|
|
484
|
+
</Button>
|
|
485
|
+
</OrderBumpForm>
|
|
486
|
+
))}
|
|
487
|
+
</div>
|
|
488
|
+
) : null}
|
|
489
|
+
{methods.length === 0 ? (
|
|
490
|
+
<p className="rounded-md bg-muted/60 px-4 py-3 text-sm text-muted-foreground">{storefrontTranslate("La tienda todavía no tiene medios de pago activos.")}</p>
|
|
491
|
+
) : (
|
|
492
|
+
<CheckoutForm single={single} intent="pay" onSubmit={payment.onSubmit} className="flex flex-col gap-5">
|
|
493
|
+
<input ref={payment.token} type="hidden" name="token" defaultValue="" />
|
|
494
|
+
<input ref={payment.deviceSessionId} type="hidden" name="deviceSessionId" defaultValue="" />
|
|
495
|
+
<input ref={payment.providerPaymentMethodId} type="hidden" name="providerPaymentMethodId" defaultValue="" />
|
|
496
|
+
<div role="radiogroup" aria-label={storefrontTranslate("Medio de pago")} className={`${GROUP} divide-y divide-input`}>
|
|
497
|
+
<PaymentMethods methods={offeredMethods} cart={cart} language={`${data.language}-${data.countryCode}`}>
|
|
498
|
+
{({ method, logos, saving, identification, manual, receipt, charge }) => (
|
|
499
|
+
<div className="group/method">
|
|
500
|
+
<label className={ROW}>
|
|
501
|
+
<input
|
|
502
|
+
type="radio"
|
|
503
|
+
name="method"
|
|
504
|
+
value={method.code}
|
|
505
|
+
defaultChecked={method.code === paymentMethod}
|
|
506
|
+
onChange={() => choosePaymentMethod(method.code)}
|
|
507
|
+
required
|
|
508
|
+
className="accent-primary"
|
|
509
|
+
/>
|
|
510
|
+
<span className="flex min-w-0 flex-1 flex-col">
|
|
511
|
+
<span>{method.name}</span>
|
|
512
|
+
{method.description ? <span className="hidden text-xs text-muted-foreground group-has-[:checked]/method:block">{method.description}</span> : null}
|
|
513
|
+
{saving ? <span className="text-xs text-muted-foreground">{saving.label}</span> : null}
|
|
514
|
+
{charge ? (
|
|
515
|
+
<span className="hidden text-xs text-muted-foreground group-has-[:checked]/method:block">
|
|
516
|
+
{storefrontTranslate("Se te cobrará {value0} con un tipo de cambio de {value1} por {value2}.", {
|
|
517
|
+
value0: charge.amount,
|
|
518
|
+
value1: charge.rate,
|
|
519
|
+
value2: charge.unit,
|
|
520
|
+
})}
|
|
521
|
+
</span>
|
|
522
|
+
) : null}
|
|
523
|
+
</span>
|
|
524
|
+
{checkout.marks ? (
|
|
525
|
+
<span className="flex flex-wrap justify-end gap-1 max-sm:[&>img:nth-of-type(n+4)]:hidden">
|
|
526
|
+
{logos.map((code) => (
|
|
527
|
+
<PaymentLogo key={code} code={code} className="h-5 w-auto" />
|
|
528
|
+
))}
|
|
529
|
+
{logos.length > MOBILE_LOGOS ? (
|
|
530
|
+
<span className="rounded border border-input px-1 text-xs leading-[1.125rem] text-muted-foreground sm:hidden">+{logos.length - MOBILE_LOGOS}</span>
|
|
531
|
+
) : null}
|
|
532
|
+
</span>
|
|
533
|
+
) : null}
|
|
534
|
+
</label>
|
|
535
|
+
{identification || manual || receipt ? (
|
|
536
|
+
<div className="hidden flex-col gap-3 bg-muted/40 px-4 py-3 text-sm group-has-[:checked]/method:flex">
|
|
537
|
+
{identification ? <Field label="DNI" {...identification} /> : null}
|
|
538
|
+
{manual?.qrImageUrl ? (
|
|
539
|
+
<img src={manual.qrImageUrl} alt={storefrontTranslate("QR de {value0}", { value0: method.name })} width={160} height={160} loading="lazy" decoding="async" className="size-40 rounded-md border border-input bg-background object-contain" />
|
|
540
|
+
) : null}
|
|
541
|
+
{manual?.phoneNumber ? (
|
|
542
|
+
<span>
|
|
543
|
+
{storefrontTranslate("Número:")} <span className="font-medium">{manual.phoneNumber}</span>
|
|
544
|
+
</span>
|
|
545
|
+
) : null}
|
|
546
|
+
{manual?.accountHolder ? (
|
|
547
|
+
<span>
|
|
548
|
+
{storefrontTranslate("Titular:")} <span className="font-medium">{manual.accountHolder}</span>
|
|
549
|
+
</span>
|
|
550
|
+
) : null}
|
|
551
|
+
{manual ? <BankAccountList accounts={manual.bankAccounts} /> : null}
|
|
552
|
+
{receipt ? (
|
|
553
|
+
<span className="flex flex-col gap-2">
|
|
554
|
+
<span className="text-xs text-muted-foreground">{storefrontTranslate("Sube la captura de tu pago (opcional).")}</span>
|
|
555
|
+
<input {...receipt} aria-label={storefrontTranslate("Captura del pago")} className="text-xs file:mr-3 file:rounded-md file:border-0 file:bg-muted file:px-3 file:py-1.5 file:text-xs file:font-medium" />
|
|
556
|
+
</span>
|
|
557
|
+
) : null}
|
|
558
|
+
</div>
|
|
559
|
+
) : null}
|
|
560
|
+
</div>
|
|
561
|
+
)}
|
|
562
|
+
</PaymentMethods>
|
|
563
|
+
</div>
|
|
564
|
+
<Button type="submit" size="lg" disabled={paying} className={checkout.button === "fit" ? "h-12 w-fit text-base" : "h-12 w-full text-base"}>
|
|
565
|
+
{paying ? storefrontTranslate("Procesando…") : `${storefrontTranslate("Pagar ahora")} · ${format(cart.totals.total)}`}
|
|
566
|
+
</Button>
|
|
567
|
+
</CheckoutForm>
|
|
568
|
+
)}
|
|
569
|
+
</section>
|
|
570
|
+
) : null}
|
|
571
|
+
<MercadoPagoDialog dialog={payment.dialog} />
|
|
572
|
+
<StripeDialog dialog={payment.stripeDialog} />
|
|
573
|
+
</main>
|
|
574
|
+
<aside
|
|
575
|
+
className="hidden border-input bg-muted/50 px-10 py-10 lg:block lg:border-l group-data-[summary=left]/checkout:lg:order-1 group-data-[summary=left]/checkout:lg:border-r group-data-[summary=left]/checkout:lg:border-l-0"
|
|
576
|
+
data-scheme={checkout.aside}
|
|
577
|
+
>
|
|
578
|
+
<div className="sticky top-8 flex flex-col gap-6">
|
|
579
|
+
{lines}
|
|
580
|
+
{totals}
|
|
581
|
+
</div>
|
|
582
|
+
</aside>
|
|
583
|
+
</div>
|
|
584
|
+
</div>
|
|
585
|
+
);
|
|
586
|
+
}
|