@ecomiq/storefront 2.1.4 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -76
  3. package/dist/account-Dx4e3syi.js +258 -0
  4. package/dist/account-forms-AVk71ko7.js +39 -0
  5. package/dist/account-forms.d.ts +13 -0
  6. package/dist/account.d.ts +240 -0
  7. package/dist/address-defaults-BqjRhS82.js +14 -0
  8. package/dist/address-defaults.d.ts +11 -0
  9. package/dist/analytics/analytics-CG1Qagwl.js +148 -0
  10. package/dist/analytics/analytics.d.ts +1 -0
  11. package/dist/analytics/config-DJr-GRhs.js +62 -0
  12. package/dist/analytics/config.d.ts +22 -0
  13. package/dist/analytics/consent-B13rBpBs.js +57 -0
  14. package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
  15. package/dist/analytics/consent-banner.d.ts +20 -0
  16. package/dist/analytics/cookies-pLozllyB.js +100 -0
  17. package/dist/analytics/cookies.d.ts +6 -0
  18. package/dist/analytics/destinations-iECrlLIr.js +144 -0
  19. package/dist/analytics/events-Dt81tIp1.js +102 -0
  20. package/dist/analytics/events.d.ts +51 -0
  21. package/dist/analytics/tracker-p4yaoOyk.js +51 -0
  22. package/dist/api-cache-DsuIn5hX.js +159 -0
  23. package/dist/bank-accounts-DlEczQQ5.js +81 -0
  24. package/dist/bank-accounts.d.ts +4 -0
  25. package/dist/captcha-DAqaIx8m.js +117 -0
  26. package/dist/captcha.d.ts +34 -0
  27. package/dist/cart-D7DxZ2lD.js +30 -0
  28. package/dist/cart-actions-Bsub_FFG.js +116 -0
  29. package/dist/cart-actions.d.ts +15 -0
  30. package/dist/cart-drawer-B7PPsR69.js +22 -0
  31. package/dist/cart-drawer.d.ts +9 -0
  32. package/dist/cart-form-y9P1r_EX.js +81 -0
  33. package/dist/cart-form.d.ts +24 -0
  34. package/dist/cart-summary-ur2VWKr-.js +89 -0
  35. package/dist/cart-summary.d.ts +31 -0
  36. package/dist/cart.d.ts +4 -0
  37. package/dist/catalog-7gJ-l5hW.js +67 -0
  38. package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
  39. package/dist/catalog-hooks.d.ts +111 -0
  40. package/dist/catalog.d.ts +5 -0
  41. package/dist/checkout-DVWSXANi.js +474 -0
  42. package/dist/checkout-form-CbOIdENC.js +131 -0
  43. package/dist/checkout-form.d.ts +20 -0
  44. package/dist/checkout.d.ts +86 -0
  45. package/dist/complaint-book-BvwLvfx0.js +117 -0
  46. package/dist/complaint-book.d.ts +62 -0
  47. package/dist/culqi-authentication-BGr20vAJ.js +204 -0
  48. package/dist/culqi-authentication.d.ts +15 -0
  49. package/dist/culqi-checkout-CTXpKgp_.js +55 -0
  50. package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
  51. package/dist/culqi-three-ds.d.ts +29 -0
  52. package/dist/customer-avatar-CJXSDroK.js +12 -0
  53. package/dist/customer-avatar.d.ts +5 -0
  54. package/dist/customer-cookies-DHOgFAMV.js +45 -0
  55. package/dist/customer-csrf-C1oDX-Py.js +17 -0
  56. package/dist/customer-csrf.d.ts +3 -0
  57. package/dist/customer-document-fields-C1K-_DAD.js +70 -0
  58. package/dist/customer-document-fields.d.ts +9 -0
  59. package/dist/customer-login-form-C7AC6vXd.js +144 -0
  60. package/dist/customer-login-form.d.ts +17 -0
  61. package/dist/customer-registration-form-BEm_NEfP.js +93 -0
  62. package/dist/customer-registration-form.d.ts +8 -0
  63. package/dist/document-url-CGsAl5Tf.js +9 -0
  64. package/dist/documents-T1mzc_j8.js +38 -0
  65. package/dist/documents.d.ts +16 -0
  66. package/dist/entries/account.d.ts +11 -0
  67. package/dist/entries/account.js +10 -0
  68. package/dist/entries/analytics.d.ts +6 -0
  69. package/dist/entries/analytics.js +5 -0
  70. package/dist/entries/cart.d.ts +8 -0
  71. package/dist/entries/cart.js +7 -0
  72. package/dist/entries/checkout.d.ts +12 -0
  73. package/dist/entries/checkout.js +11 -0
  74. package/dist/entries/order.d.ts +11 -0
  75. package/dist/entries/order.js +11 -0
  76. package/dist/favicon-YpyhVJ9H.js +28 -0
  77. package/dist/favicon.d.ts +4 -0
  78. package/dist/form-BtQcbXlJ.js +18 -0
  79. package/dist/form-contracts-BNpHaTGJ.js +166 -0
  80. package/dist/form-contracts.d.ts +42 -0
  81. package/dist/form.d.ts +3 -0
  82. package/dist/hidden-CCWtOWKR.js +3 -0
  83. package/dist/hidden.d.ts +4 -0
  84. package/dist/index.d.ts +50 -0
  85. package/dist/index.js +50 -0
  86. package/dist/invoice-fields-BG0HjL6T.js +103 -0
  87. package/dist/invoice-fields.d.ts +39 -0
  88. package/dist/links-kFxBZfdb.js +96 -0
  89. package/dist/links.d.ts +21 -0
  90. package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
  91. package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
  92. package/dist/mercadopago-dialog.d.ts +21 -0
  93. package/dist/money-DtGOtDri.js +32 -0
  94. package/dist/money.d.ts +1 -0
  95. package/dist/optimistic-cart-BveVOHuh.js +53 -0
  96. package/dist/optimistic-cart.d.ts +16 -0
  97. package/dist/order-display-DTt5WBbc.js +112 -0
  98. package/dist/order-display.d.ts +20 -0
  99. package/dist/order-draft-DxV1Uhm3.js +38 -0
  100. package/dist/order-draft.d.ts +16 -0
  101. package/dist/order-pages-Dm6l5m0m.js +38 -0
  102. package/dist/order-pages.d.ts +12 -0
  103. package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
  104. package/dist/order-payment-continuation.d.ts +10 -0
  105. package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
  106. package/dist/order-payment-in-review.d.ts +5 -0
  107. package/dist/order-status-BJXLJwxq.js +162 -0
  108. package/dist/order-status.d.ts +89 -0
  109. package/dist/order-view-9IlBg5AF.js +62 -0
  110. package/dist/order-view.d.ts +12 -0
  111. package/dist/page-action-Ctkf4xC6.js +7 -0
  112. package/dist/page-seo-a5FJl8x5.js +262 -0
  113. package/dist/page-seo.d.ts +34 -0
  114. package/dist/pages-Ca_Gt6mq.js +150 -0
  115. package/dist/pages.d.ts +45 -0
  116. package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
  117. package/dist/payment-logo-BoPaGJc_.js +46 -0
  118. package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
  119. package/dist/payment-logo.d.ts +11 -0
  120. package/dist/payment-logos-BYvdwYyJ.js +23 -0
  121. package/dist/payment-methods-B8ocTgFZ.js +73 -0
  122. package/dist/payment-methods.d.ts +58 -0
  123. package/dist/payment-return-5cjypwIw.js +7 -0
  124. package/dist/place-CmW4hBWY.js +13 -0
  125. package/dist/place.d.ts +4 -0
  126. package/dist/preview-BiIBif3C.js +427 -0
  127. package/dist/preview-address-Cf0sJ-9p.js +21 -0
  128. package/dist/preview-cart-Dg2CVktS.js +376 -0
  129. package/dist/product-preview-BspMAecJ.js +51 -0
  130. package/dist/product-preview.d.ts +5 -0
  131. package/dist/product-variants-BNDHvs1y.js +46 -0
  132. package/dist/product-variants.d.ts +12 -0
  133. package/dist/public-pages-XtwlaLwt.js +194 -0
  134. package/dist/public-pages.d.ts +67 -0
  135. package/dist/registration-CpDcv8ql.js +91 -0
  136. package/dist/registration.d.ts +12 -0
  137. package/dist/request-security-CRsRMgwj.js +76 -0
  138. package/dist/rich-text-M-Ju8RL4.js +9 -0
  139. package/dist/rich-text.d.ts +5 -0
  140. package/dist/route-text-DcJw2SsK.js +9 -0
  141. package/dist/route-text.d.ts +4 -0
  142. package/dist/section-preview-CmsMl3yu.js +83 -0
  143. package/dist/section-preview.d.ts +12 -0
  144. package/dist/sections/account-addresses.d.ts +18 -0
  145. package/dist/sections/account-addresses.js +375 -0
  146. package/dist/sections/account-order.d.ts +12 -0
  147. package/dist/sections/account-order.js +592 -0
  148. package/dist/sections/account-orders.d.ts +8 -0
  149. package/dist/sections/account-orders.js +359 -0
  150. package/dist/sections/account-profile.d.ts +16 -0
  151. package/dist/sections/account-profile.js +123 -0
  152. package/dist/sections/checkout.d.ts +24 -0
  153. package/dist/sections/checkout.js +963 -0
  154. package/dist/sections/complaint-book.d.ts +8 -0
  155. package/dist/sections/complaint-book.js +538 -0
  156. package/dist/sections/design-system.d.ts +1 -0
  157. package/dist/sections/design-system.js +310 -0
  158. package/dist/sections/draft-invoice.d.ts +20 -0
  159. package/dist/sections/draft-invoice.js +158 -0
  160. package/dist/sections/order-tracking.d.ts +19 -0
  161. package/dist/sections/order-tracking.js +141 -0
  162. package/dist/sections-CNUMWTF_.js +77 -0
  163. package/dist/sections.d.ts +19 -0
  164. package/dist/seo-GpDRhAAA.js +78 -0
  165. package/dist/session-CKvmFJyg.js +379 -0
  166. package/dist/session.d.ts +24 -0
  167. package/dist/settings-B0NO2A6Y.js +78 -0
  168. package/dist/settings.d.ts +51 -0
  169. package/dist/source-marker-DhqSkW7n.js +4 -0
  170. package/dist/sources-B-3JFJfb.js +46 -0
  171. package/dist/sources.d.ts +7 -0
  172. package/dist/store-8Q-_LFrQ.js +152 -0
  173. package/dist/store-contact--hY6CoFb.js +19 -0
  174. package/dist/store-contact.d.ts +6 -0
  175. package/dist/store.d.ts +17 -0
  176. package/dist/storefront-language-DrMjVbmk.js +29 -0
  177. package/dist/storefront-language.d.ts +14 -0
  178. package/dist/storefront-text-D25ae1qf.js +11 -0
  179. package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
  180. package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
  181. package/dist/storefront-text.d.ts +4 -0
  182. package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
  183. package/dist/stripe-dialog.d.ts +30 -0
  184. package/dist/telemetry-HuZXWBPi.js +114 -0
  185. package/dist/telemetry.d.ts +10 -0
  186. package/dist/templates-CAqoNyTW.js +40 -0
  187. package/dist/templates.d.ts +15 -0
  188. package/dist/urls-CpGQPxvf.js +71 -0
  189. package/dist/urls.d.ts +18 -0
  190. package/dist/use-checkout-CZcxXJXi.js +42 -0
  191. package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
  192. package/dist/use-checkout-payment.d.ts +18 -0
  193. package/dist/use-checkout.d.ts +36 -0
  194. package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
  195. package/dist/use-order-payment-BKIz_IO0.js +45 -0
  196. package/dist/use-order-payment.d.ts +18 -0
  197. package/dist/use-payment-token-CErx-tN-.js +105 -0
  198. package/dist/vite-payment-logos-kziu7wKb.js +27 -0
  199. package/dist/vite-payment-logos.d.ts +24 -0
  200. package/dist/vite-public-assets-DW2fgtVb.js +209 -0
  201. package/dist/vite-public-assets.d.ts +29 -0
  202. package/dist/vite-sections-DTBr0-Kg.js +464 -0
  203. package/dist/vite-sections.d.ts +21 -0
  204. package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
  205. package/dist/vite-theme-ui.d.ts +10 -0
  206. package/dist/vite.d.ts +36 -0
  207. package/dist/vite.js +127 -0
  208. package/dist/voucher-payment-CVFDZ1L8.js +66 -0
  209. package/dist/worker-CNi-gzCu.js +86 -0
  210. package/dist/worker.d.ts +27 -0
  211. package/package.json +100 -76
  212. package/src/sections/account-addresses.tsx +215 -0
  213. package/src/sections/account-order.tsx +424 -0
  214. package/src/sections/account-orders.tsx +259 -0
  215. package/src/sections/account-profile.tsx +70 -0
  216. package/src/sections/checkout.tsx +586 -0
  217. package/src/sections/complaint-book.tsx +325 -0
  218. package/src/sections/design-system.tsx +146 -0
  219. package/src/sections/draft-invoice.tsx +105 -0
  220. package/src/sections/order-tracking.tsx +81 -0
  221. package/src/vite-client.d.ts +4 -0
  222. package/bin/ecomiq.mjs +0 -179
  223. package/public/design-bridge.js +0 -318
  224. package/src/api-proxy.ts +0 -194
  225. package/src/auth/create-storefront-auth.ts +0 -94
  226. package/src/auth/ecomiq-oauth-password-client.ts +0 -26
  227. package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
  228. package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
  229. package/src/auth/ecomiq-oauth-password-types.ts +0 -24
  230. package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
  231. package/src/auth/env.ts +0 -7
  232. package/src/auth/secure-cookies.ts +0 -23
  233. package/src/auth/storefront-auth-base-url.ts +0 -117
  234. package/src/auth/storefront-auth.ts +0 -7
  235. package/src/cart-session.ts +0 -239
  236. package/src/client.ts +0 -48
  237. package/src/codegen.mjs +0 -801
  238. package/src/components/account/customer-addresses-panel.tsx +0 -678
  239. package/src/components/account/customer-order-detail-panel.tsx +0 -285
  240. package/src/components/account/customer-orders-panel.tsx +0 -192
  241. package/src/components/account/customer-profile-panel.tsx +0 -379
  242. package/src/components/checkout/cart-sync.ts +0 -143
  243. package/src/components/checkout/checkout-contact-section.tsx +0 -76
  244. package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
  245. package/src/components/checkout/checkout-form-controls.tsx +0 -139
  246. package/src/components/checkout/checkout-line-items.tsx +0 -43
  247. package/src/components/checkout/checkout-order-summary.tsx +0 -165
  248. package/src/components/checkout/checkout-payment-section.tsx +0 -212
  249. package/src/components/checkout/checkout-session-view.tsx +0 -564
  250. package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
  251. package/src/components/checkout/checkout-shipping-section.tsx +0 -98
  252. package/src/components/checkout/checkout-states.tsx +0 -73
  253. package/src/components/checkout/checkout-step-card.tsx +0 -101
  254. package/src/components/checkout/checkout-steps.ts +0 -111
  255. package/src/components/checkout/form-schema.ts +0 -281
  256. package/src/components/checkout/format.ts +0 -15
  257. package/src/components/checkout/index.ts +0 -5
  258. package/src/components/checkout/map-customer-to-checkout.ts +0 -188
  259. package/src/components/checkout/types.ts +0 -41
  260. package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
  261. package/src/components/checkout/use-checkout-geo.ts +0 -50
  262. package/src/components/checkout/use-checkout-session.ts +0 -640
  263. package/src/components/hero.tsx +0 -291
  264. package/src/config.d.ts +0 -94
  265. package/src/config.mjs +0 -146
  266. package/src/format-money.ts +0 -31
  267. package/src/health.ts +0 -90
  268. package/src/hooks/index.ts +0 -9
  269. package/src/hooks/recently-viewed.ts +0 -125
  270. package/src/index.ts +0 -349
  271. package/src/link.tsx +0 -2
  272. package/src/pages/account/account-page-skeleton.tsx +0 -69
  273. package/src/pages/account/account-page-view.tsx +0 -270
  274. package/src/pages/account/account-shell.tsx +0 -101
  275. package/src/pages/account/parse-account-route.ts +0 -36
  276. package/src/pages/account.tsx +0 -16
  277. package/src/pages/api.$.ts +0 -20
  278. package/src/pages/api.auth.$.ts +0 -16
  279. package/src/pages/checkout.$checkoutId.tsx +0 -60
  280. package/src/pages/checkout.tsx +0 -31
  281. package/src/pages/pages.$pageSlug.tsx +0 -74
  282. package/src/pages/thank-you.tsx +0 -53
  283. package/src/providers/auth.tsx +0 -96
  284. package/src/providers/cart.tsx +0 -275
  285. package/src/providers/ecomiq.ts +0 -137
  286. package/src/providers/index.ts +0 -26
  287. package/src/proxy-api.ts +0 -20
  288. package/src/public-config.ts +0 -24
  289. package/src/review-submission.ts +0 -89
  290. package/src/runtime/context.ts +0 -2
  291. package/src/runtime/default-layout.tsx +0 -64
  292. package/src/runtime/head.ts +0 -149
  293. package/src/runtime/page.tsx +0 -143
  294. package/src/runtime/postcss-browser-stub.mjs +0 -5
  295. package/src/server-config-store.ts +0 -35
  296. package/src/server.ts +0 -286
  297. package/src/styles.css +0 -76
  298. package/src/ui/badge.tsx +0 -32
  299. package/src/ui/button.tsx +0 -31
  300. package/src/ui/card.tsx +0 -31
  301. package/src/ui/carousel.tsx +0 -218
  302. package/src/ui/cn.ts +0 -6
  303. package/src/ui/index.ts +0 -8
  304. package/src/ui/separator.tsx +0 -6
  305. package/src/ui/skeleton.tsx +0 -6
  306. package/src/ui/storefront-error.tsx +0 -49
  307. package/src/utils.ts +0 -36
  308. package/src/vite-inline.mjs +0 -263
  309. package/templates/__root.tsx +0 -83
  310. package/templates/router.tsx +0 -19
