@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,379 +0,0 @@
1
- import { useId, useRef, useState, type FormEvent } from 'react'
2
- import { z } from 'zod'
3
- import {
4
- customerProfileToPatch,
5
- type CustomerProfile,
6
- type CustomerProfilePatch,
7
- } from '@ecomiq/core'
8
- import { Loader2, Pencil } from 'lucide-react'
9
- import { Button, cn } from '../../ui'
10
- import {
11
- CheckoutSelectField,
12
- CheckoutTextField,
13
- FieldError,
14
- } from '../checkout/checkout-form-controls'
15
-
16
- const IDENTIFICATION_TYPES = ['DNI', 'CE', 'Pasaporte', 'RUC'] as const
17
-
18
- const nameSchema = z.object({
19
- firstName: z.string().trim().min(1, 'Nombre requerido').max(50),
20
- lastName: z.string().trim().min(1, 'Apellido requerido').max(50),
21
- })
22
-
23
- const phoneSchema = z.object({
24
- phone: z.string().trim().min(6, 'Ingresa un número válido'),
25
- })
26
-
27
- const documentSchema = z.object({
28
- identificationType: z.string().min(1, 'Selecciona el tipo'),
29
- identificationNumber: z.string().trim().min(1, 'Número de documento requerido'),
30
- })
31
-
32
- type EditSection = 'name' | 'phone' | 'document' | null
33
-
34
- function formatDocument(profile: CustomerProfile): string {
35
- if (profile.identificationType && profile.identificationNumber) {
36
- return `${profile.identificationType} · ${profile.identificationNumber}`
37
- }
38
- if (profile.identificationNumber) return profile.identificationNumber
39
- return 'Sin registrar'
40
- }
41
-
42
- export type CustomerProfilePanelProps = {
43
- profile?: CustomerProfile | null
44
- isLoading: boolean
45
- errorMessage?: string | null
46
- onUpdateProfile: (payload: CustomerProfilePatch) => Promise<void>
47
- }
48
-
49
- export function CustomerProfilePanel({
50
- profile,
51
- isLoading,
52
- errorMessage,
53
- onUpdateProfile,
54
- }: CustomerProfilePanelProps) {
55
- const formId = useId()
56
- const dialogRef = useRef<HTMLDialogElement>(null)
57
- const [editSection, setEditSection] = useState<EditSection>(null)
58
- const [saving, setSaving] = useState(false)
59
- const [formError, setFormError] = useState<string | null>(null)
60
-
61
- const [firstName, setFirstName] = useState('')
62
- const [lastName, setLastName] = useState('')
63
- const [phone, setPhone] = useState('')
64
- const [identificationType, setIdentificationType] = useState('DNI')
65
- const [identificationNumber, setIdentificationNumber] = useState('')
66
- const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({})
67
-
68
- const openEdit = (section: Exclude<EditSection, null>) => {
69
- if (!profile) return
70
- setEditSection(section)
71
- setFormError(null)
72
- setFieldErrors({})
73
- if (section === 'name') {
74
- setFirstName(profile.firstName)
75
- setLastName(profile.lastName)
76
- } else if (section === 'phone') {
77
- setPhone(profile.phone || '')
78
- } else {
79
- setIdentificationType(profile.identificationType || 'DNI')
80
- setIdentificationNumber(profile.identificationNumber || '')
81
- }
82
- dialogRef.current?.showModal()
83
- }
84
-
85
- const closeEdit = () => {
86
- dialogRef.current?.close()
87
- setEditSection(null)
88
- setFormError(null)
89
- setFieldErrors({})
90
- }
91
-
92
- const savePatch = async (partial: Partial<CustomerProfilePatch>) => {
93
- if (!profile) return
94
- try {
95
- setSaving(true)
96
- setFormError(null)
97
- await onUpdateProfile({
98
- ...customerProfileToPatch(profile),
99
- ...partial,
100
- })
101
- closeEdit()
102
- } catch (error) {
103
- setFormError(
104
- error instanceof Error
105
- ? error.message
106
- : 'No se pudieron guardar los cambios',
107
- )
108
- } finally {
109
- setSaving(false)
110
- }
111
- }
112
-
113
- const handleSubmit = (event: FormEvent) => {
114
- event.preventDefault()
115
- if (editSection === 'name') {
116
- const parsed = nameSchema.safeParse({ firstName, lastName })
117
- if (!parsed.success) {
118
- const next: Record<string, string> = {}
119
- for (const issue of parsed.error.issues) {
120
- const key = issue.path[0]
121
- if (typeof key === 'string' && !(key in next)) next[key] = issue.message
122
- }
123
- setFieldErrors(next)
124
- return
125
- }
126
- void savePatch(parsed.data)
127
- return
128
- }
129
- if (editSection === 'phone') {
130
- const parsed = phoneSchema.safeParse({ phone })
131
- if (!parsed.success) {
132
- setFieldErrors({ phone: parsed.error.issues[0]?.message ?? 'Inválido' })
133
- return
134
- }
135
- void savePatch(parsed.data)
136
- return
137
- }
138
- if (editSection === 'document') {
139
- const parsed = documentSchema.safeParse({
140
- identificationType,
141
- identificationNumber,
142
- })
143
- if (!parsed.success) {
144
- const next: Record<string, string> = {}
145
- for (const issue of parsed.error.issues) {
146
- const key = issue.path[0]
147
- if (typeof key === 'string' && !(key in next)) next[key] = issue.message
148
- }
149
- setFieldErrors(next)
150
- return
151
- }
152
- void savePatch(parsed.data)
153
- }
154
- }
155
-
156
- const fullName = profile
157
- ? `${profile.firstName} ${profile.lastName}`.trim() || 'Cliente'
158
- : '—'
159
-
160
- const dialogTitle =
161
- editSection === 'name'
162
- ? 'Editar nombre'
163
- : editSection === 'phone'
164
- ? 'Editar celular'
165
- : 'Documento de identidad'
166
-
167
- if (isLoading) {
168
- return (
169
- <div className="flex items-center gap-2 rounded-lg border border-border bg-background px-4 py-8 text-sm text-muted-foreground">
170
- <Loader2 className="size-4 shrink-0 animate-spin" />
171
- Cargando datos personales…
172
- </div>
173
- )
174
- }
175
-
176
- return (
177
- <div className="space-y-6">
178
- <div>
179
- <h2 className="text-lg font-medium text-foreground">Datos personales</h2>
180
- <p className="mt-1 text-sm text-muted-foreground">
181
- Administra tu nombre, documento y contacto para pedidos y facturación.
182
- </p>
183
- </div>
184
-
185
- {errorMessage ? (
186
- <p className="rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive">
187
- {errorMessage}
188
- </p>
189
- ) : null}
190
-
191
- <div className="overflow-hidden rounded-lg border border-border bg-background">
192
- <ProfileRow
193
- label="Nombre y apellidos"
194
- value={fullName}
195
- onEdit={() => openEdit('name')}
196
- disabled={!profile}
197
- />
198
- <ProfileRow
199
- label="Tipo de documento"
200
- value={profile ? formatDocument(profile) : 'Sin registrar'}
201
- onEdit={() => openEdit('document')}
202
- disabled={!profile}
203
- />
204
- <ProfileRow
205
- label="Celular"
206
- value={profile?.phone?.trim() ? profile.phone : 'Sin registrar'}
207
- onEdit={() => openEdit('phone')}
208
- disabled={!profile}
209
- />
210
- <ProfileRow
211
- label="Correo"
212
- value={profile?.email ?? '—'}
213
- editable={false}
214
- isLast
215
- />
216
- </div>
217
-
218
- <dialog
219
- ref={dialogRef}
220
- className="fixed left-1/2 top-1/2 z-50 m-0 w-[min(100%-2rem,28rem)] -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-background p-0 shadow-lg backdrop:bg-black/40"
221
- onClose={() => {
222
- setEditSection(null)
223
- setFormError(null)
224
- setFieldErrors({})
225
- }}
226
- >
227
- <form className="space-y-4 p-6" onSubmit={handleSubmit}>
228
- <h3 className="text-base font-medium text-foreground">{dialogTitle}</h3>
229
-
230
- {editSection === 'name' ? (
231
- <div className="grid gap-4 sm:grid-cols-2">
232
- <CheckoutTextField
233
- id={`${formId}-firstName`}
234
- label="Nombre"
235
- value={firstName}
236
- error={fieldErrors.firstName}
237
- autoComplete="given-name"
238
- onChange={(v) => {
239
- setFieldErrors((prev) => {
240
- const next = { ...prev }
241
- delete next.firstName
242
- return next
243
- })
244
- setFirstName(v)
245
- }}
246
- />
247
- <CheckoutTextField
248
- id={`${formId}-lastName`}
249
- label="Apellidos"
250
- value={lastName}
251
- error={fieldErrors.lastName}
252
- autoComplete="family-name"
253
- onChange={(v) => {
254
- setFieldErrors((prev) => {
255
- const next = { ...prev }
256
- delete next.lastName
257
- return next
258
- })
259
- setLastName(v)
260
- }}
261
- />
262
- </div>
263
- ) : null}
264
-
265
- {editSection === 'phone' ? (
266
- <CheckoutTextField
267
- id={`${formId}-phone`}
268
- label="Celular"
269
- value={phone}
270
- error={fieldErrors.phone}
271
- placeholder="+51999999999"
272
- autoComplete="tel"
273
- onChange={(v) => {
274
- setFieldErrors((prev) => {
275
- const next = { ...prev }
276
- delete next.phone
277
- return next
278
- })
279
- setPhone(v)
280
- }}
281
- />
282
- ) : null}
283
-
284
- {editSection === 'document' ? (
285
- <div className="grid gap-4">
286
- <CheckoutSelectField
287
- id={`${formId}-identificationType`}
288
- label="Tipo"
289
- value={identificationType}
290
- error={fieldErrors.identificationType}
291
- options={IDENTIFICATION_TYPES.map((t) => ({
292
- value: t,
293
- label: t,
294
- }))}
295
- onChange={(v) => {
296
- setFieldErrors((prev) => {
297
- const next = { ...prev }
298
- delete next.identificationType
299
- return next
300
- })
301
- setIdentificationType(v)
302
- }}
303
- />
304
- <CheckoutTextField
305
- id={`${formId}-identificationNumber`}
306
- label="Número"
307
- value={identificationNumber}
308
- error={fieldErrors.identificationNumber}
309
- placeholder="12345678"
310
- onChange={(v) => {
311
- setFieldErrors((prev) => {
312
- const next = { ...prev }
313
- delete next.identificationNumber
314
- return next
315
- })
316
- setIdentificationNumber(v)
317
- }}
318
- />
319
- </div>
320
- ) : null}
321
-
322
- {formError ? <FieldError message={formError} /> : null}
323
-
324
- <div className="flex flex-wrap justify-end gap-2 pt-2">
325
- <Button type="button" variant="outline" onClick={closeEdit}>
326
- Cancelar
327
- </Button>
328
- <Button type="submit" disabled={saving}>
329
- {saving ? <Loader2 className="size-4 animate-spin" /> : 'Guardar'}
330
- </Button>
331
- </div>
332
- </form>
333
- </dialog>
334
- </div>
335
- )
336
- }
337
-
338
- function ProfileRow({
339
- label,
340
- value,
341
- onEdit,
342
- editable = true,
343
- disabled = false,
344
- isLast = false,
345
- }: {
346
- label: string
347
- value: string
348
- onEdit?: () => void
349
- editable?: boolean
350
- disabled?: boolean
351
- isLast?: boolean
352
- }) {
353
- return (
354
- <div
355
- className={cn(
356
- 'flex items-start justify-between gap-4 px-5 py-5',
357
- !isLast && 'border-b border-border',
358
- )}
359
- >
360
- <div className="min-w-0">
361
- <p className="text-sm font-medium text-foreground">{label}</p>
362
- <p className="mt-1 break-words text-sm text-muted-foreground">{value}</p>
363
- </div>
364
- {editable && onEdit ? (
365
- <Button
366
- type="button"
367
- variant="ghost"
368
- size="sm"
369
- className="shrink-0 gap-1"
370
- onClick={onEdit}
371
- disabled={disabled}
372
- >
373
- <Pencil className="size-3.5" />
374
- Editar
375
- </Button>
376
- ) : null}
377
- </div>
378
- )
379
- }
@@ -1,143 +0,0 @@
1
- import type { StorefrontCart, StorefrontCartItem } from '@ecomiq/core'
2
-
3
- export type CartLineSignatureItem = {
4
- id: string
5
- variantId: string
6
- quantity: number
7
- }
8
-
9
- export type CartReconcileAction =
10
- | { action: 'add'; variantId: string; quantity: number }
11
- | { action: 'update'; itemId: string; quantity: number }
12
- | { action: 'remove'; itemId: string }
13
-
14
- export function cartItemsSignature(items: CartLineSignatureItem[]): string {
15
- return items
16
- .map((i) => `${i.variantId}:${i.quantity}`)
17
- .sort()
18
- .join('|')
19
- }
20
-
21
- export function toSignatureItems(
22
- items: StorefrontCartItem[] | undefined,
23
- ): CartLineSignatureItem[] {
24
- return (items ?? []).map((item) => ({
25
- id: item.id,
26
- variantId: String(item.variantId),
27
- quantity: item.quantity,
28
- }))
29
- }
30
-
31
- export function areCartSignaturesEqual(
32
- a: CartLineSignatureItem[],
33
- b: CartLineSignatureItem[],
34
- ): boolean {
35
- return cartItemsSignature(a) === cartItemsSignature(b)
36
- }
37
-
38
- export function cartSessionSignature(
39
- cartTokenId: string | null | undefined,
40
- items: CartLineSignatureItem[],
41
- ): string {
42
- return `${cartTokenId ?? ''}|${cartItemsSignature(items)}`
43
- }
44
-
45
- function groupQuantitiesByVariant(items: CartLineSignatureItem[]): Map<string, number> {
46
- const map = new Map<string, number>()
47
- for (const item of items) {
48
- map.set(item.variantId, (map.get(item.variantId) ?? 0) + Math.max(0, item.quantity))
49
- }
50
- return map
51
- }
52
-
53
- function groupLinesByVariant(
54
- items: CartLineSignatureItem[],
55
- ): Map<string, CartLineSignatureItem[]> {
56
- const map = new Map<string, CartLineSignatureItem[]>()
57
- for (const item of items) {
58
- const lines = map.get(item.variantId) ?? []
59
- lines.push(item)
60
- map.set(item.variantId, lines)
61
- }
62
- return map
63
- }
64
-
65
- /** Acciones para alinear el API con el carrito local (fuente de verdad). */
66
- export function findCartReconciliationActions(
67
- local: CartLineSignatureItem[],
68
- api: CartLineSignatureItem[],
69
- ): CartReconcileAction[] {
70
- const actions: CartReconcileAction[] = []
71
- const localQty = groupQuantitiesByVariant(local)
72
- const apiLinesByVariant = groupLinesByVariant(api)
73
- const variantIds = new Set([...localQty.keys(), ...apiLinesByVariant.keys()])
74
-
75
- for (const variantId of variantIds) {
76
- const targetQty = localQty.get(variantId) ?? 0
77
- const apiLines = apiLinesByVariant.get(variantId) ?? []
78
- const apiQty = apiLines.reduce((sum, line) => sum + line.quantity, 0)
79
-
80
- if (targetQty > apiQty) {
81
- actions.push({
82
- action: 'add',
83
- variantId,
84
- quantity: targetQty - apiQty,
85
- })
86
- continue
87
- }
88
-
89
- if (targetQty >= apiQty) continue
90
-
91
- let excess = apiQty - targetQty
92
- for (const line of [...apiLines].reverse()) {
93
- if (excess <= 0) break
94
- if (line.quantity <= excess) {
95
- actions.push({ action: 'remove', itemId: line.id })
96
- excess -= line.quantity
97
- } else {
98
- actions.push({
99
- action: 'update',
100
- itemId: line.id,
101
- quantity: line.quantity - excess,
102
- })
103
- excess = 0
104
- }
105
- }
106
- }
107
-
108
- return actions
109
- }
110
-
111
- export function isCheckoutCartAlreadyPurchased(
112
- cart: StorefrontCart | undefined,
113
- ): boolean {
114
- if (!cart) return false
115
- if (cart.completedAt) return true
116
- return cart.status === 'completed'
117
- }
118
-
119
- const PROVIDER_CHECKOUT_UI = new Set(['stripe', 'mercadopago', 'mercado_pago'])
120
-
121
- function normalizeProviderKey(raw: string): string {
122
- return raw.trim().toLowerCase().replace(/[\s-]+/g, '_')
123
- }
124
-
125
- export function methodUsesProviderCheckoutUi(method: {
126
- isManual: boolean
127
- kind: string
128
- code: string
129
- providerMethodCode?: string | null
130
- }): boolean {
131
- if (method.isManual) return false
132
- const keys = [method.providerMethodCode, method.kind, method.code].filter(
133
- Boolean,
134
- ) as string[]
135
- return keys.some((key) => {
136
- const normalized = normalizeProviderKey(key)
137
- return (
138
- PROVIDER_CHECKOUT_UI.has(normalized) ||
139
- normalized.includes('stripe') ||
140
- normalized.includes('mercado')
141
- )
142
- })
143
- }
@@ -1,76 +0,0 @@
1
- import { Link } from '../../link'
2
- import { Button, buttonVariants, cn } from '../../ui'
3
- import type { CheckoutFieldId } from './form-schema'
4
- import { CheckoutTextField } from './checkout-form-controls'
5
-
6
- function checkoutAuthRedirect(): string {
7
- if (typeof window === 'undefined') return '/checkout'
8
- return `${window.location.pathname}${window.location.search}`
9
- }
10
-
11
- /** Paso 1: Mis datos (email). */
12
- export function CheckoutContactSection({
13
- email,
14
- fieldError,
15
- sessionLabel,
16
- isSaving,
17
- onEmailChange,
18
- onClearError,
19
- onContinueAsGuest,
20
- }: {
21
- email: string
22
- fieldError?: string
23
- sessionLabel?: string | null
24
- isSaving?: boolean
25
- onEmailChange: (value: string) => void
26
- onClearError: (id: CheckoutFieldId) => void
27
- onContinueAsGuest: () => void | Promise<void>
28
- }) {
29
- const authRedirect = checkoutAuthRedirect()
30
-
31
- return (
32
- <div className="space-y-5">
33
- {sessionLabel ? (
34
- <p className="text-sm text-muted-foreground">
35
- Sesión como <span className="font-medium text-foreground">{sessionLabel}</span>
36
- </p>
37
- ) : null}
38
-
39
- <CheckoutTextField
40
- id="email"
41
- label="Correo electrónico"
42
- error={fieldError}
43
- value={email}
44
- onChange={(v) => {
45
- onClearError('email')
46
- onEmailChange(v)
47
- }}
48
- type="email"
49
- autoComplete="email"
50
- />
51
-
52
- <div className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center">
53
- {!sessionLabel ? (
54
- <>
55
- <Link
56
- to="/login"
57
- search={{ redirect: authRedirect }}
58
- className={cn(buttonVariants({ variant: 'default' }), 'uppercase tracking-wide')}
59
- >
60
- ¿Ya tienes una cuenta?
61
- </Link>
62
- </>
63
- ) : null}
64
- <Button
65
- type="button"
66
- variant={sessionLabel ? 'default' : 'outline'}
67
- className="uppercase tracking-wide"
68
- disabled={isSaving}
69
- onClick={() => void onContinueAsGuest()}
70
- >
71
- {sessionLabel ? 'Continuar' : 'Continuar como invitado'}
72
- </Button>
73
- </div>
74
- </div>
75
- )
76
- }
@@ -1,15 +0,0 @@
1
- import { Button } from '../../ui'
2
-
3
- export function CheckoutExternalUpdateBanner({ onReload }: { onReload: () => void }) {
4
- return (
5
- <div
6
- className="mb-6 flex flex-wrap items-center justify-between gap-3 rounded-lg border border-amber-300/80 bg-amber-50 px-4 py-3 text-sm text-amber-950"
7
- role="alert"
8
- >
9
- <p>Tu carrito se acaba de actualizar</p>
10
- <Button type="button" size="sm" onClick={onReload}>
11
- Recargar checkout
12
- </Button>
13
- </div>
14
- )
15
- }