@ecomiq/storefront 2.1.3 → 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.
Files changed (317) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -123
  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 -81
  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/cloud-runtime-client.ts +0 -378
  238. package/src/codegen.mjs +0 -789
  239. package/src/components/account/customer-addresses-panel.tsx +0 -678
  240. package/src/components/account/customer-order-detail-panel.tsx +0 -285
  241. package/src/components/account/customer-orders-panel.tsx +0 -192
  242. package/src/components/account/customer-profile-panel.tsx +0 -379
  243. package/src/components/checkout/cart-sync.ts +0 -143
  244. package/src/components/checkout/checkout-contact-section.tsx +0 -76
  245. package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
  246. package/src/components/checkout/checkout-form-controls.tsx +0 -139
  247. package/src/components/checkout/checkout-line-items.tsx +0 -43
  248. package/src/components/checkout/checkout-order-summary.tsx +0 -165
  249. package/src/components/checkout/checkout-payment-section.tsx +0 -212
  250. package/src/components/checkout/checkout-session-view.tsx +0 -564
  251. package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
  252. package/src/components/checkout/checkout-shipping-section.tsx +0 -98
  253. package/src/components/checkout/checkout-states.tsx +0 -73
  254. package/src/components/checkout/checkout-step-card.tsx +0 -101
  255. package/src/components/checkout/checkout-steps.ts +0 -111
  256. package/src/components/checkout/form-schema.ts +0 -281
  257. package/src/components/checkout/format.ts +0 -15
  258. package/src/components/checkout/index.ts +0 -5
  259. package/src/components/checkout/map-customer-to-checkout.ts +0 -188
  260. package/src/components/checkout/types.ts +0 -41
  261. package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
  262. package/src/components/checkout/use-checkout-geo.ts +0 -50
  263. package/src/components/checkout/use-checkout-session.ts +0 -640
  264. package/src/components/hero.tsx +0 -291
  265. package/src/config.d.ts +0 -94
  266. package/src/config.mjs +0 -146
  267. package/src/format-money.ts +0 -31
  268. package/src/health.ts +0 -90
  269. package/src/hooks/index.ts +0 -9
  270. package/src/hooks/recently-viewed.ts +0 -125
  271. package/src/index.ts +0 -354
  272. package/src/link.tsx +0 -2
  273. package/src/pages/account/account-page-skeleton.tsx +0 -69
  274. package/src/pages/account/account-page-view.tsx +0 -270
  275. package/src/pages/account/account-shell.tsx +0 -101
  276. package/src/pages/account/parse-account-route.ts +0 -36
  277. package/src/pages/account.tsx +0 -16
  278. package/src/pages/api.$.ts +0 -20
  279. package/src/pages/api.auth.$.ts +0 -16
  280. package/src/pages/checkout.$checkoutId.tsx +0 -60
  281. package/src/pages/checkout.tsx +0 -31
  282. package/src/pages/pages.$pageSlug.tsx +0 -111
  283. package/src/pages/thank-you.tsx +0 -53
  284. package/src/providers/auth.tsx +0 -96
  285. package/src/providers/cart.tsx +0 -275
  286. package/src/providers/ecomiq.ts +0 -137
  287. package/src/providers/index.ts +0 -26
  288. package/src/proxy-api.ts +0 -20
  289. package/src/public-config.ts +0 -24
  290. package/src/review-submission.ts +0 -89
  291. package/src/runtime/context.ts +0 -2
  292. package/src/runtime/default-layout.tsx +0 -64
  293. package/src/runtime/head.ts +0 -149
  294. package/src/runtime/page.tsx +0 -143
  295. package/src/runtime/postcss-browser-stub.mjs +0 -5
  296. package/src/server-config-store.ts +0 -35
  297. package/src/server.ts +0 -301
  298. package/src/storefront-definition/contract.ts +0 -1
  299. package/src/storefront-definition/data.ts +0 -188
  300. package/src/storefront-definition/index.ts +0 -2
  301. package/src/storefront-definition/renderer.tsx +0 -1481
  302. package/src/storefront-layout.ts +0 -59
  303. package/src/storefront-runtime.ts +0 -173
  304. package/src/styles.css +0 -97
  305. package/src/ui/badge.tsx +0 -32
  306. package/src/ui/button.tsx +0 -31
  307. package/src/ui/card.tsx +0 -31
  308. package/src/ui/carousel.tsx +0 -218
  309. package/src/ui/cn.ts +0 -6
  310. package/src/ui/index.ts +0 -8
  311. package/src/ui/separator.tsx +0 -6
  312. package/src/ui/skeleton.tsx +0 -6
  313. package/src/ui/storefront-error.tsx +0 -49
  314. package/src/utils.ts +0 -36
  315. package/src/vite-inline.mjs +0 -263
  316. package/templates/__root.tsx +0 -83
  317. package/templates/router.tsx +0 -19
