@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.
- package/LICENSE +21 -0
- package/README.md +150 -123
- package/dist/account-Dx4e3syi.js +258 -0
- package/dist/account-forms-AVk71ko7.js +39 -0
- package/dist/account-forms.d.ts +13 -0
- package/dist/account.d.ts +240 -0
- package/dist/address-defaults-BqjRhS82.js +14 -0
- package/dist/address-defaults.d.ts +11 -0
- package/dist/analytics/analytics-CG1Qagwl.js +148 -0
- package/dist/analytics/analytics.d.ts +1 -0
- package/dist/analytics/config-DJr-GRhs.js +62 -0
- package/dist/analytics/config.d.ts +22 -0
- package/dist/analytics/consent-B13rBpBs.js +57 -0
- package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
- package/dist/analytics/consent-banner.d.ts +20 -0
- package/dist/analytics/cookies-pLozllyB.js +100 -0
- package/dist/analytics/cookies.d.ts +6 -0
- package/dist/analytics/destinations-iECrlLIr.js +144 -0
- package/dist/analytics/events-Dt81tIp1.js +102 -0
- package/dist/analytics/events.d.ts +51 -0
- package/dist/analytics/tracker-p4yaoOyk.js +51 -0
- package/dist/api-cache-DsuIn5hX.js +159 -0
- package/dist/bank-accounts-DlEczQQ5.js +81 -0
- package/dist/bank-accounts.d.ts +4 -0
- package/dist/captcha-DAqaIx8m.js +117 -0
- package/dist/captcha.d.ts +34 -0
- package/dist/cart-D7DxZ2lD.js +30 -0
- package/dist/cart-actions-Bsub_FFG.js +116 -0
- package/dist/cart-actions.d.ts +15 -0
- package/dist/cart-drawer-B7PPsR69.js +22 -0
- package/dist/cart-drawer.d.ts +9 -0
- package/dist/cart-form-y9P1r_EX.js +81 -0
- package/dist/cart-form.d.ts +24 -0
- package/dist/cart-summary-ur2VWKr-.js +89 -0
- package/dist/cart-summary.d.ts +31 -0
- package/dist/cart.d.ts +4 -0
- package/dist/catalog-7gJ-l5hW.js +67 -0
- package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
- package/dist/catalog-hooks.d.ts +111 -0
- package/dist/catalog.d.ts +5 -0
- package/dist/checkout-DVWSXANi.js +474 -0
- package/dist/checkout-form-CbOIdENC.js +131 -0
- package/dist/checkout-form.d.ts +20 -0
- package/dist/checkout.d.ts +86 -0
- package/dist/complaint-book-BvwLvfx0.js +117 -0
- package/dist/complaint-book.d.ts +62 -0
- package/dist/culqi-authentication-BGr20vAJ.js +204 -0
- package/dist/culqi-authentication.d.ts +15 -0
- package/dist/culqi-checkout-CTXpKgp_.js +55 -0
- package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
- package/dist/culqi-three-ds.d.ts +29 -0
- package/dist/customer-avatar-CJXSDroK.js +12 -0
- package/dist/customer-avatar.d.ts +5 -0
- package/dist/customer-cookies-DHOgFAMV.js +45 -0
- package/dist/customer-csrf-C1oDX-Py.js +17 -0
- package/dist/customer-csrf.d.ts +3 -0
- package/dist/customer-document-fields-C1K-_DAD.js +70 -0
- package/dist/customer-document-fields.d.ts +9 -0
- package/dist/customer-login-form-C7AC6vXd.js +144 -0
- package/dist/customer-login-form.d.ts +17 -0
- package/dist/customer-registration-form-BEm_NEfP.js +93 -0
- package/dist/customer-registration-form.d.ts +8 -0
- package/dist/document-url-CGsAl5Tf.js +9 -0
- package/dist/documents-T1mzc_j8.js +38 -0
- package/dist/documents.d.ts +16 -0
- package/dist/entries/account.d.ts +11 -0
- package/dist/entries/account.js +10 -0
- package/dist/entries/analytics.d.ts +6 -0
- package/dist/entries/analytics.js +5 -0
- package/dist/entries/cart.d.ts +8 -0
- package/dist/entries/cart.js +7 -0
- package/dist/entries/checkout.d.ts +12 -0
- package/dist/entries/checkout.js +11 -0
- package/dist/entries/order.d.ts +11 -0
- package/dist/entries/order.js +11 -0
- package/dist/favicon-YpyhVJ9H.js +28 -0
- package/dist/favicon.d.ts +4 -0
- package/dist/form-BtQcbXlJ.js +18 -0
- package/dist/form-contracts-BNpHaTGJ.js +166 -0
- package/dist/form-contracts.d.ts +42 -0
- package/dist/form.d.ts +3 -0
- package/dist/hidden-CCWtOWKR.js +3 -0
- package/dist/hidden.d.ts +4 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +50 -0
- package/dist/invoice-fields-BG0HjL6T.js +103 -0
- package/dist/invoice-fields.d.ts +39 -0
- package/dist/links-kFxBZfdb.js +96 -0
- package/dist/links.d.ts +21 -0
- package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
- package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
- package/dist/mercadopago-dialog.d.ts +21 -0
- package/dist/money-DtGOtDri.js +32 -0
- package/dist/money.d.ts +1 -0
- package/dist/optimistic-cart-BveVOHuh.js +53 -0
- package/dist/optimistic-cart.d.ts +16 -0
- package/dist/order-display-DTt5WBbc.js +112 -0
- package/dist/order-display.d.ts +20 -0
- package/dist/order-draft-DxV1Uhm3.js +38 -0
- package/dist/order-draft.d.ts +16 -0
- package/dist/order-pages-Dm6l5m0m.js +38 -0
- package/dist/order-pages.d.ts +12 -0
- package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
- package/dist/order-payment-continuation.d.ts +10 -0
- package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
- package/dist/order-payment-in-review.d.ts +5 -0
- package/dist/order-status-BJXLJwxq.js +162 -0
- package/dist/order-status.d.ts +89 -0
- package/dist/order-view-9IlBg5AF.js +62 -0
- package/dist/order-view.d.ts +12 -0
- package/dist/page-action-Ctkf4xC6.js +7 -0
- package/dist/page-seo-a5FJl8x5.js +262 -0
- package/dist/page-seo.d.ts +34 -0
- package/dist/pages-Ca_Gt6mq.js +150 -0
- package/dist/pages.d.ts +45 -0
- package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
- package/dist/payment-logo-BoPaGJc_.js +46 -0
- package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
- package/dist/payment-logo.d.ts +11 -0
- package/dist/payment-logos-BYvdwYyJ.js +23 -0
- package/dist/payment-methods-B8ocTgFZ.js +73 -0
- package/dist/payment-methods.d.ts +58 -0
- package/dist/payment-return-5cjypwIw.js +7 -0
- package/dist/place-CmW4hBWY.js +13 -0
- package/dist/place.d.ts +4 -0
- package/dist/preview-BiIBif3C.js +427 -0
- package/dist/preview-address-Cf0sJ-9p.js +21 -0
- package/dist/preview-cart-Dg2CVktS.js +376 -0
- package/dist/product-preview-BspMAecJ.js +51 -0
- package/dist/product-preview.d.ts +5 -0
- package/dist/product-variants-BNDHvs1y.js +46 -0
- package/dist/product-variants.d.ts +12 -0
- package/dist/public-pages-XtwlaLwt.js +194 -0
- package/dist/public-pages.d.ts +67 -0
- package/dist/registration-CpDcv8ql.js +91 -0
- package/dist/registration.d.ts +12 -0
- package/dist/request-security-CRsRMgwj.js +76 -0
- package/dist/rich-text-M-Ju8RL4.js +9 -0
- package/dist/rich-text.d.ts +5 -0
- package/dist/route-text-DcJw2SsK.js +9 -0
- package/dist/route-text.d.ts +4 -0
- package/dist/section-preview-CmsMl3yu.js +83 -0
- package/dist/section-preview.d.ts +12 -0
- package/dist/sections/account-addresses.d.ts +18 -0
- package/dist/sections/account-addresses.js +375 -0
- package/dist/sections/account-order.d.ts +12 -0
- package/dist/sections/account-order.js +592 -0
- package/dist/sections/account-orders.d.ts +8 -0
- package/dist/sections/account-orders.js +359 -0
- package/dist/sections/account-profile.d.ts +16 -0
- package/dist/sections/account-profile.js +123 -0
- package/dist/sections/checkout.d.ts +24 -0
- package/dist/sections/checkout.js +963 -0
- package/dist/sections/complaint-book.d.ts +8 -0
- package/dist/sections/complaint-book.js +538 -0
- package/dist/sections/design-system.d.ts +1 -0
- package/dist/sections/design-system.js +310 -0
- package/dist/sections/draft-invoice.d.ts +20 -0
- package/dist/sections/draft-invoice.js +158 -0
- package/dist/sections/order-tracking.d.ts +19 -0
- package/dist/sections/order-tracking.js +141 -0
- package/dist/sections-CNUMWTF_.js +77 -0
- package/dist/sections.d.ts +19 -0
- package/dist/seo-GpDRhAAA.js +78 -0
- package/dist/session-CKvmFJyg.js +379 -0
- package/dist/session.d.ts +24 -0
- package/dist/settings-B0NO2A6Y.js +78 -0
- package/dist/settings.d.ts +51 -0
- package/dist/source-marker-DhqSkW7n.js +4 -0
- package/dist/sources-B-3JFJfb.js +46 -0
- package/dist/sources.d.ts +7 -0
- package/dist/store-8Q-_LFrQ.js +152 -0
- package/dist/store-contact--hY6CoFb.js +19 -0
- package/dist/store-contact.d.ts +6 -0
- package/dist/store.d.ts +17 -0
- package/dist/storefront-language-DrMjVbmk.js +29 -0
- package/dist/storefront-language.d.ts +14 -0
- package/dist/storefront-text-D25ae1qf.js +11 -0
- package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
- package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
- package/dist/storefront-text.d.ts +4 -0
- package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
- package/dist/stripe-dialog.d.ts +30 -0
- package/dist/telemetry-HuZXWBPi.js +114 -0
- package/dist/telemetry.d.ts +10 -0
- package/dist/templates-CAqoNyTW.js +40 -0
- package/dist/templates.d.ts +15 -0
- package/dist/urls-CpGQPxvf.js +71 -0
- package/dist/urls.d.ts +18 -0
- package/dist/use-checkout-CZcxXJXi.js +42 -0
- package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
- package/dist/use-checkout-payment.d.ts +18 -0
- package/dist/use-checkout.d.ts +36 -0
- package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
- package/dist/use-order-payment-BKIz_IO0.js +45 -0
- package/dist/use-order-payment.d.ts +18 -0
- package/dist/use-payment-token-CErx-tN-.js +105 -0
- package/dist/vite-payment-logos-kziu7wKb.js +27 -0
- package/dist/vite-payment-logos.d.ts +24 -0
- package/dist/vite-public-assets-DW2fgtVb.js +209 -0
- package/dist/vite-public-assets.d.ts +29 -0
- package/dist/vite-sections-DTBr0-Kg.js +464 -0
- package/dist/vite-sections.d.ts +21 -0
- package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
- package/dist/vite-theme-ui.d.ts +10 -0
- package/dist/vite.d.ts +36 -0
- package/dist/vite.js +127 -0
- package/dist/voucher-payment-CVFDZ1L8.js +66 -0
- package/dist/worker-CNi-gzCu.js +86 -0
- package/dist/worker.d.ts +27 -0
- package/package.json +100 -81
- package/src/sections/account-addresses.tsx +215 -0
- package/src/sections/account-order.tsx +424 -0
- package/src/sections/account-orders.tsx +259 -0
- package/src/sections/account-profile.tsx +70 -0
- package/src/sections/checkout.tsx +586 -0
- package/src/sections/complaint-book.tsx +325 -0
- package/src/sections/design-system.tsx +146 -0
- package/src/sections/draft-invoice.tsx +105 -0
- package/src/sections/order-tracking.tsx +81 -0
- package/src/vite-client.d.ts +4 -0
- package/bin/ecomiq.mjs +0 -179
- package/public/design-bridge.js +0 -318
- package/src/api-proxy.ts +0 -194
- package/src/auth/create-storefront-auth.ts +0 -94
- package/src/auth/ecomiq-oauth-password-client.ts +0 -26
- package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
- package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
- package/src/auth/ecomiq-oauth-password-types.ts +0 -24
- package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
- package/src/auth/env.ts +0 -7
- package/src/auth/secure-cookies.ts +0 -23
- package/src/auth/storefront-auth-base-url.ts +0 -117
- package/src/auth/storefront-auth.ts +0 -7
- package/src/cart-session.ts +0 -239
- package/src/client.ts +0 -48
- package/src/cloud-runtime-client.ts +0 -378
- package/src/codegen.mjs +0 -789
- package/src/components/account/customer-addresses-panel.tsx +0 -678
- package/src/components/account/customer-order-detail-panel.tsx +0 -285
- package/src/components/account/customer-orders-panel.tsx +0 -192
- package/src/components/account/customer-profile-panel.tsx +0 -379
- package/src/components/checkout/cart-sync.ts +0 -143
- package/src/components/checkout/checkout-contact-section.tsx +0 -76
- package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
- package/src/components/checkout/checkout-form-controls.tsx +0 -139
- package/src/components/checkout/checkout-line-items.tsx +0 -43
- package/src/components/checkout/checkout-order-summary.tsx +0 -165
- package/src/components/checkout/checkout-payment-section.tsx +0 -212
- package/src/components/checkout/checkout-session-view.tsx +0 -564
- package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
- package/src/components/checkout/checkout-shipping-section.tsx +0 -98
- package/src/components/checkout/checkout-states.tsx +0 -73
- package/src/components/checkout/checkout-step-card.tsx +0 -101
- package/src/components/checkout/checkout-steps.ts +0 -111
- package/src/components/checkout/form-schema.ts +0 -281
- package/src/components/checkout/format.ts +0 -15
- package/src/components/checkout/index.ts +0 -5
- package/src/components/checkout/map-customer-to-checkout.ts +0 -188
- package/src/components/checkout/types.ts +0 -41
- package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
- package/src/components/checkout/use-checkout-geo.ts +0 -50
- package/src/components/checkout/use-checkout-session.ts +0 -640
- package/src/components/hero.tsx +0 -291
- package/src/config.d.ts +0 -94
- package/src/config.mjs +0 -146
- package/src/format-money.ts +0 -31
- package/src/health.ts +0 -90
- package/src/hooks/index.ts +0 -9
- package/src/hooks/recently-viewed.ts +0 -125
- package/src/index.ts +0 -354
- package/src/link.tsx +0 -2
- package/src/pages/account/account-page-skeleton.tsx +0 -69
- package/src/pages/account/account-page-view.tsx +0 -270
- package/src/pages/account/account-shell.tsx +0 -101
- package/src/pages/account/parse-account-route.ts +0 -36
- package/src/pages/account.tsx +0 -16
- package/src/pages/api.$.ts +0 -20
- package/src/pages/api.auth.$.ts +0 -16
- package/src/pages/checkout.$checkoutId.tsx +0 -60
- package/src/pages/checkout.tsx +0 -31
- package/src/pages/pages.$pageSlug.tsx +0 -111
- package/src/pages/thank-you.tsx +0 -53
- package/src/providers/auth.tsx +0 -96
- package/src/providers/cart.tsx +0 -275
- package/src/providers/ecomiq.ts +0 -137
- package/src/providers/index.ts +0 -26
- package/src/proxy-api.ts +0 -20
- package/src/public-config.ts +0 -24
- package/src/review-submission.ts +0 -89
- package/src/runtime/context.ts +0 -2
- package/src/runtime/default-layout.tsx +0 -64
- package/src/runtime/head.ts +0 -149
- package/src/runtime/page.tsx +0 -143
- package/src/runtime/postcss-browser-stub.mjs +0 -5
- package/src/server-config-store.ts +0 -35
- package/src/server.ts +0 -301
- package/src/storefront-definition/contract.ts +0 -1
- package/src/storefront-definition/data.ts +0 -188
- package/src/storefront-definition/index.ts +0 -2
- package/src/storefront-definition/renderer.tsx +0 -1481
- package/src/storefront-layout.ts +0 -59
- package/src/storefront-runtime.ts +0 -173
- package/src/styles.css +0 -97
- package/src/ui/badge.tsx +0 -32
- package/src/ui/button.tsx +0 -31
- package/src/ui/card.tsx +0 -31
- package/src/ui/carousel.tsx +0 -218
- package/src/ui/cn.ts +0 -6
- package/src/ui/index.ts +0 -8
- package/src/ui/separator.tsx +0 -6
- package/src/ui/skeleton.tsx +0 -6
- package/src/ui/storefront-error.tsx +0 -49
- package/src/utils.ts +0 -36
- package/src/vite-inline.mjs +0 -263
- package/templates/__root.tsx +0 -83
- package/templates/router.tsx +0 -19
package/src/review-submission.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CreateProductReviewParams,
|
|
3
|
-
StorefrontReview,
|
|
4
|
-
} from '@ecomiq/core'
|
|
5
|
-
import { ApiError } from '@ecomiq/core'
|
|
6
|
-
import { z } from 'zod'
|
|
7
|
-
|
|
8
|
-
const ReviewProblemSchema = z.object({
|
|
9
|
-
code: z.string().max(120).regex(/^[A-Za-z0-9._-]+$/).optional(),
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
interface ProductReviewClient {
|
|
13
|
-
createProductReview(
|
|
14
|
-
params: CreateProductReviewParams,
|
|
15
|
-
): Promise<StorefrontReview>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface ProductReviewSubmissionError {
|
|
19
|
-
status: 400 | 409 | 429 | 500
|
|
20
|
-
code?: string
|
|
21
|
-
message: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type ProductReviewSubmissionResult =
|
|
25
|
-
| {
|
|
26
|
-
ok: true
|
|
27
|
-
review: StorefrontReview
|
|
28
|
-
}
|
|
29
|
-
| {
|
|
30
|
-
ok: false
|
|
31
|
-
error: ProductReviewSubmissionError
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function submitProductReview(
|
|
35
|
-
client: ProductReviewClient,
|
|
36
|
-
input: CreateProductReviewParams,
|
|
37
|
-
): Promise<ProductReviewSubmissionResult> {
|
|
38
|
-
try {
|
|
39
|
-
const review = await client.createProductReview(input)
|
|
40
|
-
return { ok: true, review }
|
|
41
|
-
} catch (error) {
|
|
42
|
-
if (!(error instanceof ApiError)) throw error
|
|
43
|
-
|
|
44
|
-
let code: string | undefined
|
|
45
|
-
const problem = ReviewProblemSchema.safeParse(error.body)
|
|
46
|
-
if (problem.success) code = problem.data.code
|
|
47
|
-
|
|
48
|
-
if (error.status === 400) {
|
|
49
|
-
let message = 'Revisa los datos de la reseña e inténtalo nuevamente.'
|
|
50
|
-
if (
|
|
51
|
-
code === 'ProductReview.OrderInvalid' ||
|
|
52
|
-
code === 'ProductReview.OrderOwnerMismatch' ||
|
|
53
|
-
code === 'ProductReview.ProductNotPurchased'
|
|
54
|
-
) {
|
|
55
|
-
message = 'No pudimos verificar la compra con los datos ingresados.'
|
|
56
|
-
}
|
|
57
|
-
return { ok: false, error: { status: 400, code, message } }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (error.status === 409) {
|
|
61
|
-
let message = 'No pudimos registrar esta reseña porque ya existe una similar.'
|
|
62
|
-
if (code === 'ProductReview.AlreadyExists') {
|
|
63
|
-
message = 'Ya enviaste una reseña para este producto.'
|
|
64
|
-
}
|
|
65
|
-
return { ok: false, error: { status: 409, code, message } }
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (error.status === 429) {
|
|
69
|
-
return {
|
|
70
|
-
ok: false,
|
|
71
|
-
error: {
|
|
72
|
-
status: 429,
|
|
73
|
-
code,
|
|
74
|
-
message:
|
|
75
|
-
'Enviaste varias reseñas en poco tiempo. Espera un momento e inténtalo nuevamente.',
|
|
76
|
-
},
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
ok: false,
|
|
82
|
-
error: {
|
|
83
|
-
status: 500,
|
|
84
|
-
code,
|
|
85
|
-
message: 'No pudimos enviar la reseña. Inténtalo nuevamente.',
|
|
86
|
-
},
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
package/src/runtime/context.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layout por defecto de Ecomiq.
|
|
3
|
-
* Nav y footer salen de settings.cms.menus (main / footer).
|
|
4
|
-
*/
|
|
5
|
-
import { Link } from '@tanstack/react-router'
|
|
6
|
-
import { getMenuItems } from '@ecomiq/core'
|
|
7
|
-
import type { LayoutProps } from '../index'
|
|
8
|
-
|
|
9
|
-
export default function DefaultLayout({ children, settings }: LayoutProps) {
|
|
10
|
-
const mainItems = getMenuItems(settings, 'main')
|
|
11
|
-
const footerItems = getMenuItems(settings, 'footer')
|
|
12
|
-
const nav =
|
|
13
|
-
mainItems.length > 0
|
|
14
|
-
? mainItems.map((item) => ({ label: item.label, to: item.url, target: item.target }))
|
|
15
|
-
: [{ label: 'Productos', to: '/', target: null as string | null }]
|
|
16
|
-
|
|
17
|
-
const storeName = settings?.store.name ?? 'Tienda'
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className="flex min-h-dvh flex-col">
|
|
21
|
-
<header className="sticky top-0 z-40 border-b bg-background/90 backdrop-blur supports-[backdrop-filter]:bg-background/75">
|
|
22
|
-
<div className="mx-auto flex h-14 w-full max-w-6xl items-center justify-between px-4">
|
|
23
|
-
<Link to="/" className="font-semibold tracking-tight text-lg">
|
|
24
|
-
{storeName}
|
|
25
|
-
</Link>
|
|
26
|
-
<nav className="flex items-center gap-5 text-sm text-muted-foreground">
|
|
27
|
-
{nav.map((item) =>
|
|
28
|
-
item.target === '_blank' ? (
|
|
29
|
-
<a
|
|
30
|
-
key={item.to}
|
|
31
|
-
href={item.to}
|
|
32
|
-
target="_blank"
|
|
33
|
-
rel="noreferrer"
|
|
34
|
-
className="transition-colors hover:text-foreground"
|
|
35
|
-
>
|
|
36
|
-
{item.label}
|
|
37
|
-
</a>
|
|
38
|
-
) : (
|
|
39
|
-
<Link key={item.to} to={item.to} className="transition-colors hover:text-foreground">
|
|
40
|
-
{item.label}
|
|
41
|
-
</Link>
|
|
42
|
-
),
|
|
43
|
-
)}
|
|
44
|
-
</nav>
|
|
45
|
-
</div>
|
|
46
|
-
</header>
|
|
47
|
-
<main className="mx-auto w-full max-w-6xl flex-1 px-4 py-8">{children}</main>
|
|
48
|
-
<footer className="border-t">
|
|
49
|
-
<div className="mx-auto flex w-full max-w-6xl flex-col gap-4 px-4 py-6 text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between">
|
|
50
|
-
<p>{settings?.store.description || `${storeName} — creado con Ecomiq`}</p>
|
|
51
|
-
{footerItems.length > 0 && (
|
|
52
|
-
<nav className="flex flex-wrap gap-4">
|
|
53
|
-
{footerItems.map((item) => (
|
|
54
|
-
<Link key={item.url} to={item.url} className="transition-colors hover:text-foreground">
|
|
55
|
-
{item.label}
|
|
56
|
-
</Link>
|
|
57
|
-
))}
|
|
58
|
-
</nav>
|
|
59
|
-
)}
|
|
60
|
-
</div>
|
|
61
|
-
</footer>
|
|
62
|
-
</div>
|
|
63
|
-
)
|
|
64
|
-
}
|
package/src/runtime/head.ts
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime interno: construcción del <head>.
|
|
3
|
-
* Mezcla SEO/branding/tracking de `/v1/settings` con el `meta` de cada página
|
|
4
|
-
* (la página siempre gana) y genera los scripts de analytics del CMS.
|
|
5
|
-
*/
|
|
6
|
-
import type { StorefrontSettings } from '@ecomiq/core'
|
|
7
|
-
import type { PageMeta } from '../index'
|
|
8
|
-
import {
|
|
9
|
-
cmsBranding,
|
|
10
|
-
cmsSeo,
|
|
11
|
-
storeDisplayName,
|
|
12
|
-
storeSeoDescription,
|
|
13
|
-
} from '../utils'
|
|
14
|
-
|
|
15
|
-
type MetaTag = Record<string, string>
|
|
16
|
-
type LinkTag = Record<string, string>
|
|
17
|
-
type ScriptTag = Record<string, string | boolean> & { children?: string }
|
|
18
|
-
|
|
19
|
-
interface HeadResult {
|
|
20
|
-
meta?: MetaTag[]
|
|
21
|
-
links?: LinkTag[]
|
|
22
|
-
scripts?: ScriptTag[]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function serializeJsonLd(value: Record<string, unknown>) {
|
|
26
|
-
return JSON.stringify(value)
|
|
27
|
-
.replace(/</g, '\\u003c')
|
|
28
|
-
.replace(/\u2028/g, '\\u2028')
|
|
29
|
-
.replace(/\u2029/g, '\\u2029')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function siteName(settings: StorefrontSettings | null | undefined): string {
|
|
33
|
-
return storeDisplayName(settings) ?? 'Tienda'
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Aplica titleTemplate del CMS (`{page} | {site}` o `%s · Tienda`). */
|
|
37
|
-
export function resolveTitle(
|
|
38
|
-
pageTitle: string | undefined,
|
|
39
|
-
settings: StorefrontSettings | null | undefined,
|
|
40
|
-
): string {
|
|
41
|
-
const name = siteName(settings)
|
|
42
|
-
const seo = cmsSeo(settings)
|
|
43
|
-
if (!pageTitle) return seo?.metaTitle ?? name
|
|
44
|
-
|
|
45
|
-
const template = seo?.titleTemplate?.trim()
|
|
46
|
-
if (!template) return `${pageTitle} · ${name}`
|
|
47
|
-
|
|
48
|
-
return template
|
|
49
|
-
.replaceAll('{page}', pageTitle)
|
|
50
|
-
.replaceAll('{site}', name)
|
|
51
|
-
.replace('%s', pageTitle)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Head global: charset, viewport, favicon, tracking. Va en el root. */
|
|
55
|
-
export function buildRootHead(
|
|
56
|
-
settings: StorefrontSettings | null | undefined,
|
|
57
|
-
): HeadResult {
|
|
58
|
-
const branding = cmsBranding(settings)
|
|
59
|
-
const seo = cmsSeo(settings)
|
|
60
|
-
|
|
61
|
-
const meta: MetaTag[] = [
|
|
62
|
-
{ charSet: 'utf-8' },
|
|
63
|
-
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
|
64
|
-
]
|
|
65
|
-
if (seo?.robotsIndex === false) {
|
|
66
|
-
meta.push({ name: 'robots', content: 'noindex, nofollow' })
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const links: LinkTag[] = [
|
|
70
|
-
{ rel: 'icon', href: branding?.faviconUrl ?? '/favicon.ico' },
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
return { meta, links, scripts: buildAnalyticsScripts(settings) }
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Head por página: title (con plantilla), description, OG/Twitter, robots, metas extra y JSON-LD. */
|
|
77
|
-
export function buildPageHead(
|
|
78
|
-
pageMeta: PageMeta | undefined,
|
|
79
|
-
settings: StorefrontSettings | null | undefined,
|
|
80
|
-
): HeadResult {
|
|
81
|
-
const branding = cmsBranding(settings)
|
|
82
|
-
const seo = cmsSeo(settings)
|
|
83
|
-
const title = resolveTitle(pageMeta?.title, settings)
|
|
84
|
-
const description = pageMeta?.description ?? storeSeoDescription(settings) ?? undefined
|
|
85
|
-
const ogImage = pageMeta?.ogImage ?? branding?.socialImageUrl ?? undefined
|
|
86
|
-
const name = siteName(settings)
|
|
87
|
-
|
|
88
|
-
const meta: MetaTag[] = [{ title }]
|
|
89
|
-
if (description) meta.push({ name: 'description', content: description })
|
|
90
|
-
if (pageMeta?.noIndex || seo?.robotsIndex === false) {
|
|
91
|
-
meta.push({ name: 'robots', content: 'noindex, nofollow' })
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
meta.push({ property: 'og:title', content: title })
|
|
95
|
-
meta.push({ property: 'og:type', content: 'website' })
|
|
96
|
-
meta.push({ property: 'og:site_name', content: name })
|
|
97
|
-
if (description) meta.push({ property: 'og:description', content: description })
|
|
98
|
-
if (ogImage) {
|
|
99
|
-
meta.push({ property: 'og:image', content: ogImage })
|
|
100
|
-
meta.push({ name: 'twitter:card', content: 'summary_large_image' })
|
|
101
|
-
meta.push({ name: 'twitter:image', content: ogImage })
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
for (const extra of pageMeta?.meta ?? []) meta.push(extra as MetaTag)
|
|
105
|
-
|
|
106
|
-
const scripts: ScriptTag[] = []
|
|
107
|
-
if (pageMeta?.jsonLd) {
|
|
108
|
-
scripts.push({ type: 'application/ld+json', children: serializeJsonLd(pageMeta.jsonLd) })
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return { meta, ...(scripts.length ? { scripts } : {}) }
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** Traduce tracking + customScripts del CMS a etiquetas <script>. */
|
|
115
|
-
export function buildAnalyticsScripts(
|
|
116
|
-
settings: StorefrontSettings | null | undefined,
|
|
117
|
-
): ScriptTag[] {
|
|
118
|
-
const tracking = settings?.cms?.settings?.tracking
|
|
119
|
-
const custom = settings?.cms?.settings?.customScripts
|
|
120
|
-
const scripts: ScriptTag[] = []
|
|
121
|
-
|
|
122
|
-
if (tracking?.googleAnalyticsMeasurementId) {
|
|
123
|
-
const id = tracking.googleAnalyticsMeasurementId
|
|
124
|
-
scripts.push({ src: `https://www.googletagmanager.com/gtag/js?id=${id}`, async: true })
|
|
125
|
-
scripts.push({
|
|
126
|
-
children: `window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)};gtag('js',new Date());gtag('config','${id}');`,
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (tracking?.googleTagManagerContainerId) {
|
|
131
|
-
const id = tracking.googleTagManagerContainerId
|
|
132
|
-
scripts.push({
|
|
133
|
-
children: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','${id}');`,
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (tracking?.metaPixelId) {
|
|
138
|
-
const id = tracking.metaPixelId
|
|
139
|
-
scripts.push({
|
|
140
|
-
children: `!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,document,'script','https://connect.facebook.net/en_US/fbevents.js');fbq('init','${id}');fbq('track','PageView');`,
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (custom?.enabled && custom.head) {
|
|
145
|
-
scripts.push({ children: custom.head })
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return scripts
|
|
149
|
-
}
|
package/src/runtime/page.tsx
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime interno de Ecomiq (solo lo importa el código generado en `.ecomiq`).
|
|
3
|
-
* Traduce el contrato público `definePage` a las opciones del router interno.
|
|
4
|
-
*/
|
|
5
|
-
import { useContext } from 'react'
|
|
6
|
-
import { useRouterState } from '@tanstack/react-router'
|
|
7
|
-
import type { StorefrontSettings } from '@ecomiq/core'
|
|
8
|
-
import type { EcomiqPage, PublicEcomiqConfig } from '../index'
|
|
9
|
-
import { AppEcomiqContext } from '../providers/ecomiq'
|
|
10
|
-
import { serverApi } from '../server'
|
|
11
|
-
import { StorefrontError } from '../ui/storefront-error'
|
|
12
|
-
import { buildPageHead } from './head'
|
|
13
|
-
|
|
14
|
-
function isSettings(value: unknown): value is StorefrontSettings {
|
|
15
|
-
return Boolean(
|
|
16
|
-
value &&
|
|
17
|
-
typeof value === 'object' &&
|
|
18
|
-
value !== null &&
|
|
19
|
-
'store' in value,
|
|
20
|
-
)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function resolveSettingsForMeta(
|
|
24
|
-
matches?: Array<{ loaderData?: unknown }>,
|
|
25
|
-
): Promise<StorefrontSettings | undefined> {
|
|
26
|
-
// Preferir el loader del root si ya está en el match (hidratación cliente).
|
|
27
|
-
// En SSR `matches` suele venir stale (updateMatch no muta el array), así que
|
|
28
|
-
// caemos a serverApi: el cliente core cachea settings y no hay HTTP extra.
|
|
29
|
-
const fromRoot = matches?.[0]?.loaderData
|
|
30
|
-
if (isSettings(fromRoot)) {
|
|
31
|
-
return fromRoot
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
return await serverApi.getSettings()
|
|
35
|
-
} catch {
|
|
36
|
-
return undefined
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function usePageSettings(): StorefrontSettings | undefined {
|
|
41
|
-
// Mismo módulo que AppEcomiqProvider (no el barrel) para evitar contextos duplicados.
|
|
42
|
-
const fromProvider = useContext(AppEcomiqContext)?.settings
|
|
43
|
-
const fromRootLoader = useRouterState({
|
|
44
|
-
select: (state) => {
|
|
45
|
-
const data = state.matches[0]?.loaderData
|
|
46
|
-
return isSettings(data) ? data : undefined
|
|
47
|
-
},
|
|
48
|
-
})
|
|
49
|
-
return fromProvider ?? fromRootLoader
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function pageToRouteOptions<
|
|
53
|
-
TData,
|
|
54
|
-
TParams extends Record<string, string>,
|
|
55
|
-
TSearch extends Record<string, unknown>,
|
|
56
|
-
>(
|
|
57
|
-
page: EcomiqPage<TData, TParams, TSearch>,
|
|
58
|
-
config: PublicEcomiqConfig,
|
|
59
|
-
) {
|
|
60
|
-
const readSearch = page.validateSearch ?? ((raw: Record<string, unknown>) => raw as TSearch)
|
|
61
|
-
const reloadOnSearch = page.reloadOnSearch !== false
|
|
62
|
-
const stableLoaderDeps: Record<string, never> = {}
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
preload: page.preload,
|
|
66
|
-
shouldReload: reloadOnSearch ? undefined : false,
|
|
67
|
-
validateSearch: readSearch,
|
|
68
|
-
loaderDeps: ({ search }: { search: TSearch }) => reloadOnSearch ? search : stableLoaderDeps,
|
|
69
|
-
loader: page.loader
|
|
70
|
-
? ({ params, deps, location }: {
|
|
71
|
-
params: TParams
|
|
72
|
-
deps: TSearch | Record<string, never>
|
|
73
|
-
location?: { search?: Record<string, unknown> }
|
|
74
|
-
}) => {
|
|
75
|
-
let search = readSearch(location?.search ?? {})
|
|
76
|
-
if (reloadOnSearch) {
|
|
77
|
-
search = deps as TSearch
|
|
78
|
-
}
|
|
79
|
-
return page.loader!({
|
|
80
|
-
params,
|
|
81
|
-
search,
|
|
82
|
-
config,
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
: undefined,
|
|
86
|
-
head: async ({ loaderData, params, match, matches }: {
|
|
87
|
-
loaderData: TData
|
|
88
|
-
params: TParams
|
|
89
|
-
match: { search: TSearch }
|
|
90
|
-
matches?: Array<{ loaderData?: unknown }>
|
|
91
|
-
}) => {
|
|
92
|
-
// Con loader: si TanStack calcula head antes de tener datos, degrada al head global.
|
|
93
|
-
// Sin loader: loaderData es siempre undefined — aún así hay que llamar meta.
|
|
94
|
-
const waitingForLoader = Boolean(page.loader) && loaderData === undefined
|
|
95
|
-
if (waitingForLoader) return buildPageHead(undefined, undefined)
|
|
96
|
-
|
|
97
|
-
const settings = page.meta
|
|
98
|
-
? await resolveSettingsForMeta(matches)
|
|
99
|
-
: undefined
|
|
100
|
-
|
|
101
|
-
return buildPageHead(
|
|
102
|
-
page.meta?.({
|
|
103
|
-
data: loaderData,
|
|
104
|
-
params,
|
|
105
|
-
search: match.search,
|
|
106
|
-
config,
|
|
107
|
-
settings,
|
|
108
|
-
}),
|
|
109
|
-
settings,
|
|
110
|
-
)
|
|
111
|
-
},
|
|
112
|
-
pendingComponent: page.loading,
|
|
113
|
-
errorComponent: page.error
|
|
114
|
-
? ({ error, reset }: { error: Error; reset: () => void }) => page.error!({ error, reset })
|
|
115
|
-
: ({ reset }: { error: Error; reset: () => void }) => <StorefrontError onRetry={reset} />,
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function PageBoundary<
|
|
120
|
-
TData,
|
|
121
|
-
TParams extends Record<string, string>,
|
|
122
|
-
TSearch extends Record<string, unknown>,
|
|
123
|
-
>({
|
|
124
|
-
page,
|
|
125
|
-
data,
|
|
126
|
-
params,
|
|
127
|
-
search,
|
|
128
|
-
}: {
|
|
129
|
-
page: EcomiqPage<TData, TParams, TSearch>
|
|
130
|
-
data: TData
|
|
131
|
-
params: TParams
|
|
132
|
-
search: TSearch
|
|
133
|
-
}) {
|
|
134
|
-
const settings = usePageSettings()
|
|
135
|
-
// Concurrent render / contexto duplicado: no tumbar el root.
|
|
136
|
-
// El re-render sync con provider o el loader del root suelen recuperar.
|
|
137
|
-
if (!settings) {
|
|
138
|
-
const Loading = page.loading
|
|
139
|
-
return Loading ? <Loading /> : null
|
|
140
|
-
}
|
|
141
|
-
const Component = page.component
|
|
142
|
-
return <Component data={data} params={params} search={search} settings={settings} />
|
|
143
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { CreateApiOptions } from '@ecomiq/core'
|
|
2
|
-
import { createServerOnlyFn } from '@tanstack/react-start'
|
|
3
|
-
|
|
4
|
-
export interface ServerEcomiqCloudConfig {
|
|
5
|
-
baseUrl?: string
|
|
6
|
-
artifactBaseUrl?: string
|
|
7
|
-
storeSlug?: string
|
|
8
|
-
timeoutMs?: number
|
|
9
|
-
cacheTtlMs?: number
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface ServerEcomiqConfig {
|
|
13
|
-
domain: string
|
|
14
|
-
api?: CreateApiOptions
|
|
15
|
-
cloud?: ServerEcomiqCloudConfig
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const serverConfigKey = Symbol.for('@ecomiq/storefront/server-config')
|
|
19
|
-
const serverGlobal = globalThis as typeof globalThis & {
|
|
20
|
-
[key: symbol]: ServerEcomiqConfig | undefined
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const registerServerConfig = createServerOnlyFn((config: ServerEcomiqConfig): void => {
|
|
24
|
-
serverGlobal[serverConfigKey] = config
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
export const getServerConfig = createServerOnlyFn((): ServerEcomiqConfig => {
|
|
28
|
-
const registered = serverGlobal[serverConfigKey]
|
|
29
|
-
if (!registered) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
'Config server de Ecomiq no disponible. Ejecuta la app mediante `ecomiq dev|build`.',
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
return registered
|
|
35
|
-
})
|