@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.
Files changed (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -103
  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,400 +0,0 @@
1
- import { useState } from 'react'
2
- import type { Country, Department, District, Province } from '@ecomiq/core'
3
- import type { CustomerSavedAddress } from '@ecomiq/core'
4
- import { Button } from '../../ui'
5
- import type { CheckoutContactForm } from './types'
6
- import type { CheckoutFieldId } from './form-schema'
7
- import {
8
- CheckoutPhoneField,
9
- CheckoutSelectField,
10
- CheckoutTextField,
11
- FieldError,
12
- fieldClass,
13
- } from './checkout-form-controls'
14
- import {
15
- CheckoutShippingSection,
16
- type CheckoutShippingOption,
17
- } from './checkout-shipping-section'
18
-
19
- const DIAL_BY_COUNTRY: Record<string, string> = {
20
- PE: '+51',
21
- US: '+1',
22
- MX: '+52',
23
- CL: '+56',
24
- CO: '+57',
25
- AR: '+54',
26
- ES: '+34',
27
- }
28
-
29
- function dialCodeFor(countryCode: string) {
30
- return DIAL_BY_COUNTRY[countryCode.toUpperCase()] ?? '+51'
31
- }
32
-
33
- /** Paso 2: dirección de entrega + método de envío + facturación. */
34
- export function CheckoutShippingAddressSection({
35
- contact,
36
- fieldErrors,
37
- countries,
38
- departments,
39
- provinces,
40
- districts,
41
- shippingOptions,
42
- selectedShippingKey,
43
- shippingLoading,
44
- shippingError,
45
- isSaving,
46
- money,
47
- onPatch,
48
- onSelectShipping,
49
- onClearError,
50
- onContinue,
51
- savedAddresses,
52
- selectedSavedAddressId,
53
- onSelectSavedAddress,
54
- canSaveToAccount = false,
55
- addressSaveError,
56
- saveToAccount = false,
57
- saveAddressKey = 'Casa',
58
- onSaveToAccountChange,
59
- onSaveAddressKeyChange,
60
- }: {
61
- contact: CheckoutContactForm
62
- fieldErrors: Partial<Record<CheckoutFieldId, string>>
63
- countries: Country[]
64
- departments: Department[]
65
- provinces: Province[]
66
- districts: District[]
67
- shippingOptions: CheckoutShippingOption[]
68
- selectedShippingKey: string | null
69
- shippingLoading: boolean
70
- shippingError: string | null
71
- isSaving?: boolean
72
- money: (value: number) => string
73
- onPatch: (patch: Partial<CheckoutContactForm>) => void
74
- onSelectShipping: (key: string) => void
75
- onClearError: (id: CheckoutFieldId) => void
76
- onContinue: (options?: {
77
- saveToAccount?: boolean
78
- addressKey?: string
79
- }) => void | Promise<void>
80
- savedAddresses?: CustomerSavedAddress[]
81
- selectedSavedAddressId?: string | null
82
- onSelectSavedAddress?: (addressId: string) => void
83
- /** Muestra checkbox para guardar en “Mis direcciones” (usuario logueado). */
84
- canSaveToAccount?: boolean
85
- addressSaveError?: string | null
86
- saveToAccount?: boolean
87
- saveAddressKey?: string
88
- onSaveToAccountChange?: (checked: boolean) => void
89
- onSaveAddressKeyChange?: (value: string) => void
90
- }) {
91
- const isPe = contact.countryCode.toUpperCase() === 'PE'
92
- const hasSavedAddresses = Boolean(savedAddresses?.length && onSelectSavedAddress)
93
- const [localSaveError, setLocalSaveError] = useState<string | null>(null)
94
- const saveError = addressSaveError ?? localSaveError
95
-
96
- return (
97
- <div className="space-y-6">
98
- {hasSavedAddresses ? (
99
- <div className="min-w-0">
100
- <label
101
- htmlFor="saved-address"
102
- className="text-sm font-medium text-foreground"
103
- >
104
- Dirección guardada
105
- </label>
106
- <select
107
- id="saved-address"
108
- className={fieldClass()}
109
- value={selectedSavedAddressId ?? ''}
110
- onChange={(e) => {
111
- const id = e.target.value
112
- if (id) onSelectSavedAddress?.(id)
113
- }}
114
- >
115
- <option value="" disabled>
116
- Seleccionar dirección…
117
- </option>
118
- {savedAddresses!.map((addr) => (
119
- <option key={addr.id} value={addr.id}>
120
- {addr.addressKey}
121
- {addr.isDefault ? ' (predeterminada)' : ''}
122
- {' — '}
123
- {[addr.addressLine, addr.addressNumber, addr.district]
124
- .filter(Boolean)
125
- .join(' ')}
126
- </option>
127
- ))}
128
- </select>
129
- <p className="mt-1.5 text-xs text-muted-foreground">
130
- Usa una dirección de tu cuenta o edita los campos abajo.
131
- </p>
132
- </div>
133
- ) : null}
134
-
135
- <div className="grid gap-4 sm:grid-cols-2">
136
- <CheckoutTextField
137
- id="firstName"
138
- label="Nombre"
139
- error={fieldErrors.firstName}
140
- value={contact.firstName}
141
- onChange={(v) => {
142
- onClearError('firstName')
143
- onPatch({ firstName: v })
144
- }}
145
- autoComplete="given-name"
146
- />
147
- <CheckoutTextField
148
- id="lastName"
149
- label="Apellido"
150
- error={fieldErrors.lastName}
151
- value={contact.lastName}
152
- onChange={(v) => {
153
- onClearError('lastName')
154
- onPatch({ lastName: v })
155
- }}
156
- autoComplete="family-name"
157
- />
158
- </div>
159
-
160
- <CheckoutPhoneField
161
- id="phone"
162
- label="Teléfono"
163
- error={fieldErrors.phone}
164
- value={contact.phone}
165
- dialCode={dialCodeFor(contact.countryCode)}
166
- onChange={(v) => {
167
- onClearError('phone')
168
- onPatch({ phone: v })
169
- }}
170
- />
171
-
172
- <CheckoutTextField
173
- id="address"
174
- label="Dirección"
175
- error={fieldErrors.address}
176
- value={contact.address}
177
- onChange={(v) => {
178
- onClearError('address')
179
- onPatch({ address: v })
180
- }}
181
- autoComplete="street-address"
182
- />
183
-
184
- <CheckoutTextField
185
- id="address2"
186
- label="Información adicional de referencia"
187
- value={contact.address2}
188
- onChange={(v) => onPatch({ address2: v })}
189
- autoComplete="address-line2"
190
- />
191
-
192
- <div className="min-w-0">
193
- <label htmlFor="countryCode" className="text-sm font-medium">
194
- País
195
- </label>
196
- <select
197
- id="countryCode"
198
- className={fieldClass(fieldErrors.countryCode)}
199
- value={contact.countryCode}
200
- onChange={(e) => {
201
- onClearError('countryCode')
202
- onPatch({
203
- countryCode: e.target.value,
204
- departmentCode: '',
205
- provinceCode: '',
206
- districtCode: '',
207
- departmentName: '',
208
- provinceName: '',
209
- districtName: '',
210
- city: '',
211
- province: '',
212
- })
213
- }}
214
- >
215
- {(countries.length ? countries : [{ code: 'PE', name: 'Perú' }]).map((c) => (
216
- <option key={c.code} value={c.code}>
217
- {c.name}
218
- </option>
219
- ))}
220
- </select>
221
- <FieldError message={fieldErrors.countryCode} />
222
- </div>
223
-
224
- {isPe ? (
225
- <div className="grid gap-4 sm:grid-cols-3">
226
- <CheckoutSelectField
227
- id="department"
228
- label="Departamento"
229
- error={fieldErrors.department}
230
- value={contact.departmentCode}
231
- options={departments.map((d) => ({ value: d.code, label: d.name }))}
232
- onChange={(code) => {
233
- onClearError('department')
234
- const dep = departments.find((d) => d.code === code)
235
- onPatch({
236
- departmentCode: code,
237
- departmentName: dep?.name ?? '',
238
- provinceCode: '',
239
- provinceName: '',
240
- districtCode: '',
241
- districtName: '',
242
- })
243
- }}
244
- />
245
- <CheckoutSelectField
246
- id="province-geo"
247
- label="Provincia"
248
- error={fieldErrors['province-geo']}
249
- value={contact.provinceCode}
250
- options={provinces.map((d) => ({ value: d.code, label: d.name }))}
251
- onChange={(code) => {
252
- onClearError('province-geo')
253
- const prov = provinces.find((d) => d.code === code)
254
- onPatch({
255
- provinceCode: code,
256
- provinceName: prov?.name ?? '',
257
- province: prov?.name ?? '',
258
- districtCode: '',
259
- districtName: '',
260
- })
261
- }}
262
- />
263
- <CheckoutSelectField
264
- id="district"
265
- label="Distrito"
266
- error={fieldErrors.district}
267
- value={contact.districtCode}
268
- options={districts.map((d) => ({ value: d.code, label: d.name }))}
269
- onChange={(code) => {
270
- onClearError('district')
271
- const dist = districts.find((d) => d.code === code)
272
- onPatch({
273
- districtCode: code,
274
- districtName: dist?.name ?? '',
275
- city: dist?.name ?? '',
276
- })
277
- }}
278
- />
279
- </div>
280
- ) : (
281
- <div className="grid gap-4 sm:grid-cols-3">
282
- <CheckoutTextField
283
- id="city"
284
- label="Ciudad"
285
- error={fieldErrors.city}
286
- value={contact.city}
287
- onChange={(v) => {
288
- onClearError('city')
289
- onPatch({ city: v })
290
- }}
291
- />
292
- <CheckoutTextField
293
- id="province-region"
294
- label="Región / Estado"
295
- error={fieldErrors['province-region']}
296
- value={contact.province}
297
- onChange={(v) => {
298
- onClearError('province-region')
299
- onPatch({ province: v })
300
- }}
301
- />
302
- <CheckoutTextField
303
- id="postalCode"
304
- label="Código postal"
305
- value={contact.postalCode}
306
- onChange={(v) => onPatch({ postalCode: v })}
307
- autoComplete="postal-code"
308
- />
309
- </div>
310
- )}
311
-
312
- {isPe ? (
313
- <CheckoutTextField
314
- id="postalCode"
315
- label="Código postal"
316
- value={contact.postalCode}
317
- onChange={(v) => onPatch({ postalCode: v })}
318
- autoComplete="postal-code"
319
- />
320
- ) : null}
321
-
322
- <CheckoutShippingSection
323
- options={shippingOptions}
324
- selectedKey={selectedShippingKey}
325
- loading={shippingLoading}
326
- error={shippingError}
327
- needsPostalCode={!contact.postalCode.trim()}
328
- fieldError={fieldErrors['checkout-shipping-method']}
329
- money={money}
330
- onSelect={onSelectShipping}
331
- onClearError={onClearError}
332
- />
333
-
334
- <div className="space-y-3">
335
- <h3 className="text-base font-semibold tracking-tight">Dirección de facturación</h3>
336
- <label className="flex items-center gap-2.5 text-sm text-foreground">
337
- <input
338
- type="checkbox"
339
- className="size-4 rounded border-border accent-foreground"
340
- checked
341
- disabled
342
- readOnly
343
- />
344
- Misma dirección que la de entrega
345
- </label>
346
- </div>
347
-
348
- {canSaveToAccount ? (
349
- <div className="space-y-3 rounded-lg border border-border bg-muted/20 p-4">
350
- <label className="flex items-start gap-2.5 text-sm text-foreground">
351
- <input
352
- type="checkbox"
353
- className="mt-0.5 size-4 rounded border-border accent-foreground"
354
- checked={saveToAccount}
355
- onChange={(e) => {
356
- onSaveToAccountChange?.(e.target.checked)
357
- setLocalSaveError(null)
358
- }}
359
- />
360
- <span>
361
- Guardar esta dirección en{' '}
362
- <span className="font-medium">Mis direcciones</span>
363
- </span>
364
- </label>
365
- {saveToAccount ? (
366
- <CheckoutTextField
367
- id="addressKey"
368
- label="Nombre de la dirección"
369
- value={saveAddressKey}
370
- placeholder="Casa, Oficina…"
371
- onChange={(v) => onSaveAddressKeyChange?.(v)}
372
- />
373
- ) : null}
374
- {saveError ? <FieldError message={saveError} /> : null}
375
- </div>
376
- ) : null}
377
-
378
- <Button
379
- type="button"
380
- className="w-full uppercase tracking-wide"
381
- size="lg"
382
- disabled={isSaving}
383
- onClick={() => {
384
- setLocalSaveError(null)
385
- if (saveToAccount && !saveAddressKey.trim()) {
386
- setLocalSaveError('Indica un nombre para la dirección')
387
- return
388
- }
389
- void onContinue(
390
- canSaveToAccount && saveToAccount
391
- ? { saveToAccount: true, addressKey: saveAddressKey.trim() }
392
- : undefined,
393
- )
394
- }}
395
- >
396
- {isSaving ? 'Guardando…' : 'Enviar y continuar'}
397
- </Button>
398
- </div>
399
- )
400
- }
@@ -1,98 +0,0 @@
1
- import { Check } from 'lucide-react'
2
- import type { ShippingOptionGroup, ShippingRate } from '@ecomiq/core'
3
- import { cn } from '../../ui'
4
- import type { CheckoutFieldId } from './form-schema'
5
- import { FieldError } from './checkout-form-controls'
6
-
7
- export type CheckoutShippingOption = {
8
- key: string
9
- group: ShippingOptionGroup
10
- rate: ShippingRate
11
- }
12
-
13
- function shippingSubtitle(rate: ShippingRate) {
14
- if (rate.description?.trim()) return rate.description
15
- if (rate.estimatedDeliveryDays != null) {
16
- return `${rate.estimatedDeliveryDays} días hábiles`
17
- }
18
- return 'Entrega estándar'
19
- }
20
-
21
- export function CheckoutShippingSection({
22
- options,
23
- selectedKey,
24
- loading,
25
- error,
26
- needsPostalCode,
27
- fieldError,
28
- money,
29
- onSelect,
30
- onClearError,
31
- }: {
32
- options: CheckoutShippingOption[]
33
- selectedKey: string | null
34
- loading: boolean
35
- error: string | null
36
- needsPostalCode: boolean
37
- fieldError?: string
38
- money: (value: number) => string
39
- onSelect: (key: string) => void
40
- onClearError: (id: CheckoutFieldId) => void
41
- }) {
42
- return (
43
- <div className="space-y-4">
44
- <h3 className="text-base font-semibold tracking-tight">Envío</h3>
45
- {loading ? <p className="text-sm text-muted-foreground">Cargando opciones…</p> : null}
46
- {error ? <p className="text-sm text-destructive">{error}</p> : null}
47
- {needsPostalCode ? (
48
- <p className="text-sm text-muted-foreground">
49
- Ingresa un código postal para ver métodos de envío.
50
- </p>
51
- ) : null}
52
-
53
- <div id="checkout-shipping-method" className="grid gap-3">
54
- {options.map((opt) => {
55
- const selected = selectedKey === opt.key
56
- return (
57
- <button
58
- key={opt.key}
59
- type="button"
60
- data-checkout-shipping={opt.key}
61
- aria-pressed={selected}
62
- className={cn(
63
- 'relative flex min-h-[4.5rem] items-center justify-between gap-4 rounded-lg border bg-background px-4 py-3.5 text-left transition-colors',
64
- selected
65
- ? 'border-foreground ring-1 ring-foreground'
66
- : 'border-border [@media(hover:hover)]:hover:border-foreground/30',
67
- )}
68
- onClick={() => {
69
- onClearError('checkout-shipping-method')
70
- onSelect(opt.key)
71
- }}
72
- >
73
- <span className="min-w-0 pr-8">
74
- <span className="block text-sm font-semibold">{opt.rate.name}</span>
75
- <span className="mt-1 block text-xs text-muted-foreground">
76
- {shippingSubtitle(opt.rate)}
77
- </span>
78
- </span>
79
- <span className="shrink-0 text-right text-sm font-semibold tabular-nums">
80
- {opt.rate.price === 0 ? (
81
- 'GRATIS'
82
- ) : (
83
- money(opt.rate.price)
84
- )}
85
- </span>
86
- {selected ? (
87
- <span className="absolute right-3 top-3 flex size-5 items-center justify-center rounded-full bg-foreground text-background">
88
- <Check className="size-3" strokeWidth={3} aria-hidden="true" />
89
- </span>
90
- ) : null}
91
- </button>
92
- )
93
- })}
94
- </div>
95
- <FieldError message={fieldError} />
96
- </div>
97
- )
98
- }
@@ -1,73 +0,0 @@
1
- import { ShoppingBag } from 'lucide-react'
2
- import { Link } from '../../link'
3
- import { Skeleton, StorefrontError, buttonVariants, cn } from '../../ui'
4
-
5
- export function CheckoutLoadingState() {
6
- return (
7
- <div className="mx-auto w-full max-w-6xl space-y-8 px-4 py-8 sm:py-10" role="status">
8
- <Skeleton className="h-10 w-56" />
9
- <div className="grid gap-8 lg:grid-cols-[minmax(0,1fr)_22rem]">
10
- <Skeleton className="h-72 w-full rounded-xl" />
11
- <Skeleton className="h-80 w-full rounded-xl" />
12
- </div>
13
- <span className="sr-only">Cargando checkout</span>
14
- </div>
15
- )
16
- }
17
-
18
- export function CheckoutAlreadyPurchasedState() {
19
- return (
20
- <section className="mx-auto flex min-h-[50vh] w-full max-w-lg flex-col items-center justify-center px-4 py-16 text-center">
21
- <h1 className="text-2xl font-semibold tracking-tight">Pedido ya realizado</h1>
22
- <p className="mt-2 text-sm text-muted-foreground">
23
- Este checkout ya fue completado. Tu bolsa se ha reiniciado para que puedas seguir
24
- comprando.
25
- </p>
26
- <Link to="/" className={cn(buttonVariants(), 'mt-6')}>
27
- Volver a la tienda
28
- </Link>
29
- </section>
30
- )
31
- }
32
-
33
- export function CheckoutErrorState({
34
- description,
35
- onRetry,
36
- }: {
37
- description?: string
38
- onRetry: () => void
39
- }) {
40
- return (
41
- <StorefrontError
42
- title="No pudimos cargar el checkout"
43
- description={description ?? 'El carrito puede haber expirado.'}
44
- onRetry={onRetry}
45
- returnTo="/cart"
46
- returnLabel="Volver al carrito"
47
- />
48
- )
49
- }
50
-
51
- export function CheckoutEmptyState() {
52
- return (
53
- <section className="mx-auto flex min-h-[55vh] w-full max-w-6xl items-center px-4 py-16">
54
- <div className="max-w-lg">
55
- <div className="mb-5 flex size-12 items-center justify-center rounded-lg bg-muted text-muted-foreground">
56
- <ShoppingBag className="size-5" aria-hidden="true" />
57
- </div>
58
- <h1 className="text-3xl font-semibold tracking-tight">No hay productos para pagar</h1>
59
- <p className="mt-3 text-sm text-muted-foreground">
60
- Agrega productos al carrito antes de continuar con el checkout.
61
- </p>
62
- <div className="mt-6 flex flex-wrap gap-3">
63
- <Link to="/cart" className={buttonVariants({ size: 'lg', variant: 'outline' })}>
64
- Ver carrito
65
- </Link>
66
- <Link to="/products" className={buttonVariants({ size: 'lg' })}>
67
- Ir al catálogo
68
- </Link>
69
- </div>
70
- </div>
71
- </section>
72
- )
73
- }
@@ -1,101 +0,0 @@
1
- import type { ComponentType, ReactNode, SVGProps } from 'react'
2
- import { cn } from '../../ui'
3
- import type { CheckoutStepId } from './checkout-steps'
4
- import { checkoutStepStatus, type CheckoutStepFlow } from './checkout-steps'
5
-
6
- type StepIcon = ComponentType<SVGProps<SVGSVGElement>>
7
-
8
- export function CheckoutStepCard({
9
- step,
10
- flow,
11
- title,
12
- icon: Icon,
13
- summary,
14
- onEdit,
15
- children,
16
- }: {
17
- step: CheckoutStepId
18
- flow: CheckoutStepFlow
19
- title: string
20
- icon: StepIcon
21
- summary?: ReactNode
22
- onEdit?: () => void
23
- children?: ReactNode
24
- }) {
25
- const status = checkoutStepStatus(flow, step)
26
- const locked = status === 'locked'
27
- const complete = status === 'complete'
28
- const active = status === 'active'
29
-
30
- return (
31
- <section
32
- aria-labelledby={`checkout-step-${step}-title`}
33
- aria-disabled={locked || undefined}
34
- className={cn(
35
- 'overflow-hidden rounded-xl border bg-card shadow-sm transition-opacity',
36
- locked && 'opacity-55',
37
- )}
38
- >
39
- <header
40
- className={cn(
41
- 'flex items-center gap-3 border-b px-4 py-3.5 sm:px-5',
42
- locked && 'border-transparent',
43
- complete && 'border-border',
44
- active && 'border-border',
45
- )}
46
- >
47
- <Icon
48
- className={cn(
49
- 'size-5 shrink-0',
50
- locked ? 'text-muted-foreground/70' : 'text-foreground',
51
- )}
52
- aria-hidden="true"
53
- />
54
- <h2
55
- id={`checkout-step-${step}-title`}
56
- className={cn(
57
- 'min-w-0 flex-1 text-base font-semibold tracking-tight',
58
- locked && 'text-muted-foreground',
59
- )}
60
- >
61
- {title}
62
- </h2>
63
- {complete && onEdit ? (
64
- <button
65
- type="button"
66
- className="text-xs font-semibold uppercase tracking-[0.08em] text-foreground underline-offset-4 hover:underline"
67
- onClick={onEdit}
68
- >
69
- Editar
70
- </button>
71
- ) : null}
72
- </header>
73
-
74
- {complete && summary ? (
75
- <div className="space-y-3 px-4 py-4 sm:px-5">{summary}</div>
76
- ) : null}
77
-
78
- {active ? <div className="space-y-5 px-4 py-5 sm:px-5">{children}</div> : null}
79
- </section>
80
- )
81
- }
82
-
83
- export function CheckoutStepSummaryBlock({
84
- children,
85
- className,
86
- }: {
87
- children: ReactNode
88
- className?: string
89
- }) {
90
- return (
91
- <div
92
- className={cn(
93
- 'relative rounded-lg border border-border bg-background py-3 pl-4 pr-3',
94
- 'before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-foreground',
95
- className,
96
- )}
97
- >
98
- {children}
99
- </div>
100
- )
101
- }