@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,291 +0,0 @@
1
- import { Link } from '../link'
2
- import { Carousel } from '../ui/carousel'
3
- import { cn } from '../ui/cn'
4
- import { buttonVariants } from '../ui/button'
5
- import type { CmsPageDetailsDto, CmsSectionDto } from '@ecomiq/core'
6
- import { useEffect, useRef, useState } from 'react'
7
- import { z } from 'zod'
8
-
9
- /** Slide tipado para uso manual (sin CMS). */
10
- export type HeroSlide = {
11
- id?: string
12
- title: string
13
- subtitle?: string
14
- highlight?: string
15
- description?: string
16
- ctaText?: string
17
- ctaUrl?: string
18
- imageUrl?: string
19
- }
20
-
21
- export type HeroProps = {
22
- /** Página CMS: busca la sección hero automáticamente. */
23
- page?: Pick<CmsPageDetailsDto, 'sections'> | null
24
- /** Sección CMS directa (prioridad sobre `page`). */
25
- section?: CmsSectionDto | null
26
- /** Slides manuales (prioridad máxima). */
27
- slides?: HeroSlide[]
28
- /** Autoplay en ms. Default: 6000. `false` para desactivar. */
29
- autoplay?: number | false
30
- /** Altura fija en px. Si no, usa `section.settings.height`. */
31
- height?: number
32
- className?: string
33
- fallbacks?: {
34
- title?: string
35
- subtitle?: string
36
- ctaText?: string
37
- ctaUrl?: string
38
- }
39
- }
40
-
41
- const HERO_SECTION_TYPES = new Set(['hero', 'banner', 'slideshow', 'carousel'])
42
- const HERO_BLOCK_TYPES = new Set(['slide'])
43
-
44
- const HeroTextSchema = z.string().trim().min(1)
45
-
46
- const HeroSectionSettingsSchema = z
47
- .object({
48
- enabled: z.boolean().optional(),
49
- title: HeroTextSchema.optional(),
50
- subtitle: HeroTextSchema.optional(),
51
- description: HeroTextSchema.optional(),
52
- ctaText: HeroTextSchema.optional(),
53
- ctaUrl: HeroTextSchema.optional(),
54
- imageUrl: HeroTextSchema.optional(),
55
- height: z.number().positive().optional(),
56
- })
57
- .strip()
58
-
59
- const HeroBlockSettingsSchema = z
60
- .object({
61
- title: HeroTextSchema,
62
- subtitle: HeroTextSchema.optional(),
63
- highlight: HeroTextSchema.optional(),
64
- description: HeroTextSchema.optional(),
65
- ctaText: HeroTextSchema.optional(),
66
- ctaUrl: HeroTextSchema.optional(),
67
- imageUrl: HeroTextSchema.optional(),
68
- })
69
- .strip()
70
-
71
- function isHeroSection(section: CmsSectionDto): boolean {
72
- return HERO_SECTION_TYPES.has(section.type.toLowerCase())
73
- }
74
-
75
- export function findHeroSection(
76
- page?: Pick<CmsPageDetailsDto, 'sections'> | null,
77
- ): CmsSectionDto | null {
78
- return page?.sections.find(isHeroSection) ?? null
79
- }
80
-
81
- function heroHeightPx(
82
- sectionSettings: z.infer<typeof HeroSectionSettingsSchema> | null,
83
- height?: number,
84
- ): number | undefined {
85
- if (typeof height === 'number' && height > 0) return height
86
- return sectionSettings?.height
87
- }
88
-
89
- function slidesFromSection(
90
- section: CmsSectionDto,
91
- sectionSettings: z.infer<typeof HeroSectionSettingsSchema>,
92
- fallbacks: NonNullable<HeroProps['fallbacks']>,
93
- ): HeroSlide[] {
94
- const blocks = [...section.blocks].sort((a, b) => a.order - b.order)
95
- if (blocks.length === 0) {
96
- return [
97
- {
98
- id: section.id,
99
- title: sectionSettings.title ?? fallbacks.title ?? 'Nueva colección',
100
- subtitle: sectionSettings.subtitle ?? fallbacks.subtitle,
101
- description: sectionSettings.description,
102
- ctaText: sectionSettings.ctaText ?? fallbacks.ctaText ?? 'Ver colección',
103
- ctaUrl: sectionSettings.ctaUrl ?? fallbacks.ctaUrl ?? '/collections/all',
104
- imageUrl: sectionSettings.imageUrl,
105
- },
106
- ]
107
- }
108
-
109
- const slides: HeroSlide[] = []
110
- for (const block of blocks) {
111
- if (!HERO_BLOCK_TYPES.has(block.type.toLowerCase())) continue
112
-
113
- const parsed = HeroBlockSettingsSchema.safeParse(block.settings)
114
- if (!parsed.success) continue
115
-
116
- slides.push({
117
- id: block.id,
118
- title: parsed.data.title,
119
- subtitle: parsed.data.subtitle,
120
- highlight: parsed.data.highlight,
121
- description: parsed.data.description ?? sectionSettings.subtitle,
122
- ctaText: parsed.data.ctaText ?? fallbacks.ctaText ?? 'Ver colección',
123
- ctaUrl: parsed.data.ctaUrl ?? fallbacks.ctaUrl ?? '/collections/all',
124
- imageUrl: parsed.data.imageUrl,
125
- })
126
- }
127
-
128
- return slides
129
- }
130
-
131
- function resolveCmsHero(
132
- section: CmsSectionDto,
133
- fallbacks: NonNullable<HeroProps['fallbacks']>,
134
- ): {
135
- settings: z.infer<typeof HeroSectionSettingsSchema>
136
- slides: HeroSlide[]
137
- } | null {
138
- const parsed = HeroSectionSettingsSchema.safeParse(section.settings)
139
- if (!parsed.success || parsed.data.enabled === false) return null
140
-
141
- return {
142
- settings: parsed.data,
143
- slides: slidesFromSection(section, parsed.data, fallbacks),
144
- }
145
- }
146
-
147
- function selectedCmsSection(props: HeroProps): CmsSectionDto | null {
148
- if (props.section) {
149
- if (isHeroSection(props.section)) return props.section
150
- return null
151
- }
152
-
153
- return findHeroSection(props.page)
154
- }
155
-
156
- function resolveSlides(props: HeroProps): { slides: HeroSlide[]; heightPx?: number } {
157
- if (props.slides?.length) {
158
- let sectionSettings: z.infer<typeof HeroSectionSettingsSchema> | null = null
159
- if (props.section) {
160
- const parsed = HeroSectionSettingsSchema.safeParse(props.section.settings)
161
- if (parsed.success) sectionSettings = parsed.data
162
- }
163
- return {
164
- slides: props.slides,
165
- heightPx: heroHeightPx(sectionSettings, props.height),
166
- }
167
- }
168
-
169
- const section = selectedCmsSection(props)
170
- if (!section) return { slides: [], heightPx: props.height }
171
-
172
- const resolved = resolveCmsHero(section, props.fallbacks ?? {})
173
- if (!resolved) return { slides: [], heightPx: props.height }
174
-
175
- return {
176
- slides: resolved.slides,
177
- heightPx: heroHeightPx(resolved.settings, props.height),
178
- }
179
- }
180
-
181
- function HeroSlideContent({
182
- slide,
183
- priorityImage,
184
- heightPx,
185
- }: {
186
- slide: HeroSlide
187
- priorityImage?: boolean
188
- heightPx?: number
189
- }) {
190
- const [failedImage, setFailedImage] = useState<string | null>(null)
191
- const imageRef = useRef<HTMLImageElement>(null)
192
- const hasImage = Boolean(slide.imageUrl && slide.imageUrl !== failedImage)
193
-
194
- useEffect(() => {
195
- const image = imageRef.current
196
- if (slide.imageUrl && image?.complete && image.naturalWidth === 0) {
197
- setFailedImage(slide.imageUrl)
198
- }
199
- }, [slide.imageUrl])
200
-
201
- return (
202
- <div
203
- className={cn(
204
- 'relative w-full overflow-hidden',
205
- !heightPx && 'min-h-[min(70vh,520px)] md:aspect-[21/9] md:min-h-0',
206
- )}
207
- style={heightPx ? { height: `${heightPx}px` } : undefined}
208
- >
209
- {hasImage ? (
210
- <img
211
- ref={imageRef}
212
- src={slide.imageUrl}
213
- alt=""
214
- aria-hidden
215
- decoding="async"
216
- fetchPriority={priorityImage ? 'high' : 'auto'}
217
- loading={priorityImage ? 'eager' : 'lazy'}
218
- onError={() => setFailedImage(slide.imageUrl ?? null)}
219
- className="pointer-events-none absolute inset-0 h-full w-full object-cover object-center"
220
- />
221
- ) : (
222
- <div className="absolute inset-0 bg-muted" aria-hidden />
223
- )}
224
-
225
- <div
226
- className="absolute inset-0 bg-linear-to-r from-black/80 via-black/45 to-black/10"
227
- aria-hidden
228
- />
229
-
230
- <div className="relative mx-auto flex h-full min-h-[inherit] w-full max-w-6xl items-center px-4 py-12 md:py-16">
231
- <div className="relative z-10 max-w-xl text-white">
232
- {slide.subtitle ? (
233
- <p className="text-xs font-medium uppercase tracking-[0.2em] text-white/70">
234
- {slide.subtitle}
235
- </p>
236
- ) : null}
237
- <h1 className="mt-3 text-4xl font-light leading-tight tracking-tight md:text-5xl lg:text-6xl">
238
- {slide.title}
239
- </h1>
240
- {slide.highlight ? (
241
- <p className="mt-2 text-2xl font-medium tracking-wide text-primary-foreground md:text-3xl">
242
- {slide.highlight}
243
- </p>
244
- ) : null}
245
- {slide.description ? (
246
- <p className="mt-4 max-w-lg text-sm leading-relaxed text-white/80 md:text-base">
247
- {slide.description}
248
- </p>
249
- ) : null}
250
- {slide.ctaText && slide.ctaUrl ? (
251
- <Link to={slide.ctaUrl} className={cn(buttonVariants({ size: 'lg' }), 'mt-8')}>
252
- {slide.ctaText}
253
- </Link>
254
- ) : null}
255
- </div>
256
- </div>
257
- </div>
258
- )
259
- }
260
-
261
- /**
262
- * Hero de tienda con carrusel Embla interno.
263
- *
264
- * ```tsx
265
- * <Hero page={homePage} />
266
- * <Hero section={heroSection} />
267
- * <Hero slides={[{ title: 'Hola', ctaText: 'Ver', ctaUrl: '/' }]} />
268
- * ```
269
- */
270
- export function Hero(props: HeroProps) {
271
- const { slides, heightPx } = resolveSlides(props)
272
- if (!slides.length) return null
273
-
274
- return (
275
- <Carousel
276
- className={cn('bg-foreground', props.className)}
277
- fade
278
- autoplay={props.autoplay === undefined ? 6000 : props.autoplay}
279
- aria-label="Destacados"
280
- >
281
- {slides.map((slide, index) => (
282
- <HeroSlideContent
283
- key={slide.id ?? index}
284
- slide={slide}
285
- priorityImage={index === 0}
286
- heightPx={heightPx}
287
- />
288
- ))}
289
- </Carousel>
290
- )
291
- }
package/src/config.d.ts DELETED
@@ -1,94 +0,0 @@
1
- import type { EcomiqThemeConfig } from './index'
2
-
3
- export interface EcomiqApiConfig {
4
- baseUrl?: string
5
- version?: string
6
- headers?: Record<string, string>
7
- timeoutMs?: number
8
- }
9
-
10
- export interface EcomiqCloudConfig {
11
- /**
12
- * URL privada del plano de control. En producción debe configurarse de forma
13
- * explícita; durante `ecomiq dev` el runtime usa http://127.0.0.1:8020.
14
- */
15
- baseUrl?: string
16
- /**
17
- * Slug canónico en Cloud. Si se omite, se deriva del primer label de
18
- * `domain`.
19
- */
20
- storeSlug?: string
21
- timeoutMs?: number
22
- /** TTL del cache de la publicación activa. Default: 15 segundos. */
23
- cacheTtlMs?: number
24
- }
25
-
26
- export type EcomiqCloudflareRoute =
27
- | string
28
- | {
29
- pattern: string
30
- zone_id: string
31
- custom_domain?: boolean
32
- }
33
- | {
34
- pattern: string
35
- zone_name: string
36
- custom_domain?: boolean
37
- }
38
- | {
39
- pattern: string
40
- custom_domain: boolean
41
- enabled?: boolean
42
- previews_enabled?: boolean
43
- }
44
-
45
- export interface EcomiqConfigInput {
46
- /**
47
- * Identidad de tienda para `x-Store-Domain`.
48
- * Acepta `marketplace`, `marketplace.ecomiq.app` o `marketplace.pe`.
49
- */
50
- domain: string
51
- api?: EcomiqApiConfig
52
- /** Configuración server-only para resolver la publicación activa. */
53
- cloud?: EcomiqCloudConfig
54
- theme?: EcomiqThemeConfig
55
- dev?: {
56
- port?: number
57
- apiBaseUrl?: string
58
- /** Default `node`. `workerd` emula Cloudflare en local. */
59
- runtime?: 'workerd' | 'node'
60
- /**
61
- * HTTPS en localhost. `true` = cert auto; `{ cert, key }` = PEM propios.
62
- */
63
- https?: boolean | { cert: string; key: string }
64
- }
65
- deploy?: {
66
- target?: 'cloudflare'
67
- name?: string
68
- routes?: EcomiqCloudflareRoute[]
69
- }
70
- env?: Record<string, string>
71
- }
72
-
73
- export interface ResolvedEcomiqConfig
74
- extends Omit<EcomiqConfigInput, 'api' | 'cloud'> {
75
- api: {
76
- baseUrl: string
77
- version: string
78
- headers?: Record<string, string>
79
- timeoutMs: number
80
- }
81
- cloud?: {
82
- baseUrl?: string
83
- storeSlug?: string
84
- timeoutMs: number
85
- cacheTtlMs: number
86
- }
87
- }
88
-
89
- export function parseConfig(
90
- config: unknown,
91
- options?: { source?: string },
92
- ): ResolvedEcomiqConfig
93
-
94
- export function defineConfig(config: EcomiqConfigInput): ResolvedEcomiqConfig
package/src/config.mjs DELETED
@@ -1,146 +0,0 @@
1
- /**
2
- * Configuración base de un proyecto Ecomiq (ecomiq.config.js).
3
- * Se valida con Zod al arrancar la CLI para dar errores claros.
4
- *
5
- * Identidad de tienda, SEO, analytics y menús salen de `/v1/settings`.
6
- * Aquí solo queda lo operativo: domain, api, cloud, theme, dev, deploy, env.
7
- */
8
- import { z } from 'zod'
9
-
10
- const cloudflareRouteSchema = z.union([
11
- z.string().min(1),
12
- z
13
- .object({
14
- pattern: z.string().min(1),
15
- zone_id: z.string().min(1),
16
- custom_domain: z.boolean().optional(),
17
- })
18
- .strict(),
19
- z
20
- .object({
21
- pattern: z.string().min(1),
22
- zone_name: z.string().min(1),
23
- custom_domain: z.boolean().optional(),
24
- })
25
- .strict(),
26
- z
27
- .object({
28
- pattern: z.string().min(1),
29
- custom_domain: z.boolean(),
30
- enabled: z.boolean().optional(),
31
- previews_enabled: z.boolean().optional(),
32
- })
33
- .strict(),
34
- ])
35
-
36
- const configSchema = z.object({
37
- /**
38
- * Identidad de la tienda en el API storefront (header x-Store-Domain).
39
- * Acepta slug corto o dominio: `marketplace`, `marketplace.ecomiq.app`, `marketplace.pe`.
40
- */
41
- domain: z.string().min(1),
42
- /** API de storefront. baseUrl default: https://storefront.ecomiq.pe */
43
- api: z
44
- .object({
45
- baseUrl: z.string().url().default('https://storefront.ecomiq.pe'),
46
- /** Segmento de path de la API, ej. 'v1' → {baseUrl}/v1/settings */
47
- version: z.string().min(1).default('v1'),
48
- headers: z.record(z.string()).optional(),
49
- /** Timeout en ms para cada request. */
50
- timeoutMs: z.number().int().positive().default(10_000),
51
- })
52
- .default({}),
53
- /**
54
- * Plano de control server-only. `baseUrl` se exige operativamente en
55
- * producción; si falta, el storefront degrada a la home clásica.
56
- */
57
- cloud: z
58
- .object({
59
- baseUrl: z.string().url().optional(),
60
- artifactBaseUrl: z.string().url().optional(),
61
- storeSlug: z
62
- .string()
63
- .trim()
64
- .min(2)
65
- .max(100)
66
- .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)
67
- .optional(),
68
- timeoutMs: z.number().int().positive().default(3_000),
69
- cacheTtlMs: z.number().int().min(0).max(300_000).default(15_000),
70
- })
71
- .strict()
72
- .optional(),
73
- /** Tokens de tema; sobreescriben las variables CSS del design system. */
74
- theme: z
75
- .object({
76
- primary: z.string().optional(),
77
- primaryForeground: z.string().optional(),
78
- background: z.string().optional(),
79
- foreground: z.string().optional(),
80
- accent: z.string().optional(),
81
- radius: z.string().optional(),
82
- })
83
- .optional(),
84
- dev: z
85
- .object({
86
- port: z.number().int().default(3000),
87
- apiBaseUrl: z.string().url(),
88
- /**
89
- * Runtime SSR en `ecomiq dev`. Default: `node`.
90
- * Usa `workerd` para emular Cloudflare Workers en local.
91
- * `ecomiq build` / `deploy` siempre usan workerd.
92
- */
93
- runtime: z.enum(['workerd', 'node']),
94
- /**
95
- * HTTPS en localhost (`ecomiq dev`).
96
- * `true` → certificado auto (basic-ssl).
97
- * `{ cert, key }` → PEM propios relativos al proyecto.
98
- */
99
- https: z.union([
100
- z.boolean(),
101
- z
102
- .object({
103
- cert: z.string().min(1),
104
- key: z.string().min(1),
105
- })
106
- .strict(),
107
- ]),
108
- })
109
- .partial()
110
- .optional(),
111
- deploy: z
112
- .object({
113
- target: z.literal('cloudflare').default('cloudflare'),
114
- /** Nombre del Worker en Cloudflare. */
115
- name: z.string().optional(),
116
- /** Rutas/custom domains del Worker. */
117
- routes: z.array(cloudflareRouteSchema).optional(),
118
- })
119
- .optional(),
120
- /** Variables expuestas al Worker (no secretas). */
121
- env: z.record(z.string()).optional(),
122
- })
123
-
124
- export function parseConfig(config, options = {}) {
125
- const source = options.source ?? 'ecomiq.config.js'
126
- if (
127
- config === null ||
128
- typeof config !== 'object' ||
129
- (Object.getPrototypeOf(config) !== Object.prototype && Object.getPrototypeOf(config) !== null)
130
- ) {
131
- throw new Error(`${source} inválido:\n · <raíz>: el default export debe ser un objeto plano`)
132
- }
133
-
134
- const parsed = configSchema.safeParse(config)
135
- if (!parsed.success) {
136
- const issues = parsed.error.issues
137
- .map((issue) => ` · ${issue.path.join('.') || '<raíz>'}: ${issue.message}`)
138
- .join('\n')
139
- throw new Error(`${source} inválido:\n${issues}`)
140
- }
141
- return parsed.data
142
- }
143
-
144
- export function defineConfig(config) {
145
- return parseConfig(config)
146
- }
@@ -1,31 +0,0 @@
1
- export type MoneyLocale = {
2
- language: string
3
- currency: string
4
- }
5
-
6
- /**
7
- * Formatea un monto con `Intl.NumberFormat` (moneda ISO-4217).
8
- *
9
- * @example
10
- * formatMoney(19.9, { language: 'es-PE', currency: 'PEN' })
11
- * formatMoney(19.9, 'es-PE', 'PEN')
12
- */
13
- export function formatMoney(value: number, locale: MoneyLocale): string
14
- export function formatMoney(value: number, language: string, currency: string): string
15
- export function formatMoney(
16
- value: number,
17
- languageOrLocale: string | MoneyLocale,
18
- currency?: string,
19
- ): string {
20
- const language =
21
- typeof languageOrLocale === 'string' ? languageOrLocale : languageOrLocale.language
22
- const currencyCode =
23
- typeof languageOrLocale === 'string' ? currency! : languageOrLocale.currency
24
-
25
- return new Intl.NumberFormat(language, {
26
- style: 'currency',
27
- currency: currencyCode,
28
- minimumFractionDigits: 2,
29
- maximumFractionDigits: 2,
30
- }).format(value)
31
- }
package/src/health.ts DELETED
@@ -1,90 +0,0 @@
1
- /**
2
- * Health interno del runtime storefront.
3
- * GET /ping → liveness puro: "ok" 200 (target del probe de vm-manager).
4
- * GET /__ecomiq/health → proceso up + estado de /v1/settings (sin SSR de páginas).
5
- */
6
- import { getStorefrontClient } from './client'
7
-
8
- export const ECOMIQ_HEALTH_PATH = '/__ecomiq/health'
9
- export const ECOMIQ_PING_PATH = '/ping'
10
-
11
- export type EcomiqHealthSettingsStatus = {
12
- ok: boolean
13
- latencyMs: number
14
- storeId?: string
15
- domain?: string | null
16
- error?: string
17
- }
18
-
19
- export type EcomiqHealthResponse = {
20
- ok: true
21
- settings: EcomiqHealthSettingsStatus
22
- }
23
-
24
- async function probeSettings(): Promise<EcomiqHealthSettingsStatus> {
25
- const started = Date.now()
26
- try {
27
- const settings = await getStorefrontClient().getSettings()
28
- return {
29
- ok: true,
30
- latencyMs: Date.now() - started,
31
- storeId: settings.store.id,
32
- domain: settings.store.domain ?? settings.store.slug,
33
- }
34
- } catch (error) {
35
- return {
36
- ok: false,
37
- latencyMs: Date.now() - started,
38
- error: error instanceof Error ? error.message : String(error),
39
- }
40
- }
41
- }
42
-
43
- /**
44
- * Si el request es al health interno, responde JSON.
45
- * Si no, `null` para que el caller siga con el fetch normal de TanStack.
46
- */
47
- export async function handleEcomiqHealth(
48
- request: Request,
49
- ): Promise<Response | null> {
50
- const { pathname } = new URL(request.url)
51
- if (pathname !== ECOMIQ_HEALTH_PATH && pathname !== ECOMIQ_PING_PATH) {
52
- return null
53
- }
54
-
55
- if (request.method !== 'GET' && request.method !== 'HEAD') {
56
- return new Response(null, {
57
- status: 405,
58
- headers: { allow: 'GET, HEAD' },
59
- })
60
- }
61
-
62
- if (pathname === ECOMIQ_PING_PATH) {
63
- return new Response(request.method === 'HEAD' ? null : 'ok', {
64
- status: 200,
65
- headers: {
66
- 'content-type': 'text/plain; charset=utf-8',
67
- 'cache-control': 'no-store',
68
- },
69
- })
70
- }
71
-
72
- const body: EcomiqHealthResponse = {
73
- ok: true,
74
- settings: await probeSettings(),
75
- }
76
-
77
- if (request.method === 'HEAD') {
78
- return new Response(null, {
79
- status: 200,
80
- headers: { 'content-type': 'application/json; charset=utf-8' },
81
- })
82
- }
83
-
84
- return Response.json(body, {
85
- status: 200,
86
- headers: {
87
- 'cache-control': 'no-store',
88
- },
89
- })
90
- }
@@ -1,9 +0,0 @@
1
- export {
2
- listRecentlyViewed,
3
- trackRecentlyViewed,
4
- toRecentlyViewedItem,
5
- useRecentlyViewed,
6
- useTrackRecentlyViewed,
7
- type RecentlyViewedItem,
8
- type TrackableProduct,
9
- } from './recently-viewed'