@@ -1,640 +0,0 @@
1
- import {
2
- useCallback,
3
- useEffect,
4
- useMemo,
5
- useRef,
6
- useState,
7
- } from 'react'
8
- import type {
9
- CartCheckoutResponse,
10
- CartPaymentMethod,
11
- ShippingOptionGroup,
12
- ShippingRate,
13
- StorefrontCart,
14
- StorefrontOrderBump,
15
- } from '@ecomiq/core'
16
- import { useCart } from '../../providers'
17
- import { serverApi } from '../../server'
18
- import {
19
- areCartSignaturesEqual,
20
- cartItemsSignature,
21
- cartSessionSignature,
22
- findCartReconciliationActions,
23
- isCheckoutCartAlreadyPurchased,
24
- methodUsesProviderCheckoutUi,
25
- toSignatureItems,
26
- } from './cart-sync'
27
- import { emptyCheckoutContact, type CheckoutContactForm } from './types'
28
-
29
- type ShippingFlat = {
30
- key: string
31
- group: ShippingOptionGroup
32
- rate: ShippingRate
33
- }
34
-
35
- export function useCheckoutSession(cartToken: string) {
36
- const { cart: providerCart, clearCart, reload: reloadProviderCart } = useCart()
37
-
38
- const [checkoutCart, setCheckoutCart] = useState<StorefrontCart | null>(null)
39
- const [paymentMethods, setPaymentMethods] = useState<CartPaymentMethod[]>([])
40
- const [orderBumps, setOrderBumps] = useState<StorefrontOrderBump[]>([])
41
- const [loadStatus, setLoadStatus] = useState<'loading' | 'ready' | 'error'>('loading')
42
- const [loadError, setLoadError] = useState<string>()
43
- const [checkoutAlreadyPurchased, setCheckoutAlreadyPurchased] = useState(false)
44
- const [isReconciling, setIsReconciling] = useState(false)
45
- const [cartChangedExternally, setCartChangedExternally] = useState(false)
46
-
47
- const [contact, setContact] = useState<CheckoutContactForm>(() => emptyCheckoutContact())
48
- const [selectedShippingKey, setSelectedShippingKey] = useState<string | null>(null)
49
- const [selectedPaymentCode, setSelectedPaymentCode] = useState<string | null>(null)
50
- const [providerPaymentCompleted, setProviderPaymentCompleted] = useState(false)
51
- const [couponCode, setCouponCode] = useState('')
52
- const [couponError, setCouponError] = useState<string | null>(null)
53
- const [formError, setFormError] = useState<string | null>(null)
54
- const [isSaving, setIsSaving] = useState(false)
55
-
56
- const [shippingGroups, setShippingGroups] = useState<ShippingOptionGroup[]>([])
57
- const [shippingLoading, setShippingLoading] = useState(false)
58
- const [shippingError, setShippingError] = useState<string | null>(null)
59
-
60
- const [checkoutPaymentResponse, setCheckoutPaymentResponse] =
61
- useState<CartCheckoutResponse | null>(null)
62
- const [checkoutPaymentMethodCode, setCheckoutPaymentMethodCode] = useState<string | null>(
63
- null,
64
- )
65
- const [checkoutPaymentLoading, setCheckoutPaymentLoading] = useState(false)
66
- const [checkoutPaymentError, setCheckoutPaymentError] = useState<string | null>(null)
67
- const [deliveryPersisted, setDeliveryPersisted] = useState(false)
68
-
69
- const hydratedRef = useRef(false)
70
- const reconcileAttemptRef = useRef<string | null>(null)
71
- const isReconcilingRef = useRef(false)
72
- const checkoutPaymentInitKeyRef = useRef<string | null>(null)
73
- const baselineSigRef = useRef<string | null>(null)
74
-
75
- const localItems = useMemo(
76
- () => toSignatureItems(providerCart?.items),
77
- [providerCart?.items],
78
- )
79
- const apiItems = useMemo(
80
- () => toSignatureItems(checkoutCart?.items),
81
- [checkoutCart?.items],
82
- )
83
- const cartsReconciled = useMemo(
84
- () => areCartSignaturesEqual(localItems, apiItems),
85
- [localItems, apiItems],
86
- )
87
-
88
- const loadCheckout = useCallback(async () => {
89
- setLoadStatus('loading')
90
- setLoadError(undefined)
91
- try {
92
- const data = await serverApi.getCartCheckout({ cartToken })
93
- if (isCheckoutCartAlreadyPurchased(data.cart)) {
94
- setCheckoutAlreadyPurchased(true)
95
- clearCart()
96
- setCheckoutCart(data.cart)
97
- setLoadStatus('ready')
98
- return
99
- }
100
- setCheckoutCart(data.cart)
101
- setPaymentMethods(data.paymentMethods.methods.filter((m) => m.isActive))
102
- setOrderBumps(data.orderBumps ?? [])
103
- setCheckoutAlreadyPurchased(false)
104
- setLoadStatus('ready')
105
- } catch (error) {
106
- setLoadError(
107
- error instanceof Error ? error.message : 'No pudimos cargar el checkout.',
108
- )
109
- setLoadStatus('error')
110
- }
111
- }, [cartToken, clearCart])
112
-
113
- useEffect(() => {
114
- hydratedRef.current = false
115
- reconcileAttemptRef.current = null
116
- checkoutPaymentInitKeyRef.current = null
117
- baselineSigRef.current = null
118
- setCartChangedExternally(false)
119
- setSelectedShippingKey(null)
120
- setSelectedPaymentCode(null)
121
- setProviderPaymentCompleted(false)
122
- setCheckoutPaymentResponse(null)
123
- setCheckoutPaymentMethodCode(null)
124
- setDeliveryPersisted(false)
125
- void loadCheckout()
126
- }, [cartToken, loadCheckout])
127
-
128
- useEffect(() => {
129
- if (!checkoutCart || hydratedRef.current) return
130
- hydratedRef.current = true
131
- const ship = checkoutCart.shippingAddress
132
- setContact((c) => ({
133
- ...c,
134
- email: checkoutCart.email ?? c.email,
135
- firstName: checkoutCart.firstName ?? c.firstName,
136
- lastName: checkoutCart.lastName ?? c.lastName,
137
- phone: checkoutCart.phone ?? c.phone,
138
- address: ship?.address1 ?? c.address,
139
- address2: ship?.address2 ?? c.address2,
140
- postalCode: ship?.postalCode ?? c.postalCode,
141
- province: ship?.province ?? c.province,
142
- city: ship?.city ?? c.city,
143
- countryCode: ship?.countryCode ?? c.countryCode,
144
- company: ship?.company ?? c.company,
145
- districtName: ship?.district ?? c.districtName,
146
- }))
147
- }, [checkoutCart])
148
-
149
- // Cross-tab: sesión de carrito o ítems del provider cambian tras baseline.
150
- useEffect(() => {
151
- if (loadStatus !== 'ready' || isReconciling || checkoutAlreadyPurchased) return
152
- const sig = cartSessionSignature(cartToken, localItems)
153
- if (baselineSigRef.current === null) {
154
- baselineSigRef.current = sig
155
- return
156
- }
157
- if (sig !== baselineSigRef.current) {
158
- setCartChangedExternally(true)
159
- }
160
- }, [cartToken, checkoutAlreadyPurchased, isReconciling, loadStatus, localItems])
161
-
162
- useEffect(() => {
163
- const key = `ecomiq:cart:v1:`
164
- const onStorage = (event: StorageEvent) => {
165
- if (!event.key?.startsWith(key)) return
166
- setCartChangedExternally(true)
167
- }
168
- window.addEventListener('storage', onStorage)
169
- return () => window.removeEventListener('storage', onStorage)
170
- }, [])
171
-
172
- // Reconciliación local → API
173
- useEffect(() => {
174
- if (loadStatus !== 'ready' || !checkoutCart || checkoutAlreadyPurchased) return
175
- if (isReconcilingRef.current) return
176
- if (providerCart && providerCart.token !== cartToken) return
177
- if (localItems.length === 0 && apiItems.length === 0) return
178
- if (areCartSignaturesEqual(localItems, apiItems)) return
179
-
180
- const actions = findCartReconciliationActions(localItems, apiItems)
181
- if (actions.length === 0) return
182
-
183
- const attemptKey = `${cartToken}::${cartItemsSignature(localItems)}::${cartItemsSignature(apiItems)}`
184
- if (reconcileAttemptRef.current === attemptKey) return
185
-
186
- reconcileAttemptRef.current = attemptKey
187
- isReconcilingRef.current = true
188
- setIsReconciling(true)
189
-
190
- void (async () => {
191
- try {
192
- for (const action of actions) {
193
- if (action.action === 'remove') {
194
- try {
195
- const next = await serverApi.removeCartItem({
196
- cartToken,
197
- cartItemId: action.itemId,
198
- })
199
- if (next.ok) setCheckoutCart(next.value)
200
- } catch {
201
- /* stock / línea inválida */
202
- }
203
- } else if (action.action === 'update') {
204
- try {
205
- const next = await serverApi.setCartItemQuantity({
206
- cartToken,
207
- cartItemId: action.itemId,
208
- quantity: action.quantity,
209
- })
210
- if (next.ok) setCheckoutCart(next.value)
211
- } catch {
212
- /* */
213
- }
214
- }
215
- }
216
- for (const action of actions) {
217
- if (action.action !== 'add') continue
218
- try {
219
- const next = await serverApi.addCartItem({
220
- cartToken,
221
- variantId: action.variantId,
222
- quantity: action.quantity,
223
- })
224
- setCheckoutCart(next)
225
- } catch {
226
- /* */
227
- }
228
- }
229
- await loadCheckout()
230
- await reloadProviderCart()
231
- baselineSigRef.current = null
232
- } finally {
233
- isReconcilingRef.current = false
234
- setIsReconciling(false)
235
- }
236
- })()
237
- }, [
238
- apiItems,
239
- cartToken,
240
- checkoutAlreadyPurchased,
241
- checkoutCart,
242
- loadCheckout,
243
- loadStatus,
244
- localItems,
245
- providerCart,
246
- reloadProviderCart,
247
- ])
248
-
249
- const postalCode = contact.postalCode.trim()
250
- useEffect(() => {
251
- if (!postalCode || checkoutAlreadyPurchased) {
252
- setShippingGroups([])
253
- return
254
- }
255
- let cancelled = false
256
- setShippingLoading(true)
257
- setShippingError(null)
258
- void serverApi
259
- .getShippingOptions({ cartToken, postalCode })
260
- .then((res) => {
261
- if (!cancelled) setShippingGroups(res.groups)
262
- })
263
- .catch((error) => {
264
- if (!cancelled) {
265
- setShippingError(
266
- error instanceof Error ? error.message : 'No pudimos cargar envíos.',
267
- )
268
- setShippingGroups([])
269
- }
270
- })
271
- .finally(() => {
272
- if (!cancelled) setShippingLoading(false)
273
- })
274
- return () => {
275
- cancelled = true
276
- }
277
- }, [cartToken, checkoutAlreadyPurchased, postalCode])
278
-
279
- const shippingFlat = useMemo(() => {
280
- const out: ShippingFlat[] = []
281
- for (const group of shippingGroups) {
282
- for (const rate of group.rates ?? []) {
283
- out.push({
284
- key: `${group.sellerId ?? ''}::${rate.rateId}`,
285
- group,
286
- rate,
287
- })
288
- }
289
- }
290
- return out
291
- }, [shippingGroups])
292
-
293
- useEffect(() => {
294
- if (!shippingFlat.length || selectedShippingKey) return
295
- setSelectedShippingKey(shippingFlat[0]!.key)
296
- }, [shippingFlat, selectedShippingKey])
297
-
298
- const externalRef = `checkout-${cartToken}`
299
-
300
- const initProviderCheckout = useCallback(
301
- (paymentMethodCode: string) => {
302
- const method = paymentMethods.find((m) => m.code === paymentMethodCode)
303
- if (!method || !methodUsesProviderCheckoutUi(method)) return
304
-
305
- const dedupeKey = `${cartToken}:${paymentMethodCode}`
306
- if (checkoutPaymentInitKeyRef.current === dedupeKey) return
307
- checkoutPaymentInitKeyRef.current = dedupeKey
308
-
309
- setCheckoutPaymentLoading(true)
310
- setCheckoutPaymentError(null)
311
- setCheckoutPaymentMethodCode(paymentMethodCode)
312
- void serverApi
313
- .checkoutCart({ cartToken, paymentMethodCode, externalRef })
314
- .then((result) => {
315
- setCheckoutPaymentResponse(result)
316
- })
317
- .catch((error) => {
318
- setCheckoutPaymentError(
319
- error instanceof Error ? error.message : 'No pudimos iniciar el pago.',
320
- )
321
- setCheckoutPaymentResponse(null)
322
- })
323
- .finally(() => setCheckoutPaymentLoading(false))
324
- },
325
- [cartToken, externalRef, paymentMethods],
326
- )
327
-
328
- const selectPaymentMethod = useCallback(
329
- (paymentMethodCode: string) => {
330
- setSelectedPaymentCode(paymentMethodCode)
331
- setProviderPaymentCompleted(false)
332
- initProviderCheckout(paymentMethodCode)
333
- },
334
- [initProviderCheckout],
335
- )
336
-
337
- const clearPaymentSession = useCallback(() => {
338
- checkoutPaymentInitKeyRef.current = null
339
- setSelectedPaymentCode(null)
340
- setProviderPaymentCompleted(false)
341
- setCheckoutPaymentResponse(null)
342
- setCheckoutPaymentMethodCode(null)
343
- setCheckoutPaymentError(null)
344
- setCheckoutPaymentLoading(false)
345
- }, [])
346
-
347
- const ensureDefaultPaymentMethod = useCallback(() => {
348
- if (selectedPaymentCode || !paymentMethods.length) return
349
- const first = paymentMethods.find((m) => m.isDefault) ?? paymentMethods[0]
350
- if (first) selectPaymentMethod(first.code)
351
- }, [paymentMethods, selectPaymentMethod, selectedPaymentCode])
352
-
353
- const saveBuyer = useCallback(async () => {
354
- const buyerCart = await serverApi.patchCartBuyer({
355
- cartToken,
356
- email: contact.email.trim(),
357
- firstName: contact.firstName.trim(),
358
- lastName: contact.lastName.trim(),
359
- phone: contact.phone.trim(),
360
- })
361
- setCheckoutCart(buyerCart)
362
- return buyerCart
363
- }, [cartToken, contact.email, contact.firstName, contact.lastName, contact.phone])
364
-
365
- const saveAddresses = useCallback(async () => {
366
- const countryCode = (contact.countryCode || 'PE').toUpperCase()
367
- const districtName = contact.districtName || contact.city
368
- const provinceName = contact.provinceName || contact.province
369
- const baseAddress = {
370
- name: `${contact.firstName} ${contact.lastName}`.trim() || contact.address,
371
- address1: contact.address,
372
- city: districtName,
373
- countryCode,
374
- company: contact.company || null,
375
- address2: contact.address2 || null,
376
- province: provinceName,
377
- postalCode: contact.postalCode,
378
- district: districtName,
379
- phone: contact.phone || null,
380
- latitude: null,
381
- longitude: null,
382
- geo: {
383
- departmentCode: contact.departmentCode || '',
384
- provinceCode: contact.provinceCode || '',
385
- districtCode: contact.districtCode || '',
386
- },
387
- }
388
-
389
- const updated = await serverApi.patchCartAddresses({
390
- cartToken,
391
- shippingAddress: baseAddress,
392
- billingAddress: baseAddress,
393
- })
394
- setCheckoutCart(updated)
395
- return updated
396
- }, [cartToken, contact])
397
-
398
- const saveContactAndDelivery = useCallback(async () => {
399
- await saveBuyer()
400
- await saveAddresses()
401
- }, [saveAddresses, saveBuyer])
402
-
403
- const confirmShipping = useCallback(
404
- async (picked: ShippingFlat) => {
405
- const updated = await serverApi.putCartShippingMethod({
406
- cartToken,
407
- rateId: picked.rate.rateId,
408
- shippingMethodId: picked.rate.shippingMethodId,
409
- serviceCode: picked.rate.serviceCode ?? null,
410
- sellerId: picked.group.sellerId ?? null,
411
- shippingProfileId: picked.group.shippingProfileId ?? null,
412
- locationId: null,
413
- pickupPointId: null,
414
- scheduledDate: null,
415
- scheduledTime: null,
416
- })
417
- setCheckoutCart(updated)
418
- return updated
419
- },
420
- [cartToken],
421
- )
422
-
423
- /** Persiste buyer + dirección + envío al completar el paso Dirección. */
424
- const persistDeliveryStep = useCallback(async () => {
425
- setFormError(null)
426
- setIsSaving(true)
427
- try {
428
- await saveContactAndDelivery()
429
- if (selectedShippingKey) {
430
- const picked = shippingFlat.find((o) => o.key === selectedShippingKey)
431
- if (picked) await confirmShipping(picked)
432
- }
433
- setDeliveryPersisted(true)
434
- clearPaymentSession()
435
- } catch (error) {
436
- setFormError(
437
- error instanceof Error ? error.message : 'No se pudo guardar la dirección.',
438
- )
439
- throw error
440
- } finally {
441
- setIsSaving(false)
442
- }
443
- }, [
444
- clearPaymentSession,
445
- confirmShipping,
446
- saveContactAndDelivery,
447
- selectedShippingKey,
448
- shippingFlat,
449
- ])
450
-
451
- const invalidateDeliveryPersistence = useCallback(() => {
452
- setDeliveryPersisted(false)
453
- clearPaymentSession()
454
- }, [clearPaymentSession])
455
-
456
- const handleApplyCoupon = useCallback(async () => {
457
- const code = couponCode.trim()
458
- if (!code) return
459
- setCouponError(null)
460
- try {
461
- const next = await serverApi.addCartPromotionCode({ cartToken, code })
462
- setCheckoutCart(next)
463
- setCouponCode('')
464
- } catch (error) {
465
- setCouponError(error instanceof Error ? error.message : 'No se pudo aplicar el cupón.')
466
- }
467
- }, [cartToken, couponCode])
468
-
469
- const handleRemoveCoupon = useCallback(
470
- async (code: string) => {
471
- setCouponError(null)
472
- try {
473
- const next = await serverApi.removeCartPromotionCode({ cartToken, code })
474
- setCheckoutCart(next)
475
- } catch (error) {
476
- setCouponError(error instanceof Error ? error.message : 'No se pudo remover el cupón.')
477
- }
478
- },
479
- [cartToken],
480
- )
481
-
482
- const handleAddOrderBump = useCallback(
483
- async (orderBump: StorefrontOrderBump) => {
484
- const next = await serverApi.addCartItem({
485
- cartToken,
486
- variantId: orderBump.variantId,
487
- quantity: 1,
488
- })
489
- setCheckoutCart(next)
490
- await loadCheckout()
491
- await reloadProviderCart()
492
- },
493
- [cartToken, loadCheckout, reloadProviderCart],
494
- )
495
-
496
- const uploadPaymentReceipt = useCallback(
497
- async (file: File) => {
498
- return serverApi.uploadPaymentReceipt({ cartToken, file, fileName: file.name })
499
- },
500
- [cartToken],
501
- )
502
-
503
- const payNow = useCallback(
504
- async (options?: { receiptUrl?: string }): Promise<CartCheckoutResponse | undefined> => {
505
- setFormError(null)
506
- setIsSaving(true)
507
- try {
508
- if (!deliveryPersisted) {
509
- await saveContactAndDelivery()
510
- if (selectedShippingKey) {
511
- const picked = shippingFlat.find((o) => o.key === selectedShippingKey)
512
- if (picked) await confirmShipping(picked)
513
- }
514
- setDeliveryPersisted(true)
515
- }
516
- const code =
517
- selectedPaymentCode ?? paymentMethods[0]?.code
518
- if (!code) {
519
- setFormError('Selecciona un método de pago.')
520
- return
521
- }
522
- const method = paymentMethods.find((m) => m.code === code)
523
- const usesProviderUi = method ? methodUsesProviderCheckoutUi(method) : false
524
-
525
- if (usesProviderUi) {
526
- if (!providerPaymentCompleted) {
527
- setFormError(
528
- 'Completa el pago con Stripe o Mercado Pago antes de continuar.',
529
- )
530
- return
531
- }
532
- if (
533
- checkoutPaymentResponse &&
534
- checkoutPaymentMethodCode === code
535
- ) {
536
- return checkoutPaymentResponse
537
- }
538
- }
539
-
540
- const result = await serverApi.checkoutCart({
541
- cartToken,
542
- paymentMethodCode: code,
543
- externalRef,
544
- ...(options?.receiptUrl ? { receiptUrl: options.receiptUrl } : {}),
545
- })
546
- setCheckoutPaymentResponse(result)
547
- setCheckoutPaymentMethodCode(code)
548
- return result
549
- } catch (error) {
550
- setFormError(
551
- error instanceof Error ? error.message : 'No se pudo completar el pago.',
552
- )
553
- throw error
554
- } finally {
555
- setIsSaving(false)
556
- }
557
- },
558
- [
559
- cartToken,
560
- checkoutPaymentMethodCode,
561
- checkoutPaymentResponse,
562
- confirmShipping,
563
- deliveryPersisted,
564
- externalRef,
565
- paymentMethods,
566
- providerPaymentCompleted,
567
- saveContactAndDelivery,
568
- selectedPaymentCode,
569
- selectedShippingKey,
570
- shippingFlat,
571
- ],
572
- )
573
-
574
- const reloadCheckout = useCallback(() => {
575
- if (typeof window !== 'undefined') window.location.reload()
576
- }, [])
577
-
578
- const uiCart = cartsReconciled || !providerCart ? checkoutCart : providerCart
579
- const totals = uiCart?.totals
580
- const subtotal = cartsReconciled
581
- ? (totals?.subtotal ?? 0)
582
- : (providerCart?.totals.subtotal ?? 0)
583
- const shipping = cartsReconciled ? (totals?.shippingTotal ?? 0) : 0
584
- const discount = cartsReconciled ? (totals?.discountTotal ?? 0) : 0
585
- const tax = cartsReconciled ? (totals?.taxTotal ?? 0) : 0
586
- const total = cartsReconciled
587
- ? (totals?.total ?? subtotal + shipping + tax - discount)
588
- : subtotal
589
-
590
- return {
591
- cartToken,
592
- loadStatus,
593
- loadError,
594
- cartData: checkoutCart,
595
- uiCart,
596
- paymentMethods,
597
- orderBumps,
598
- isReconciling,
599
- cartChangedExternally,
600
- checkoutAlreadyPurchased,
601
- reloadCheckout,
602
- clearCart,
603
- contact,
604
- setContact,
605
- selectedShippingKey,
606
- setSelectedShippingKey,
607
- selectedPaymentCode,
608
- selectPaymentMethod,
609
- ensureDefaultPaymentMethod,
610
- clearPaymentSession,
611
- persistDeliveryStep,
612
- invalidateDeliveryPersistence,
613
- saveBuyer,
614
- providerPaymentCompleted,
615
- setProviderPaymentCompleted,
616
- payNow,
617
- couponCode,
618
- setCouponCode,
619
- couponError,
620
- formError,
621
- handleApplyCoupon,
622
- handleRemoveCoupon,
623
- handleAddOrderBump,
624
- shippingFlat,
625
- shippingLoading,
626
- shippingError,
627
- uploadPaymentReceipt,
628
- checkoutPaymentResponse,
629
- checkoutPaymentMethodCode,
630
- checkoutPaymentLoading,
631
- checkoutPaymentError,
632
- isSaving: isSaving || cartChangedExternally,
633
- cartsReconciled,
634
- subtotal,
635
- shipping,
636
- discount,
637
- tax,
638
- total,
639
- }
640
- }