@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
|
@@ -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
|
-
}
|
package/src/pages/account.tsx
DELETED
|
@@ -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
|
-
})
|
package/src/pages/api.$.ts
DELETED
|
@@ -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
|
package/src/pages/api.auth.$.ts
DELETED
|
@@ -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
|
-
}
|
package/src/pages/checkout.tsx
DELETED
|
@@ -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,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Página de contenido (`/pages/$pageSlug`).
|
|
3
|
-
* La Definition es la dueña de estas rutas; el CMS queda solo como fallback
|
|
4
|
-
* de lectura para tiendas que aún no migraron sus páginas.
|
|
5
|
-
*/
|
|
6
|
-
import type { CmsPageDetailsDto } from '@ecomiq/core'
|
|
7
|
-
import type { ActiveStorefrontPage } from '../storefront-runtime'
|
|
8
|
-
import {
|
|
9
|
-
DefinitionSections,
|
|
10
|
-
definePage,
|
|
11
|
-
sanitizeHtml,
|
|
12
|
-
serverApi,
|
|
13
|
-
stripHtml,
|
|
14
|
-
z,
|
|
15
|
-
} from '../index'
|
|
16
|
-
import { StorefrontError } from '../ui'
|
|
17
|
-
|
|
18
|
-
const ContentPageSettingsSchema = z.object({
|
|
19
|
-
title: z.string().optional(),
|
|
20
|
-
html: z.string().optional(),
|
|
21
|
-
seo: z.object({
|
|
22
|
-
title: z.string().optional(),
|
|
23
|
-
description: z.string().optional(),
|
|
24
|
-
}).optional(),
|
|
25
|
-
}).passthrough()
|
|
26
|
-
|
|
27
|
-
type ContentPageData =
|
|
28
|
-
| Extract<ActiveStorefrontPage, { kind: 'definition' }>
|
|
29
|
-
| {
|
|
30
|
-
kind: 'cms'
|
|
31
|
-
page: CmsPageDetailsDto
|
|
32
|
-
content: z.infer<typeof ContentPageSettingsSchema>
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default definePage<ContentPageData, { pageSlug: string }>({
|
|
36
|
-
loader: async ({ params }) => {
|
|
37
|
-
const resolved = await serverApi.getActiveStorefrontPage(
|
|
38
|
-
`/pages/${params.pageSlug}`,
|
|
39
|
-
)
|
|
40
|
-
if (resolved.kind === 'definition') return resolved
|
|
41
|
-
|
|
42
|
-
const page = await serverApi.getPageBySlug(params.pageSlug)
|
|
43
|
-
if (page.status !== 'published') throw new Error('Página no publicada')
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
kind: 'cms' as const,
|
|
47
|
-
page,
|
|
48
|
-
content: ContentPageSettingsSchema.parse(page.settings ?? {}),
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
meta: ({ data, settings }) => {
|
|
53
|
-
if (data.kind === 'definition') {
|
|
54
|
-
const { page } = data
|
|
55
|
-
return {
|
|
56
|
-
title: page.seo.title ?? page.title ?? settings?.store.name ?? '',
|
|
57
|
-
...(page.seo.description === undefined
|
|
58
|
-
? {}
|
|
59
|
-
: { description: page.seo.description }),
|
|
60
|
-
...(page.seo.noindex ? { noIndex: true } : {}),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return {
|
|
64
|
-
title: data.content.seo?.title ?? data.content.title ?? data.page.name,
|
|
65
|
-
description: data.content.seo?.description ?? (
|
|
66
|
-
data.content.html
|
|
67
|
-
? stripHtml(data.content.html).slice(0, 155)
|
|
68
|
-
: undefined
|
|
69
|
-
),
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
error: () => (
|
|
74
|
-
<StorefrontError
|
|
75
|
-
title="Página no disponible"
|
|
76
|
-
description="El contenido no existe o todavía no está publicado."
|
|
77
|
-
/>
|
|
78
|
-
),
|
|
79
|
-
|
|
80
|
-
component: ({ data, settings }) => {
|
|
81
|
-
if (data.kind === 'definition') {
|
|
82
|
-
return (
|
|
83
|
-
<DefinitionSections
|
|
84
|
-
definition={data.definition}
|
|
85
|
-
pageId={data.page.id}
|
|
86
|
-
data={data.data}
|
|
87
|
-
locale={settings.store.language}
|
|
88
|
-
currency={settings.store.currency}
|
|
89
|
-
/>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
return (
|
|
93
|
-
<article className="mx-auto w-full max-w-4xl px-4 py-10 sm:py-14">
|
|
94
|
-
<header className="mb-8 border-b pb-8">
|
|
95
|
-
<h1 className="text-balance text-3xl font-semibold tracking-[-0.02em] md:text-4xl">
|
|
96
|
-
{data.content.title ?? data.page.name}
|
|
97
|
-
</h1>
|
|
98
|
-
</header>
|
|
99
|
-
|
|
100
|
-
{data.content.html ? (
|
|
101
|
-
<div
|
|
102
|
-
className="prose prose-neutral max-w-none text-foreground prose-headings:font-semibold prose-a:text-primary"
|
|
103
|
-
dangerouslySetInnerHTML={{ __html: sanitizeHtml(data.content.html) }}
|
|
104
|
-
/>
|
|
105
|
-
) : (
|
|
106
|
-
<p className="text-muted-foreground">Contenido no disponible.</p>
|
|
107
|
-
)}
|
|
108
|
-
</article>
|
|
109
|
-
)
|
|
110
|
-
},
|
|
111
|
-
})
|