@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.
Files changed (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -76
  3. package/dist/account-Dx4e3syi.js +258 -0
  4. package/dist/account-forms-AVk71ko7.js +39 -0
  5. package/dist/account-forms.d.ts +13 -0
  6. package/dist/account.d.ts +240 -0
  7. package/dist/address-defaults-BqjRhS82.js +14 -0
  8. package/dist/address-defaults.d.ts +11 -0
  9. package/dist/analytics/analytics-CG1Qagwl.js +148 -0
  10. package/dist/analytics/analytics.d.ts +1 -0
  11. package/dist/analytics/config-DJr-GRhs.js +62 -0
  12. package/dist/analytics/config.d.ts +22 -0
  13. package/dist/analytics/consent-B13rBpBs.js +57 -0
  14. package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
  15. package/dist/analytics/consent-banner.d.ts +20 -0
  16. package/dist/analytics/cookies-pLozllyB.js +100 -0
  17. package/dist/analytics/cookies.d.ts +6 -0
  18. package/dist/analytics/destinations-iECrlLIr.js +144 -0
  19. package/dist/analytics/events-Dt81tIp1.js +102 -0
  20. package/dist/analytics/events.d.ts +51 -0
  21. package/dist/analytics/tracker-p4yaoOyk.js +51 -0
  22. package/dist/api-cache-DsuIn5hX.js +159 -0
  23. package/dist/bank-accounts-DlEczQQ5.js +81 -0
  24. package/dist/bank-accounts.d.ts +4 -0
  25. package/dist/captcha-DAqaIx8m.js +117 -0
  26. package/dist/captcha.d.ts +34 -0
  27. package/dist/cart-D7DxZ2lD.js +30 -0
  28. package/dist/cart-actions-Bsub_FFG.js +116 -0
  29. package/dist/cart-actions.d.ts +15 -0
  30. package/dist/cart-drawer-B7PPsR69.js +22 -0
  31. package/dist/cart-drawer.d.ts +9 -0
  32. package/dist/cart-form-y9P1r_EX.js +81 -0
  33. package/dist/cart-form.d.ts +24 -0
  34. package/dist/cart-summary-ur2VWKr-.js +89 -0
  35. package/dist/cart-summary.d.ts +31 -0
  36. package/dist/cart.d.ts +4 -0
  37. package/dist/catalog-7gJ-l5hW.js +67 -0
  38. package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
  39. package/dist/catalog-hooks.d.ts +111 -0
  40. package/dist/catalog.d.ts +5 -0
  41. package/dist/checkout-DVWSXANi.js +474 -0
  42. package/dist/checkout-form-CbOIdENC.js +131 -0
  43. package/dist/checkout-form.d.ts +20 -0
  44. package/dist/checkout.d.ts +86 -0
  45. package/dist/complaint-book-BvwLvfx0.js +117 -0
  46. package/dist/complaint-book.d.ts +62 -0
  47. package/dist/culqi-authentication-BGr20vAJ.js +204 -0
  48. package/dist/culqi-authentication.d.ts +15 -0
  49. package/dist/culqi-checkout-CTXpKgp_.js +55 -0
  50. package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
  51. package/dist/culqi-three-ds.d.ts +29 -0
  52. package/dist/customer-avatar-CJXSDroK.js +12 -0
  53. package/dist/customer-avatar.d.ts +5 -0
  54. package/dist/customer-cookies-DHOgFAMV.js +45 -0
  55. package/dist/customer-csrf-C1oDX-Py.js +17 -0
  56. package/dist/customer-csrf.d.ts +3 -0
  57. package/dist/customer-document-fields-C1K-_DAD.js +70 -0
  58. package/dist/customer-document-fields.d.ts +9 -0
  59. package/dist/customer-login-form-C7AC6vXd.js +144 -0
  60. package/dist/customer-login-form.d.ts +17 -0
  61. package/dist/customer-registration-form-BEm_NEfP.js +93 -0
  62. package/dist/customer-registration-form.d.ts +8 -0
  63. package/dist/document-url-CGsAl5Tf.js +9 -0
  64. package/dist/documents-T1mzc_j8.js +38 -0
  65. package/dist/documents.d.ts +16 -0
  66. package/dist/entries/account.d.ts +11 -0
  67. package/dist/entries/account.js +10 -0
  68. package/dist/entries/analytics.d.ts +6 -0
  69. package/dist/entries/analytics.js +5 -0
  70. package/dist/entries/cart.d.ts +8 -0
  71. package/dist/entries/cart.js +7 -0
  72. package/dist/entries/checkout.d.ts +12 -0
  73. package/dist/entries/checkout.js +11 -0
  74. package/dist/entries/order.d.ts +11 -0
  75. package/dist/entries/order.js +11 -0
  76. package/dist/favicon-YpyhVJ9H.js +28 -0
  77. package/dist/favicon.d.ts +4 -0
  78. package/dist/form-BtQcbXlJ.js +18 -0
  79. package/dist/form-contracts-BNpHaTGJ.js +166 -0
  80. package/dist/form-contracts.d.ts +42 -0
  81. package/dist/form.d.ts +3 -0
  82. package/dist/hidden-CCWtOWKR.js +3 -0
  83. package/dist/hidden.d.ts +4 -0
  84. package/dist/index.d.ts +50 -0
  85. package/dist/index.js +50 -0
  86. package/dist/invoice-fields-BG0HjL6T.js +103 -0
  87. package/dist/invoice-fields.d.ts +39 -0
  88. package/dist/links-kFxBZfdb.js +96 -0
  89. package/dist/links.d.ts +21 -0
  90. package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
  91. package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
  92. package/dist/mercadopago-dialog.d.ts +21 -0
  93. package/dist/money-DtGOtDri.js +32 -0
  94. package/dist/money.d.ts +1 -0
  95. package/dist/optimistic-cart-BveVOHuh.js +53 -0
  96. package/dist/optimistic-cart.d.ts +16 -0
  97. package/dist/order-display-DTt5WBbc.js +112 -0
  98. package/dist/order-display.d.ts +20 -0
  99. package/dist/order-draft-DxV1Uhm3.js +38 -0
  100. package/dist/order-draft.d.ts +16 -0
  101. package/dist/order-pages-Dm6l5m0m.js +38 -0
  102. package/dist/order-pages.d.ts +12 -0
  103. package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
  104. package/dist/order-payment-continuation.d.ts +10 -0
  105. package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
  106. package/dist/order-payment-in-review.d.ts +5 -0
  107. package/dist/order-status-BJXLJwxq.js +162 -0
  108. package/dist/order-status.d.ts +89 -0
  109. package/dist/order-view-9IlBg5AF.js +62 -0
  110. package/dist/order-view.d.ts +12 -0
  111. package/dist/page-action-Ctkf4xC6.js +7 -0
  112. package/dist/page-seo-a5FJl8x5.js +262 -0
  113. package/dist/page-seo.d.ts +34 -0
  114. package/dist/pages-Ca_Gt6mq.js +150 -0
  115. package/dist/pages.d.ts +45 -0
  116. package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
  117. package/dist/payment-logo-BoPaGJc_.js +46 -0
  118. package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
  119. package/dist/payment-logo.d.ts +11 -0
  120. package/dist/payment-logos-BYvdwYyJ.js +23 -0
  121. package/dist/payment-methods-B8ocTgFZ.js +73 -0
  122. package/dist/payment-methods.d.ts +58 -0
  123. package/dist/payment-return-5cjypwIw.js +7 -0
  124. package/dist/place-CmW4hBWY.js +13 -0
  125. package/dist/place.d.ts +4 -0
  126. package/dist/preview-BiIBif3C.js +427 -0
  127. package/dist/preview-address-Cf0sJ-9p.js +21 -0
  128. package/dist/preview-cart-Dg2CVktS.js +376 -0
  129. package/dist/product-preview-BspMAecJ.js +51 -0
  130. package/dist/product-preview.d.ts +5 -0
  131. package/dist/product-variants-BNDHvs1y.js +46 -0
  132. package/dist/product-variants.d.ts +12 -0
  133. package/dist/public-pages-XtwlaLwt.js +194 -0
  134. package/dist/public-pages.d.ts +67 -0
  135. package/dist/registration-CpDcv8ql.js +91 -0
  136. package/dist/registration.d.ts +12 -0
  137. package/dist/request-security-CRsRMgwj.js +76 -0
  138. package/dist/rich-text-M-Ju8RL4.js +9 -0
  139. package/dist/rich-text.d.ts +5 -0
  140. package/dist/route-text-DcJw2SsK.js +9 -0
  141. package/dist/route-text.d.ts +4 -0
  142. package/dist/section-preview-CmsMl3yu.js +83 -0
  143. package/dist/section-preview.d.ts +12 -0
  144. package/dist/sections/account-addresses.d.ts +18 -0
  145. package/dist/sections/account-addresses.js +375 -0
  146. package/dist/sections/account-order.d.ts +12 -0
  147. package/dist/sections/account-order.js +592 -0
  148. package/dist/sections/account-orders.d.ts +8 -0
  149. package/dist/sections/account-orders.js +359 -0
  150. package/dist/sections/account-profile.d.ts +16 -0
  151. package/dist/sections/account-profile.js +123 -0
  152. package/dist/sections/checkout.d.ts +24 -0
  153. package/dist/sections/checkout.js +963 -0
  154. package/dist/sections/complaint-book.d.ts +8 -0
  155. package/dist/sections/complaint-book.js +538 -0
  156. package/dist/sections/design-system.d.ts +1 -0
  157. package/dist/sections/design-system.js +310 -0
  158. package/dist/sections/draft-invoice.d.ts +20 -0
  159. package/dist/sections/draft-invoice.js +158 -0
  160. package/dist/sections/order-tracking.d.ts +19 -0
  161. package/dist/sections/order-tracking.js +141 -0
  162. package/dist/sections-CNUMWTF_.js +77 -0
  163. package/dist/sections.d.ts +19 -0
  164. package/dist/seo-GpDRhAAA.js +78 -0
  165. package/dist/session-CKvmFJyg.js +379 -0
  166. package/dist/session.d.ts +24 -0
  167. package/dist/settings-B0NO2A6Y.js +78 -0
  168. package/dist/settings.d.ts +51 -0
  169. package/dist/source-marker-DhqSkW7n.js +4 -0
  170. package/dist/sources-B-3JFJfb.js +46 -0
  171. package/dist/sources.d.ts +7 -0
  172. package/dist/store-8Q-_LFrQ.js +152 -0
  173. package/dist/store-contact--hY6CoFb.js +19 -0
  174. package/dist/store-contact.d.ts +6 -0
  175. package/dist/store.d.ts +17 -0
  176. package/dist/storefront-language-DrMjVbmk.js +29 -0
  177. package/dist/storefront-language.d.ts +14 -0
  178. package/dist/storefront-text-D25ae1qf.js +11 -0
  179. package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
  180. package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
  181. package/dist/storefront-text.d.ts +4 -0
  182. package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
  183. package/dist/stripe-dialog.d.ts +30 -0
  184. package/dist/telemetry-HuZXWBPi.js +114 -0
  185. package/dist/telemetry.d.ts +10 -0
  186. package/dist/templates-CAqoNyTW.js +40 -0
  187. package/dist/templates.d.ts +15 -0
  188. package/dist/urls-CpGQPxvf.js +71 -0
  189. package/dist/urls.d.ts +18 -0
  190. package/dist/use-checkout-CZcxXJXi.js +42 -0
  191. package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
  192. package/dist/use-checkout-payment.d.ts +18 -0
  193. package/dist/use-checkout.d.ts +36 -0
  194. package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
  195. package/dist/use-order-payment-BKIz_IO0.js +45 -0
  196. package/dist/use-order-payment.d.ts +18 -0
  197. package/dist/use-payment-token-CErx-tN-.js +105 -0
  198. package/dist/vite-payment-logos-kziu7wKb.js +27 -0
  199. package/dist/vite-payment-logos.d.ts +24 -0
  200. package/dist/vite-public-assets-DW2fgtVb.js +209 -0
  201. package/dist/vite-public-assets.d.ts +29 -0
  202. package/dist/vite-sections-DTBr0-Kg.js +464 -0
  203. package/dist/vite-sections.d.ts +21 -0
  204. package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
  205. package/dist/vite-theme-ui.d.ts +10 -0
  206. package/dist/vite.d.ts +36 -0
  207. package/dist/vite.js +127 -0
  208. package/dist/voucher-payment-CVFDZ1L8.js +66 -0
  209. package/dist/worker-CNi-gzCu.js +86 -0
  210. package/dist/worker.d.ts +27 -0
  211. package/package.json +100 -76
  212. package/src/sections/account-addresses.tsx +215 -0
  213. package/src/sections/account-order.tsx +424 -0
  214. package/src/sections/account-orders.tsx +259 -0
  215. package/src/sections/account-profile.tsx +70 -0
  216. package/src/sections/checkout.tsx +586 -0
  217. package/src/sections/complaint-book.tsx +325 -0
  218. package/src/sections/design-system.tsx +146 -0
  219. package/src/sections/draft-invoice.tsx +105 -0
  220. package/src/sections/order-tracking.tsx +81 -0
  221. package/src/vite-client.d.ts +4 -0
  222. package/bin/ecomiq.mjs +0 -179
  223. package/public/design-bridge.js +0 -318
  224. package/src/api-proxy.ts +0 -194
  225. package/src/auth/create-storefront-auth.ts +0 -94
  226. package/src/auth/ecomiq-oauth-password-client.ts +0 -26
  227. package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
  228. package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
  229. package/src/auth/ecomiq-oauth-password-types.ts +0 -24
  230. package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
  231. package/src/auth/env.ts +0 -7
  232. package/src/auth/secure-cookies.ts +0 -23
  233. package/src/auth/storefront-auth-base-url.ts +0 -117
  234. package/src/auth/storefront-auth.ts +0 -7
  235. package/src/cart-session.ts +0 -239
  236. package/src/client.ts +0 -48
  237. package/src/codegen.mjs +0 -801
  238. package/src/components/account/customer-addresses-panel.tsx +0 -678
  239. package/src/components/account/customer-order-detail-panel.tsx +0 -285
  240. package/src/components/account/customer-orders-panel.tsx +0 -192
  241. package/src/components/account/customer-profile-panel.tsx +0 -379
  242. package/src/components/checkout/cart-sync.ts +0 -143
  243. package/src/components/checkout/checkout-contact-section.tsx +0 -76
  244. package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
  245. package/src/components/checkout/checkout-form-controls.tsx +0 -139
  246. package/src/components/checkout/checkout-line-items.tsx +0 -43
  247. package/src/components/checkout/checkout-order-summary.tsx +0 -165
  248. package/src/components/checkout/checkout-payment-section.tsx +0 -212
  249. package/src/components/checkout/checkout-session-view.tsx +0 -564
  250. package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
  251. package/src/components/checkout/checkout-shipping-section.tsx +0 -98
  252. package/src/components/checkout/checkout-states.tsx +0 -73
  253. package/src/components/checkout/checkout-step-card.tsx +0 -101
  254. package/src/components/checkout/checkout-steps.ts +0 -111
  255. package/src/components/checkout/form-schema.ts +0 -281
  256. package/src/components/checkout/format.ts +0 -15
  257. package/src/components/checkout/index.ts +0 -5
  258. package/src/components/checkout/map-customer-to-checkout.ts +0 -188
  259. package/src/components/checkout/types.ts +0 -41
  260. package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
  261. package/src/components/checkout/use-checkout-geo.ts +0 -50
  262. package/src/components/checkout/use-checkout-session.ts +0 -640
  263. package/src/components/hero.tsx +0 -291
  264. package/src/config.d.ts +0 -94
  265. package/src/config.mjs +0 -146
  266. package/src/format-money.ts +0 -31
  267. package/src/health.ts +0 -90
  268. package/src/hooks/index.ts +0 -9
  269. package/src/hooks/recently-viewed.ts +0 -125
  270. package/src/index.ts +0 -349
  271. package/src/link.tsx +0 -2
  272. package/src/pages/account/account-page-skeleton.tsx +0 -69
  273. package/src/pages/account/account-page-view.tsx +0 -270
  274. package/src/pages/account/account-shell.tsx +0 -101
  275. package/src/pages/account/parse-account-route.ts +0 -36
  276. package/src/pages/account.tsx +0 -16
  277. package/src/pages/api.$.ts +0 -20
  278. package/src/pages/api.auth.$.ts +0 -16
  279. package/src/pages/checkout.$checkoutId.tsx +0 -60
  280. package/src/pages/checkout.tsx +0 -31
  281. package/src/pages/pages.$pageSlug.tsx +0 -74
  282. package/src/pages/thank-you.tsx +0 -53
  283. package/src/providers/auth.tsx +0 -96
  284. package/src/providers/cart.tsx +0 -275
  285. package/src/providers/ecomiq.ts +0 -137
  286. package/src/providers/index.ts +0 -26
  287. package/src/proxy-api.ts +0 -20
  288. package/src/public-config.ts +0 -24
  289. package/src/review-submission.ts +0 -89
  290. package/src/runtime/context.ts +0 -2
  291. package/src/runtime/default-layout.tsx +0 -64
  292. package/src/runtime/head.ts +0 -149
  293. package/src/runtime/page.tsx +0 -143
  294. package/src/runtime/postcss-browser-stub.mjs +0 -5
  295. package/src/server-config-store.ts +0 -35
  296. package/src/server.ts +0 -286
  297. package/src/styles.css +0 -76
  298. package/src/ui/badge.tsx +0 -32
  299. package/src/ui/button.tsx +0 -31
  300. package/src/ui/card.tsx +0 -31
  301. package/src/ui/carousel.tsx +0 -218
  302. package/src/ui/cn.ts +0 -6
  303. package/src/ui/index.ts +0 -8
  304. package/src/ui/separator.tsx +0 -6
  305. package/src/ui/skeleton.tsx +0 -6
  306. package/src/ui/storefront-error.tsx +0 -49
  307. package/src/utils.ts +0 -36
  308. package/src/vite-inline.mjs +0 -263
  309. package/templates/__root.tsx +0 -83
  310. package/templates/router.tsx +0 -19
@@ -1,564 +0,0 @@
1
- import { useEffect, useState, type FormEvent } from 'react'
2
- import { ArrowLeft, CreditCard, Lock, Truck, User } from 'lucide-react'
3
- import type { CartCheckoutResponse } from '@ecomiq/core'
4
- import { Link } from '../../link'
5
- import { useAuth, useSettings } from '../../providers'
6
- import { buttonVariants } from '../../ui'
7
- import { CheckoutContactSection } from './checkout-contact-section'
8
- import { CheckoutExternalUpdateBanner } from './checkout-external-update-banner'
9
- import { CheckoutOrderBumps } from './checkout-line-items'
10
- import { CheckoutOrderSummary } from './checkout-order-summary'
11
- import { CheckoutPaymentSection } from './checkout-payment-section'
12
- import { CheckoutShippingAddressSection } from './checkout-shipping-address-section'
13
- import {
14
- CheckoutStepCard,
15
- CheckoutStepSummaryBlock,
16
- } from './checkout-step-card'
17
- import {
18
- completeCheckoutStep,
19
- createCheckoutStepFlow,
20
- editCheckoutStep,
21
- type CheckoutStepFlow,
22
- type CheckoutStepId,
23
- } from './checkout-steps'
24
- import {
25
- CheckoutAlreadyPurchasedState,
26
- CheckoutEmptyState,
27
- CheckoutErrorState,
28
- CheckoutLoadingState,
29
- } from './checkout-states'
30
- import { formatMoney, paymentRedirectUrl, thankYouUrl } from './format'
31
- import {
32
- buildCheckoutContactStepSchema,
33
- buildCheckoutDeliveryStepSchema,
34
- buildCheckoutFormValidationSchema,
35
- buildCheckoutPaymentStepSchema,
36
- checkoutStepForField,
37
- checkoutValidationInputFromContext,
38
- firstInvalidFieldId,
39
- focusCheckoutField,
40
- mapZodIssuesToFieldErrors,
41
- type CheckoutFieldId,
42
- } from './form-schema'
43
- import type { CheckoutContactForm } from './types'
44
- import { useCheckoutGeo } from './use-checkout-geo'
45
- import { useCheckoutCustomerPrefill } from './use-checkout-customer-prefill'
46
- import { useCheckoutSession } from './use-checkout-session'
47
-
48
- export function CheckoutSessionView({ cartToken }: { cartToken: string }) {
49
- const { store } = useSettings()
50
- const { session } = useAuth()
51
- const isAuthenticated = Boolean(session?.user)
52
- const checkout = useCheckoutSession(cartToken)
53
- const [stepFlow, setStepFlow] = useState<CheckoutStepFlow>(() => createCheckoutStepFlow())
54
- const [fieldErrors, setFieldErrors] = useState<
55
- Partial<Record<CheckoutFieldId, string>>
56
- >({})
57
- const [receiptFile, setReceiptFile] = useState<File | null>(null)
58
- const [receiptUrl, setReceiptUrl] = useState<string | null>(null)
59
- const [receiptError, setReceiptError] = useState<string | null>(null)
60
- const [acceptedAge, setAcceptedAge] = useState(false)
61
- const [acceptedPrivacy, setAcceptedPrivacy] = useState(false)
62
- const [consentError, setConsentError] = useState<string | null>(null)
63
- const [addressSaveError, setAddressSaveError] = useState<string | null>(null)
64
- const [saveToAccount, setSaveToAccount] = useState(false)
65
- const [saveAddressKey, setSaveAddressKey] = useState('Casa')
66
-
67
- const geo = useCheckoutGeo(
68
- checkout.contact.countryCode,
69
- checkout.contact.departmentCode,
70
- checkout.contact.provinceCode,
71
- )
72
-
73
- const customerPrefill = useCheckoutCustomerPrefill({
74
- enabled: isAuthenticated && checkout.loadStatus === 'ready',
75
- cartToken,
76
- currency: store.currency,
77
- contact: checkout.contact,
78
- setContact: checkout.setContact,
79
- departments: geo.departments,
80
- provinces: geo.provinces,
81
- districts: geo.districts,
82
- selectedShippingKey: checkout.selectedShippingKey,
83
- shippingFlat: checkout.shippingFlat,
84
- shippingLoading: checkout.shippingLoading,
85
- stepFlow,
86
- setStepFlow,
87
- persistDeliveryStep: checkout.persistDeliveryStep,
88
- invalidateDeliveryPersistence: checkout.invalidateDeliveryPersistence,
89
- saveAddressIntent:
90
- isAuthenticated && saveToAccount
91
- ? { enabled: true, addressKey: saveAddressKey.trim() || 'Casa' }
92
- : null,
93
- onAddressSaveError: (message) => setAddressSaveError(message),
94
- onAddressSaved: () => {
95
- setSaveToAccount(false)
96
- setAddressSaveError(null)
97
- },
98
- associateBuyer: () => checkout.saveBuyer().then(() => undefined),
99
- })
100
-
101
- const { ensureDefaultPaymentMethod } = checkout
102
-
103
- useEffect(() => {
104
- if (stepFlow.active !== 'payment') return
105
- ensureDefaultPaymentMethod()
106
- }, [ensureDefaultPaymentMethod, stepFlow.active])
107
-
108
- if (checkout.loadStatus === 'loading' || checkout.isReconciling) {
109
- return <CheckoutLoadingState />
110
- }
111
-
112
- if (checkout.checkoutAlreadyPurchased) {
113
- return <CheckoutAlreadyPurchasedState />
114
- }
115
-
116
- if (checkout.loadStatus === 'error') {
117
- return (
118
- <CheckoutErrorState
119
- description={checkout.loadError}
120
- onRetry={() => checkout.reloadCheckout()}
121
- />
122
- )
123
- }
124
-
125
- const cart = checkout.uiCart
126
- if (!cart || cart.items.length === 0) {
127
- return <CheckoutEmptyState />
128
- }
129
-
130
- const money = (value: number) => formatMoney(value, store.language, cart.currencyCode)
131
- const selectedMethod =
132
- checkout.paymentMethods.find((m) => m.code === checkout.selectedPaymentCode) ?? null
133
- const isManual = Boolean(selectedMethod?.isManual)
134
- const selectedShipping = checkout.shippingFlat.find(
135
- (o) => o.key === checkout.selectedShippingKey,
136
- )
137
-
138
- const clearFieldError = (id: CheckoutFieldId) => {
139
- setFieldErrors((prev) => {
140
- if (!prev[id]) return prev
141
- const next = { ...prev }
142
- delete next[id]
143
- return next
144
- })
145
- }
146
-
147
- const patchContact = (patch: Partial<CheckoutContactForm>) => {
148
- checkout.setContact((c) => ({ ...c, ...patch }))
149
- }
150
-
151
- const openStep = (step: CheckoutStepId) => {
152
- setStepFlow((flow) => editCheckoutStep(flow, step))
153
- checkout.invalidateDeliveryPersistence()
154
- setFieldErrors({})
155
- setConsentError(null)
156
- }
157
-
158
- const validationContext = () =>
159
- checkoutValidationInputFromContext({
160
- contact: checkout.contact,
161
- selectedShippingKey: checkout.selectedShippingKey,
162
- shippingOptionsCount: checkout.shippingFlat.length,
163
- selectedPaymentCode: checkout.selectedPaymentCode,
164
- paymentMethodsCount: checkout.paymentMethods.length,
165
- })
166
-
167
- const handleContinueContact = () => {
168
- const parsed = buildCheckoutContactStepSchema().safeParse({
169
- email: checkout.contact.email,
170
- })
171
- if (!parsed.success) {
172
- const errors = mapZodIssuesToFieldErrors(parsed.error)
173
- setFieldErrors(errors)
174
- const first = firstInvalidFieldId(errors)
175
- if (first) focusCheckoutField(first)
176
- return
177
- }
178
- setFieldErrors({})
179
-
180
- const { email, firstName, lastName, phone } = checkout.contact
181
- const canAssociateBuyer =
182
- isAuthenticated &&
183
- Boolean(email.trim() && firstName.trim() && lastName.trim() && phone.trim())
184
-
185
- void (async () => {
186
- if (canAssociateBuyer) {
187
- try {
188
- await checkout.saveBuyer()
189
- } catch {
190
- // El avance del paso no depende de asociar el buyer; se reintenta en delivery.
191
- }
192
- }
193
- setStepFlow((flow) => completeCheckoutStep(flow, 'contact'))
194
- })()
195
- }
196
-
197
- const handleContinueDelivery = async (options?: {
198
- saveToAccount?: boolean
199
- addressKey?: string
200
- }) => {
201
- const parsed = buildCheckoutDeliveryStepSchema(store.currency).safeParse(validationContext())
202
- if (!parsed.success) {
203
- const errors = mapZodIssuesToFieldErrors(parsed.error)
204
- setFieldErrors(errors)
205
- const first = firstInvalidFieldId(errors)
206
- if (first) focusCheckoutField(first)
207
- return
208
- }
209
- setFieldErrors({})
210
- setAddressSaveError(null)
211
- const shouldSave = Boolean(options?.saveToAccount ?? saveToAccount) && isAuthenticated
212
- const key = (options?.addressKey ?? saveAddressKey).trim() || 'Casa'
213
- try {
214
- await checkout.persistDeliveryStep()
215
- if (shouldSave) {
216
- try {
217
- await customerPrefill.saveContactAsCustomerAddress(checkout.contact, {
218
- addressKey: key,
219
- })
220
- setSaveToAccount(false)
221
- } catch (error) {
222
- setAddressSaveError(
223
- error instanceof Error
224
- ? error.message
225
- : 'No se pudo guardar la dirección en tu cuenta.',
226
- )
227
- return
228
- }
229
- }
230
- setStepFlow((flow) => completeCheckoutStep(flow, 'delivery'))
231
- } catch {
232
- /* formError en sesión */
233
- }
234
- }
235
-
236
- const handlePay = async (event: FormEvent) => {
237
- event.preventDefault()
238
- if (stepFlow.active !== 'payment') return
239
-
240
- if (!acceptedAge || !acceptedPrivacy) {
241
- setConsentError('Debes confirmar edad y política de privacidad.')
242
- return
243
- }
244
- setConsentError(null)
245
-
246
- const paymentParsed = buildCheckoutPaymentStepSchema().safeParse(validationContext())
247
- if (!paymentParsed.success) {
248
- const errors = mapZodIssuesToFieldErrors(paymentParsed.error)
249
- setFieldErrors(errors)
250
- const first = firstInvalidFieldId(errors)
251
- if (first) focusCheckoutField(first)
252
- return
253
- }
254
-
255
- const schema = buildCheckoutFormValidationSchema(store.currency)
256
- const parsed = schema.safeParse(validationContext())
257
- if (!parsed.success) {
258
- const errors = mapZodIssuesToFieldErrors(parsed.error)
259
- setFieldErrors(errors)
260
- const first = firstInvalidFieldId(errors)
261
- if (first) {
262
- const step = checkoutStepForField(first)
263
- setStepFlow((flow) => editCheckoutStep(flow, step))
264
- if (step !== 'payment') checkout.invalidateDeliveryPersistence()
265
- focusCheckoutField(first)
266
- }
267
- return
268
- }
269
- setFieldErrors({})
270
-
271
- try {
272
- let uploadedReceipt = receiptUrl ?? undefined
273
- if (isManual && receiptFile && !uploadedReceipt) {
274
- try {
275
- const uploaded = await checkout.uploadPaymentReceipt(receiptFile)
276
- uploadedReceipt = uploaded.url
277
- setReceiptUrl(uploaded.url)
278
- setReceiptError(null)
279
- } catch (error) {
280
- setReceiptError(
281
- error instanceof Error
282
- ? error.message
283
- : 'No se pudo subir el comprobante de pago.',
284
- )
285
- return
286
- }
287
- }
288
-
289
- const result: CartCheckoutResponse | undefined = await checkout.payNow(
290
- uploadedReceipt ? { receiptUrl: uploadedReceipt } : undefined,
291
- )
292
- if (!result) return
293
-
294
- checkout.clearCart()
295
- const redirect = paymentRedirectUrl(result)
296
- if (redirect) {
297
- window.location.assign(redirect)
298
- return
299
- }
300
- window.location.assign(
301
- thankYouUrl(cartToken, result.payment?.transactionId ?? undefined),
302
- )
303
- } catch {
304
- /* formError en sesión */
305
- }
306
- }
307
-
308
- return (
309
- <div className="mx-auto w-full max-w-6xl px-4 py-8 sm:py-10">
310
- {checkout.cartChangedExternally ? (
311
- <CheckoutExternalUpdateBanner onReload={checkout.reloadCheckout} />
312
- ) : null}
313
-
314
- <header className="mb-8 flex flex-wrap items-end justify-between gap-4 border-b pb-6">
315
- <div>
316
- <p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
317
- Compra
318
- </p>
319
- <h1 className="mt-2 text-3xl font-semibold tracking-tight sm:text-4xl">Checkout</h1>
320
- </div>
321
- <Link to="/cart" className={buttonVariants({ variant: 'ghost' })}>
322
- <ArrowLeft aria-hidden="true" />
323
- Volver al carrito
324
- </Link>
325
- </header>
326
-
327
- <form
328
- onSubmit={(e) => void handlePay(e)}
329
- className="grid items-start gap-8 lg:grid-cols-[minmax(0,1fr)_22rem] lg:gap-10"
330
- >
331
- <div className="space-y-4">
332
- <CheckoutStepCard
333
- step="contact"
334
- flow={stepFlow}
335
- title="Mis datos"
336
- icon={User}
337
- onEdit={() => openStep('contact')}
338
- summary={
339
- <CheckoutStepSummaryBlock>
340
- <p className="text-sm">{checkout.contact.email}</p>
341
- </CheckoutStepSummaryBlock>
342
- }
343
- >
344
- <CheckoutContactSection
345
- email={checkout.contact.email}
346
- fieldError={fieldErrors.email}
347
- sessionLabel={session?.user?.email ?? session?.user?.name}
348
- isSaving={checkout.isSaving}
349
- onEmailChange={(email) => patchContact({ email })}
350
- onClearError={clearFieldError}
351
- onContinueAsGuest={handleContinueContact}
352
- />
353
- </CheckoutStepCard>
354
-
355
- <CheckoutStepCard
356
- step="delivery"
357
- flow={stepFlow}
358
- title="Dirección de entrega"
359
- icon={Truck}
360
- onEdit={() => openStep('delivery')}
361
- summary={
362
- <DeliveryStepSummary
363
- contact={checkout.contact}
364
- shippingLabel={
365
- selectedShipping
366
- ? `${selectedShipping.rate.name}${
367
- selectedShipping.rate.description
368
- ? ` (${selectedShipping.rate.description})`
369
- : ''
370
- }`
371
- : null
372
- }
373
- shippingPrice={
374
- selectedShipping
375
- ? selectedShipping.rate.price === 0
376
- ? 'GRATIS'
377
- : money(selectedShipping.rate.price)
378
- : null
379
- }
380
- />
381
- }
382
- >
383
- <CheckoutShippingAddressSection
384
- contact={checkout.contact}
385
- fieldErrors={fieldErrors}
386
- countries={geo.countries}
387
- departments={geo.departments}
388
- provinces={geo.provinces}
389
- districts={geo.districts}
390
- shippingOptions={checkout.shippingFlat}
391
- selectedShippingKey={checkout.selectedShippingKey}
392
- shippingLoading={checkout.shippingLoading}
393
- shippingError={checkout.shippingError}
394
- isSaving={checkout.isSaving}
395
- money={money}
396
- onPatch={patchContact}
397
- onSelectShipping={checkout.setSelectedShippingKey}
398
- onClearError={clearFieldError}
399
- onContinue={handleContinueDelivery}
400
- savedAddresses={customerPrefill.savedAddresses}
401
- selectedSavedAddressId={customerPrefill.selectedSavedAddressId}
402
- onSelectSavedAddress={customerPrefill.selectSavedAddress}
403
- canSaveToAccount={isAuthenticated}
404
- addressSaveError={addressSaveError}
405
- saveToAccount={saveToAccount}
406
- saveAddressKey={saveAddressKey}
407
- onSaveToAccountChange={(checked) => {
408
- setSaveToAccount(checked)
409
- setAddressSaveError(null)
410
- }}
411
- onSaveAddressKeyChange={setSaveAddressKey}
412
- />
413
- </CheckoutStepCard>
414
-
415
- <CheckoutStepCard
416
- step="payment"
417
- flow={stepFlow}
418
- title="Pago"
419
- icon={CreditCard}
420
- >
421
- <div className="space-y-5">
422
- <div className="space-y-3">
423
- <label className="flex cursor-pointer items-start gap-2.5 text-sm leading-5">
424
- <input
425
- type="checkbox"
426
- className="mt-0.5 size-4 shrink-0 rounded border-border accent-foreground"
427
- checked={acceptedAge}
428
- onChange={(e) => {
429
- setAcceptedAge(e.target.checked)
430
- setConsentError(null)
431
- }}
432
- />
433
- <span>Tengo 18 años o más</span>
434
- </label>
435
- <label className="flex cursor-pointer items-start gap-2.5 text-sm leading-5">
436
- <input
437
- type="checkbox"
438
- className="mt-0.5 size-4 shrink-0 rounded border-border accent-foreground"
439
- checked={acceptedPrivacy}
440
- onChange={(e) => {
441
- setAcceptedPrivacy(e.target.checked)
442
- setConsentError(null)
443
- }}
444
- />
445
- <span>
446
- Doy mi consentimiento para que mis datos personales sean tratados conforme a
447
- la política de privacidad
448
- </span>
449
- </label>
450
- {consentError ? (
451
- <p className="text-xs text-destructive">{consentError}</p>
452
- ) : null}
453
- </div>
454
-
455
- <CheckoutPaymentSection
456
- methods={checkout.paymentMethods}
457
- selectedCode={checkout.selectedPaymentCode}
458
- fieldError={fieldErrors['checkout-payment-method']}
459
- checkoutPaymentLoading={checkout.checkoutPaymentLoading}
460
- checkoutPaymentError={checkout.checkoutPaymentError}
461
- checkoutPaymentResponse={checkout.checkoutPaymentResponse}
462
- checkoutPaymentMethodCode={checkout.checkoutPaymentMethodCode}
463
- providerPaymentCompleted={checkout.providerPaymentCompleted}
464
- receiptUrl={receiptUrl}
465
- receiptError={receiptError}
466
- onSelect={checkout.selectPaymentMethod}
467
- onClearError={clearFieldError}
468
- onMarkProviderCompleted={() => checkout.setProviderPaymentCompleted(true)}
469
- onReceiptFileChange={(file) => {
470
- setReceiptFile(file)
471
- setReceiptUrl(null)
472
- setReceiptError(null)
473
- }}
474
- />
475
-
476
- <CheckoutOrderBumps
477
- bumps={checkout.orderBumps}
478
- money={money}
479
- busy={checkout.isSaving}
480
- onAdd={(bump) => void checkout.handleAddOrderBump(bump)}
481
- />
482
- </div>
483
- </CheckoutStepCard>
484
-
485
- <p className="flex items-start gap-2 text-xs leading-5 text-muted-foreground">
486
- <Lock className="mt-0.5 size-3.5 shrink-0" aria-hidden="true" />
487
- Tus datos se usan solo para procesar este pedido.
488
- </p>
489
- </div>
490
-
491
- <CheckoutOrderSummary
492
- items={cart.items}
493
- money={money}
494
- subtotal={checkout.subtotal}
495
- discount={checkout.discount}
496
- shipping={checkout.shipping}
497
- tax={checkout.tax}
498
- total={checkout.total}
499
- hasShippingOptions={checkout.shippingFlat.length > 0}
500
- couponCode={checkout.couponCode}
501
- couponError={checkout.couponError}
502
- promotionCodes={cart.promotionCodes}
503
- formError={checkout.formError}
504
- isSaving={checkout.isSaving}
505
- canSubmit={stepFlow.active === 'payment'}
506
- submitLabel={
507
- selectedMethod
508
- ? `Pagar con ${selectedMethod.name}`
509
- : 'Pagar'
510
- }
511
- onCouponCodeChange={checkout.setCouponCode}
512
- onApplyCoupon={() => void checkout.handleApplyCoupon()}
513
- onRemoveCoupon={(code) => void checkout.handleRemoveCoupon(code)}
514
- />
515
- </form>
516
- </div>
517
- )
518
- }
519
-
520
- function DeliveryStepSummary({
521
- contact,
522
- shippingLabel,
523
- shippingPrice,
524
- }: {
525
- contact: CheckoutContactForm
526
- shippingLabel: string | null
527
- shippingPrice: string | null
528
- }) {
529
- const fullName = `${contact.firstName} ${contact.lastName}`.trim()
530
- const locality = [contact.districtName || contact.city, contact.provinceName || contact.province]
531
- .filter(Boolean)
532
- .join(', ')
533
- const addressLine = [contact.address, contact.address2, locality, contact.postalCode]
534
- .filter(Boolean)
535
- .join(', ')
536
-
537
- return (
538
- <div className="space-y-3">
539
- <CheckoutStepSummaryBlock>
540
- <p className="text-sm font-medium">{fullName || 'Destinatario'}</p>
541
- <p className="mt-1 text-sm text-muted-foreground">{addressLine}</p>
542
- </CheckoutStepSummaryBlock>
543
- {shippingLabel ? (
544
- <CheckoutStepSummaryBlock>
545
- <p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
546
- Envío
547
- </p>
548
- <div className="mt-1 flex items-start justify-between gap-3">
549
- <p className="text-sm">{shippingLabel}</p>
550
- {shippingPrice ? (
551
- <p className="shrink-0 text-sm font-semibold tabular-nums">{shippingPrice}</p>
552
- ) : null}
553
- </div>
554
- </CheckoutStepSummaryBlock>
555
- ) : null}
556
- <CheckoutStepSummaryBlock>
557
- <p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
558
- Dirección de facturación
559
- </p>
560
- <p className="mt-1 text-sm">Misma dirección que la de entrega</p>
561
- </CheckoutStepSummaryBlock>
562
- </div>
563
- )
564
- }