@@ -1,270 +0,0 @@
1
- import { useEffect, useRef, type ReactNode } from 'react'
2
- import { useQueryClient } from '@tanstack/react-query'
3
- import { useRouterState } from '@tanstack/react-router'
4
- import {
5
- createCustomerAddress,
6
- customerMeToProfile,
7
- deleteCustomerAddress,
8
- getCustomerAddresses,
9
- getCustomerMe,
10
- getCustomerOrder,
11
- getCustomerOrders,
12
- patchCustomer,
13
- toCustomerSavedAddresses,
14
- updateCustomerAddress,
15
- type CustomerAddressUpsertInput,
16
- type CustomerProfilePatch,
17
- } from '@ecomiq/core'
18
- import { MapPinHouse, MonitorSmartphone, Package, UserRound } from 'lucide-react'
19
- import { CustomerAddressesPanel } from '../../components/account/customer-addresses-panel'
20
- import { CustomerOrderDetailPanel } from '../../components/account/customer-order-detail-panel'
21
- import { CustomerOrdersPanel } from '../../components/account/customer-orders-panel'
22
- import { CustomerProfilePanel } from '../../components/account/customer-profile-panel'
23
- import {
24
- useApiQuery,
25
- useAuth,
26
- useSearchNavigate,
27
- useSettings,
28
- } from '../../providers'
29
- import { createStorefrontProxyApi } from '../../proxy-api'
30
- import { AccountShell } from './account-shell'
31
- import {
32
- AccountPageSkeleton,
33
- AccountSectionSkeleton,
34
- } from './account-page-skeleton'
35
- import { parseAccountRoute } from './parse-account-route'
36
-
37
- const ACCOUNT_BASE = '/account'
38
- const ORDERS_BASE = `${ACCOUNT_BASE}/orders`
39
- const LOGIN_HREF = '/login'
40
- const HOME_HREF = '/'
41
-
42
- function proxyApi() {
43
- return createStorefrontProxyApi()
44
- }
45
-
46
- export function AccountPageView() {
47
- const pathname = useRouterState({ select: (s) => s.location.pathname })
48
- const { section, orderId } = parseAccountRoute(pathname || ACCOUNT_BASE)
49
- const navigate = useSearchNavigate()
50
- const queryClient = useQueryClient()
51
- const settings = useSettings()
52
- const { client, session, isPending, signOut } = useAuth()
53
- const isAuthenticated = Boolean(session?.user)
54
- const redirectTargetRef = useRef<string | null>(null)
55
- const language = settings.store.language || 'es-PE'
56
-
57
- const profileQuery = useApiQuery(
58
- ['account-customer-me'],
59
- async () => customerMeToProfile(await getCustomerMe(proxyApi())),
60
- {
61
- enabled: section === 'personal' && isAuthenticated && !isPending,
62
- freshForMs: 60_000,
63
- },
64
- )
65
-
66
- const addressesQuery = useApiQuery(
67
- ['customer-addresses'],
68
- async () =>
69
- toCustomerSavedAddresses(await getCustomerAddresses(proxyApi())),
70
- {
71
- enabled: section === 'addresses' && isAuthenticated && !isPending,
72
- freshForMs: 30_000,
73
- },
74
- )
75
-
76
- const ordersQuery = useApiQuery(
77
- ['customer-orders'],
78
- async () => getCustomerOrders(proxyApi()),
79
- {
80
- enabled:
81
- section === 'orders' && !orderId && isAuthenticated && !isPending,
82
- freshForMs: 30_000,
83
- },
84
- )
85
-
86
- const orderDetailQuery = useApiQuery(
87
- ['customer-order', orderId],
88
- async () => getCustomerOrder(proxyApi(), orderId!),
89
- {
90
- enabled:
91
- section === 'orders' && Boolean(orderId) && isAuthenticated && !isPending,
92
- freshForMs: 30_000,
93
- },
94
- )
95
-
96
- useEffect(() => {
97
- if (isPending || isAuthenticated) return
98
-
99
- const redirectTarget = redirectTargetRef.current ?? (pathname || ACCOUNT_BASE)
100
- redirectTargetRef.current = redirectTarget
101
-
102
- void navigate({
103
- to: LOGIN_HREF,
104
- search: { redirect: redirectTarget },
105
- replace: true,
106
- })
107
- }, [isAuthenticated, isPending, navigate, pathname])
108
-
109
- const handleLogout = async () => {
110
- await signOut()
111
- void navigate({ to: HOME_HREF, replace: true })
112
- }
113
-
114
- const invalidateAddresses = () =>
115
- queryClient.invalidateQueries({ queryKey: ['customer-addresses'] })
116
-
117
- const handleUpdateProfile = async (payload: CustomerProfilePatch) => {
118
- await patchCustomer(proxyApi(), payload)
119
- const displayName = `${payload.firstName} ${payload.lastName}`.trim()
120
- try {
121
- await client.$fetch('/update-user', {
122
- method: 'POST',
123
- body: {
124
- name: displayName || payload.firstName,
125
- firstName: payload.firstName,
126
- lastName: payload.lastName,
127
- phone: payload.phone,
128
- },
129
- })
130
- } catch {
131
- // La sesión Better Auth es secundaria; el PATCH upstream ya se aplicó.
132
- }
133
- await queryClient.invalidateQueries({ queryKey: ['account-customer-me'] })
134
- }
135
-
136
- const handleCreateAddress = async (payload: CustomerAddressUpsertInput) => {
137
- await createCustomerAddress(proxyApi(), payload)
138
- await invalidateAddresses()
139
- }
140
-
141
- const handleUpdateAddress = async (
142
- addressId: string,
143
- payload: CustomerAddressUpsertInput,
144
- ) => {
145
- await updateCustomerAddress(proxyApi(), addressId, payload)
146
- await invalidateAddresses()
147
- }
148
-
149
- const handleDeleteAddress = async (addressId: string) => {
150
- await deleteCustomerAddress(proxyApi(), addressId)
151
- await invalidateAddresses()
152
- }
153
-
154
- const menuItems = [
155
- {
156
- key: 'personal',
157
- href: ACCOUNT_BASE,
158
- label: 'Datos personales',
159
- icon: <UserRound className="size-4" />,
160
- },
161
- {
162
- key: 'addresses',
163
- href: `${ACCOUNT_BASE}/addresses`,
164
- label: 'Direcciones',
165
- icon: <MapPinHouse className="size-4" />,
166
- },
167
- {
168
- key: 'orders',
169
- href: ORDERS_BASE,
170
- label: 'Mis pedidos',
171
- icon: <Package className="size-4" />,
172
- },
173
- {
174
- key: 'devices',
175
- href: `${ACCOUNT_BASE}/devices`,
176
- label: 'Dispositivos vinculados',
177
- icon: <MonitorSmartphone className="size-4" />,
178
- },
179
- ]
180
-
181
- if (isPending || !isAuthenticated) {
182
- return <AccountPageSkeleton />
183
- }
184
-
185
- let sectionContent: ReactNode
186
-
187
- switch (section) {
188
- case 'personal':
189
- sectionContent = (
190
- <SectionPanel>
191
- <CustomerProfilePanel
192
- profile={profileQuery.data}
193
- isLoading={profileQuery.isLoading}
194
- errorMessage={profileQuery.error?.message}
195
- onUpdateProfile={handleUpdateProfile}
196
- />
197
- </SectionPanel>
198
- )
199
- break
200
- case 'addresses':
201
- sectionContent = (
202
- <SectionPanel>
203
- <CustomerAddressesPanel
204
- addresses={addressesQuery.data}
205
- isLoading={addressesQuery.isLoading}
206
- errorMessage={addressesQuery.error?.message}
207
- onCreateAddress={handleCreateAddress}
208
- onUpdateAddress={handleUpdateAddress}
209
- onDeleteAddress={handleDeleteAddress}
210
- />
211
- </SectionPanel>
212
- )
213
- break
214
- case 'orders':
215
- sectionContent = (
216
- <SectionPanel>
217
- {orderId ? (
218
- <CustomerOrderDetailPanel
219
- order={orderDetailQuery.data}
220
- isLoading={orderDetailQuery.isLoading}
221
- errorMessage={orderDetailQuery.error?.message}
222
- backHref={ORDERS_BASE}
223
- language={language}
224
- productHref={(handle) => `/products/${encodeURIComponent(handle)}`}
225
- />
226
- ) : (
227
- <CustomerOrdersPanel
228
- orders={ordersQuery.data}
229
- isLoading={ordersQuery.isLoading}
230
- errorMessage={ordersQuery.error?.message}
231
- orderDetailBasePath={ORDERS_BASE}
232
- shopHref={HOME_HREF}
233
- language={language}
234
- />
235
- )}
236
- </SectionPanel>
237
- )
238
- break
239
- case 'devices':
240
- sectionContent = (
241
- <SectionPanel>
242
- <h2 className="text-lg font-medium text-foreground">Dispositivos vinculados</h2>
243
- <p className="mt-2 text-muted-foreground">
244
- Aún no hay endpoint para listar sesiones/dispositivos.
245
- </p>
246
- <p className="mt-1 text-sm text-muted-foreground">
247
- Cuando esté disponible el backend, podrás cerrar sesiones remotas desde aquí.
248
- </p>
249
- </SectionPanel>
250
- )
251
- break
252
- default:
253
- sectionContent = <AccountSectionSkeleton />
254
- }
255
-
256
- return (
257
- <AccountShell
258
- menuItems={menuItems}
259
- onLogout={() => void handleLogout()}
260
- pathname={pathname}
261
- homeHref={HOME_HREF}
262
- >
263
- {sectionContent}
264
- </AccountShell>
265
- )
266
- }
267
-
268
- function SectionPanel({ children }: { children: ReactNode }) {
269
- return <div className="px-6">{children}</div>
270
- }
@@ -1,101 +0,0 @@
1
- import type { ReactNode } from 'react'
2
- import { ArrowLeft, ChevronRight, LogOut } from 'lucide-react'
3
- import { Link } from '../../link'
4
- import { cn } from '../../ui/cn'
5
- import {
6
- accountSectionLabel,
7
- parseAccountRoute,
8
- } from './parse-account-route'
9
-
10
- export type AccountMenuItem = {
11
- key: string
12
- href: string
13
- label: string
14
- icon: ReactNode
15
- }
16
-
17
- type AccountShellProps = {
18
- menuItems: AccountMenuItem[]
19
- onLogout: () => void
20
- profileSyncError?: string | null
21
- children: ReactNode
22
- pathname: string
23
- homeHref?: string
24
- }
25
-
26
- export function AccountShell({
27
- menuItems,
28
- onLogout,
29
- profileSyncError,
30
- children,
31
- pathname,
32
- homeHref = '/',
33
- }: AccountShellProps) {
34
- const { section, orderId } = parseAccountRoute(pathname || '/account')
35
- const sectionTitle = accountSectionLabel(section, orderId)
36
-
37
- return (
38
- <div className="container max-w-6xl mx-auto px-4 py-8">
39
- <Link
40
- to={homeHref}
41
- className="inline-flex items-center gap-2 text-sm text-muted-foreground transition-colors [@media(hover:hover)]:hover:text-foreground"
42
- >
43
- <ArrowLeft className="size-4" />
44
- <span className="underline">Volver a la tienda</span>
45
- </Link>
46
-
47
- <h1 className="mt-2 text-3xl font-semibold tracking-tight">Mi perfil</h1>
48
-
49
- {profileSyncError ? (
50
- <p className="mt-3 text-sm text-amber-800">{profileSyncError}</p>
51
- ) : null}
52
-
53
- <div className="mt-8 grid grid-cols-1 gap-6 lg:grid-cols-[320px_1fr]">
54
- <aside className="overflow-hidden rounded-xl border border-border bg-background lg:sticky lg:top-15 lg:self-start">
55
- <nav className="divide-y divide-border">
56
- {menuItems.map((item) => {
57
- const isOrders = item.key === 'orders'
58
- const active =
59
- (section === item.key && !orderId) ||
60
- (isOrders && section === 'orders' && Boolean(orderId))
61
-
62
- return (
63
- <Link
64
- key={item.key}
65
- to={item.href}
66
- className={cn(
67
- 'flex items-center justify-between border-b border-border px-4 py-4 text-[15px] transition-colors',
68
- active
69
- ? 'bg-muted font-semibold'
70
- : '[@media(hover:hover)]:hover:bg-muted',
71
- )}
72
- >
73
- <span className="flex items-center gap-3">
74
- <span className="text-muted-foreground">{item.icon}</span>
75
- <span>{item.label}</span>
76
- </span>
77
- <ChevronRight className="size-4 text-muted-foreground" />
78
- </Link>
79
- )
80
- })}
81
- <button
82
- type="button"
83
- onClick={onLogout}
84
- className="flex w-full items-center justify-between px-4 py-4 text-left text-[15px] transition-colors [@media(hover:hover)]:hover:bg-muted"
85
- >
86
- <span className="flex items-center gap-3">
87
- <LogOut className="size-4 text-muted-foreground" />
88
- <span>Cerrar sesión</span>
89
- </span>
90
- <ChevronRight className="size-4 text-muted-foreground" />
91
- </button>
92
- </nav>
93
- </aside>
94
-
95
- <section className="min-w-0" aria-label={sectionTitle}>
96
- {children}
97
- </section>
98
- </div>
99
- </div>
100
- )
101
- }
@@ -1,36 +0,0 @@
1
- export type AccountSection = 'personal' | 'addresses' | 'orders' | 'devices'
2
-
3
- export type ParsedAccountRoute = {
4
- section: AccountSection
5
- orderId?: string
6
- }
7
-
8
- const SECTION_LABELS: Record<AccountSection, string> = {
9
- personal: 'Datos personales',
10
- addresses: 'Direcciones',
11
- orders: 'Mis pedidos',
12
- devices: 'Dispositivos vinculados',
13
- }
14
-
15
- export function accountSectionLabel(section: AccountSection, orderId?: string): string {
16
- if (section === 'orders' && orderId) return 'Detalle del pedido'
17
- return SECTION_LABELS[section]
18
- }
19
-
20
- /** Deriva sección y orderId desde `/account/...`. */
21
- export function parseAccountRoute(pathname: string): ParsedAccountRoute {
22
- const normalized = pathname.replace(/\/$/, '') || '/'
23
- const parts = normalized.split('/').filter(Boolean)
24
- const accountIdx = parts.indexOf('account')
25
- if (accountIdx === -1) return { section: 'personal' }
26
-
27
- const sub = parts.slice(accountIdx + 1)
28
- if (sub.length === 0) return { section: 'personal' }
29
- if (sub[0] === 'addresses') return { section: 'addresses' }
30
- if (sub[0] === 'devices') return { section: 'devices' }
31
- if (sub[0] === 'orders') {
32
- if (sub[1]) return { section: 'orders', orderId: decodeURIComponent(sub[1]) }
33
- return { section: 'orders' }
34
- }
35
- return { section: 'personal' }
36
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * Área de cuenta cerrada de Ecomiq (`/account`, `/account/addresses`, …).
3
- * Los templates no pueden overridear estas rutas desde `pages/account`.
4
- * Login/register viven en la raíz del template (`/login`, `/register`).
5
- */
6
- import { definePage } from '../index'
7
- import { AccountPageView } from './account/account-page-view'
8
-
9
- export default definePage({
10
- meta: () => ({
11
- title: 'Mi cuenta',
12
- description: 'Gestiona tu cuenta, direcciones y pedidos',
13
- noIndex: true,
14
- }),
15
- component: AccountPageView,
16
- })
@@ -1,20 +0,0 @@
1
- /**
2
- * Proxy catch-all same-origin: `/api/*` → storefront `/v1/*`.
3
- * Ruta cerrada del framework (no hace falta en pages/ del usuario).
4
- */
5
- import { proxyStorefrontRequest } from '../api-proxy'
6
-
7
- type ApiHandlerArgs = {
8
- request: Request
9
- params: { _splat?: string }
10
- }
11
-
12
- async function handle({ request, params }: ApiHandlerArgs) {
13
- return proxyStorefrontRequest(request, params._splat)
14
- }
15
-
16
- export const GET = handle
17
- export const POST = handle
18
- export const PUT = handle
19
- export const PATCH = handle
20
- export const DELETE = handle
@@ -1,16 +0,0 @@
1
- /**
2
- * Better Auth catch-all: `/api/auth/*`.
3
- * Ruta cerrada del framework (no hace falta en pages/ del usuario).
4
- */
5
- import { storefrontAuth } from '../auth/storefront-auth'
6
-
7
- type ApiHandlerArgs = {
8
- request: Request
9
- }
10
-
11
- async function handle({ request }: ApiHandlerArgs) {
12
- return storefrontAuth.handler(request)
13
- }
14
-
15
- export const GET = handle
16
- export const POST = handle
@@ -1,60 +0,0 @@
1
- /**
2
- * Checkout cerrado de Ecomiq (`/checkout/$checkoutId`).
3
- * Los templates heredan la ruta vía codegen; no se puede overridear desde `pages/`.
4
- * `/checkout` sin ID redirige a `/cart` (ver `checkout.tsx`).
5
- */
6
- import { useEffect } from 'react'
7
- import { CheckoutSessionView } from '../components/checkout'
8
- import { definePage } from '../index'
9
- import { useAuth, useSearchNavigate, useSettings } from '../providers'
10
-
11
- export default definePage<undefined, { checkoutId: string }>({
12
- meta: () => ({
13
- title: 'Checkout',
14
- description: 'Confirma tu pedido y completa la compra',
15
- noIndex: true,
16
- }),
17
- component: CheckoutPage,
18
- })
19
-
20
- function CheckoutPage({ params }: { params: { checkoutId: string } }) {
21
- const checkoutId = params.checkoutId?.trim() ?? ''
22
- const { store } = useSettings()
23
- const allowGuest = store.checkoutSettings.general.allowGuestCheckout
24
- const { session, isPending: authPending } = useAuth()
25
- const navigate = useSearchNavigate()
26
- const isAuthenticated = Boolean(session?.user)
27
- const needsAuth = !allowGuest && !authPending && !isAuthenticated && Boolean(checkoutId)
28
-
29
- useEffect(() => {
30
- if (checkoutId) return
31
- void navigate({ to: '/cart', replace: true })
32
- }, [checkoutId, navigate])
33
-
34
- useEffect(() => {
35
- if (!needsAuth || !checkoutId) return
36
- void navigate({
37
- to: '/login',
38
- search: { redirect: `/checkout/${checkoutId}` },
39
- replace: true,
40
- })
41
- }, [needsAuth, navigate, checkoutId])
42
-
43
- if (!checkoutId) {
44
- return (
45
- <div className="mx-auto w-full max-w-6xl px-4 py-16 text-center text-sm text-muted-foreground">
46
- Redirigiendo al carrito…
47
- </div>
48
- )
49
- }
50
-
51
- if (authPending || needsAuth) {
52
- return (
53
- <div className="mx-auto w-full max-w-6xl px-4 py-16 text-center text-sm text-muted-foreground">
54
- {needsAuth ? 'Redirigiendo para iniciar sesión…' : 'Cargando…'}
55
- </div>
56
- )
57
- }
58
-
59
- return <CheckoutSessionView cartToken={checkoutId} />
60
- }
@@ -1,31 +0,0 @@
1
- /**
2
- * Checkout cerrado de Ecomiq (`/checkout` / `/checkout/`).
3
- * Sin ID → redirige al carrito. El flujo vive en `/checkout/$checkoutId`
4
- * (ruta hermana, no hija: codegen usa `checkout.index.tsx`).
5
- */
6
- import { useEffect } from 'react'
7
- import { definePage } from '../index'
8
- import { useSearchNavigate } from '../providers'
9
-
10
- export default definePage({
11
- meta: () => ({
12
- title: 'Checkout',
13
- description: 'Confirma tu pedido y completa la compra',
14
- noIndex: true,
15
- }),
16
- component: CheckoutRedirectPage,
17
- })
18
-
19
- function CheckoutRedirectPage() {
20
- const navigate = useSearchNavigate()
21
-
22
- useEffect(() => {
23
- void navigate({ to: '/cart', replace: true })
24
- }, [navigate])
25
-
26
- return (
27
- <div className="mx-auto w-full max-w-6xl px-4 py-16 text-center text-sm text-muted-foreground">
28
- Redirigiendo al carrito…
29
- </div>
30
- )
31
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * Página de contenido CMS (`/pages/$pageSlug`).
3
- * Las páginas Definition las sirve el template (`$.tsx`).
4
- */
5
- import type { CmsPageDetailsDto } from '@ecomiq/core'
6
- import {
7
- definePage,
8
- sanitizeHtml,
9
- serverApi,
10
- stripHtml,
11
- z,
12
- } from '../index'
13
- import { StorefrontError } from '../ui'
14
-
15
- const ContentPageSettingsSchema = z.object({
16
- title: z.string().optional(),
17
- html: z.string().optional(),
18
- seo: z.object({
19
- title: z.string().optional(),
20
- description: z.string().optional(),
21
- }).optional(),
22
- }).passthrough()
23
-
24
- type ContentPageData = {
25
- page: CmsPageDetailsDto
26
- content: z.infer<typeof ContentPageSettingsSchema>
27
- }
28
-
29
- export default definePage<ContentPageData, { pageSlug: string }>({
30
- loader: async ({ params }) => {
31
- const page = await serverApi.getPageBySlug(params.pageSlug)
32
- if (page.status !== 'published') throw new Error('Página no publicada')
33
-
34
- return {
35
- page,
36
- content: ContentPageSettingsSchema.parse(page.settings ?? {}),
37
- }
38
- },
39
-
40
- meta: ({ data }) => ({
41
- title: data.content.seo?.title ?? data.content.title ?? data.page.name,
42
- description: data.content.seo?.description ?? (
43
- data.content.html
44
- ? stripHtml(data.content.html).slice(0, 155)
45
- : undefined
46
- ),
47
- }),
48
-
49
- error: () => (
50
- <StorefrontError
51
- title="Página no disponible"
52
- description="El contenido no existe o todavía no está publicado."
53
- />
54
- ),
55
-
56
- component: ({ data }) => (
57
- <article className="mx-auto w-full max-w-4xl px-4 py-10 sm:py-14">
58
- <header className="mb-8 border-b pb-8">
59
- <h1 className="text-balance text-3xl font-semibold tracking-[-0.02em] md:text-4xl">
60
- {data.content.title ?? data.page.name}
61
- </h1>
62
- </header>
63
-
64
- {data.content.html ? (
65
- <div
66
- className="prose prose-neutral max-w-none text-foreground prose-headings:font-semibold prose-a:text-primary"
67
- dangerouslySetInnerHTML={{ __html: sanitizeHtml(data.content.html) }}
68
- />
69
- ) : (
70
- <p className="text-muted-foreground">Contenido no disponible.</p>
71
- )}
72
- </article>
73
- ),
74
- })
@@ -1,53 +0,0 @@
1
- /**
2
- * Página de agradecimiento cerrada (`/thank-you`).
3
- * Tras un checkout exitoso (sin redirect del provider).
4
- */
5
- import { CheckCircle2 } from 'lucide-react'
6
- import { z } from 'zod'
7
- import { definePage, Link } from '../index'
8
- import { buttonVariants, cn } from '../ui'
9
-
10
- const ThankYouSearchSchema = z.object({
11
- cartToken: z.string().optional(),
12
- transactionId: z.string().optional(),
13
- })
14
-
15
- type ThankYouSearch = z.infer<typeof ThankYouSearchSchema>
16
-
17
- export default definePage<undefined, Record<string, never>, ThankYouSearch>({
18
- meta: () => ({
19
- title: 'Gracias por tu compra',
20
- description: 'Tu pedido fue recibido correctamente',
21
- noIndex: true,
22
- }),
23
- validateSearch: (raw) => ThankYouSearchSchema.parse(raw),
24
- component: ThankYouPage,
25
- })
26
-
27
- function ThankYouPage({ search }: { search: ThankYouSearch }) {
28
- return (
29
- <section className="mx-auto flex min-h-[55vh] w-full max-w-lg flex-col items-center justify-center px-4 py-16 text-center">
30
- <div className="mb-5 flex size-14 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-600">
31
- <CheckCircle2 className="size-7" aria-hidden="true" />
32
- </div>
33
- <h1 className="text-3xl font-semibold tracking-tight">¡Gracias por tu compra!</h1>
34
- <p className="mt-3 text-sm leading-6 text-muted-foreground">
35
- Recibimos tu pedido y lo estamos procesando. Te enviaremos una confirmación por
36
- correo cuando esté listo.
37
- </p>
38
- {search.transactionId ? (
39
- <p className="mt-4 rounded-md bg-muted px-3 py-2 font-mono text-xs text-muted-foreground">
40
- Ref. {search.transactionId}
41
- </p>
42
- ) : null}
43
- <div className="mt-8 flex flex-wrap justify-center gap-3">
44
- <Link to="/" className={cn(buttonVariants({ size: 'lg' }))}>
45
- Volver a la tienda
46
- </Link>
47
- <Link to="/products" className={cn(buttonVariants({ size: 'lg', variant: 'outline' }))}>
48
- Seguir comprando
49
- </Link>
50
- </div>
51
- </section>
52
- )
53
- }