@ecomiq/storefront 1.1.9 → 2.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/README.md +26 -418
- package/bin/ecomiq.mjs +154 -0
- package/package.json +72 -94
- package/src/api-proxy.ts +194 -0
- package/src/auth/create-storefront-auth.ts +35 -0
- package/src/auth/ecomiq-oauth-password-client.ts +26 -0
- package/src/auth/ecomiq-oauth-password-constants.ts +26 -0
- package/src/auth/ecomiq-oauth-password-plugin.ts +299 -0
- package/src/auth/ecomiq-oauth-password-types.ts +25 -0
- package/src/auth/ecomiq-storefront-sign-in.ts +47 -0
- package/src/auth/env.ts +7 -0
- package/src/cart-session.ts +241 -0
- package/src/client.ts +48 -0
- package/src/codegen.mjs +603 -0
- package/src/components/checkout/cart-sync.ts +143 -0
- package/src/components/checkout/checkout-external-update-banner.tsx +15 -0
- package/src/components/checkout/checkout-form-controls.tsx +139 -0
- package/src/components/checkout/checkout-line-items.tsx +43 -0
- package/src/components/checkout/checkout-order-summary.tsx +156 -0
- package/src/components/checkout/checkout-payment-section.tsx +215 -0
- package/src/components/checkout/checkout-session-view.tsx +240 -0
- package/src/components/checkout/checkout-shipping-address-section.tsx +266 -0
- package/src/components/checkout/checkout-shipping-section.tsx +90 -0
- package/src/components/checkout/checkout-states.tsx +73 -0
- package/src/components/checkout/form-schema.ts +229 -0
- package/src/components/checkout/format.ts +27 -0
- package/src/components/checkout/index.ts +51 -0
- package/src/components/checkout/types.ts +43 -0
- package/src/components/checkout/use-checkout-geo.ts +50 -0
- package/src/components/checkout/use-checkout-session.ts +583 -0
- package/src/components/hero.tsx +291 -0
- package/src/config.d.ts +69 -0
- package/src/config.mjs +165 -0
- package/src/index.ts +274 -0
- package/src/link.tsx +2 -0
- package/src/pages/checkout.$checkoutId.tsx +60 -0
- package/src/pages/checkout.tsx +31 -0
- package/src/pages/thank-you.tsx +53 -0
- package/src/providers/auth.tsx +69 -0
- package/src/providers/cart.tsx +275 -0
- package/src/providers/ecomiq.ts +137 -0
- package/src/providers/index.ts +26 -0
- package/src/public-config.ts +44 -0
- package/src/review-submission.ts +89 -0
- package/src/runtime/context.ts +1 -0
- package/src/runtime/default-layout.tsx +80 -0
- package/src/runtime/head.ts +126 -0
- package/src/runtime/page.tsx +82 -0
- package/src/runtime/postcss-browser-stub.mjs +5 -0
- package/src/server-config-store.ts +26 -0
- package/src/server.ts +279 -0
- package/src/styles.css +76 -0
- package/src/ui/badge.tsx +32 -0
- package/src/ui/button.tsx +31 -0
- package/src/ui/card.tsx +31 -0
- package/src/ui/carousel.tsx +218 -0
- package/src/ui/cn.ts +6 -0
- package/src/ui/index.ts +8 -0
- package/src/ui/separator.tsx +6 -0
- package/src/ui/skeleton.tsx +6 -0
- package/src/ui/storefront-error.tsx +49 -0
- package/src/utils.ts +36 -0
- package/src/vite-inline.mjs +95 -0
- package/templates/__root.tsx +77 -0
- package/templates/router.tsx +19 -0
- package/dist/chunk-64GLOWMJ.js +0 -216
- package/dist/chunk-64GLOWMJ.js.map +0 -1
- package/dist/chunk-AKD5IDEB.js +0 -177
- package/dist/chunk-AKD5IDEB.js.map +0 -1
- package/dist/chunk-EFEVJJWF.js +0 -1345
- package/dist/chunk-EFEVJJWF.js.map +0 -1
- package/dist/chunk-NAJQB5DN.js +0 -155
- package/dist/chunk-NAJQB5DN.js.map +0 -1
- package/dist/chunk-OABDGTBI.js +0 -253
- package/dist/chunk-OABDGTBI.js.map +0 -1
- package/dist/chunk-Q4U5OEUT.js +0 -21
- package/dist/chunk-Q4U5OEUT.js.map +0 -1
- package/dist/chunk-S7CPZ4UE.js +0 -6
- package/dist/chunk-S7CPZ4UE.js.map +0 -1
- package/dist/chunks/client.css +0 -984
- package/dist/chunks/client.css.map +0 -1
- package/dist/chunks/client.d.ts +0 -10759
- package/dist/chunks/client.js +0 -5398
- package/dist/chunks/client.js.map +0 -1
- package/dist/chunks/config.d.ts +0 -31
- package/dist/chunks/config.js +0 -5
- package/dist/chunks/config.js.map +0 -1
- package/dist/chunks/core-public.d.ts +0 -130
- package/dist/chunks/core-public.js +0 -92
- package/dist/chunks/core-public.js.map +0 -1
- package/dist/chunks/server-nextjs.d.ts +0 -33
- package/dist/chunks/server-nextjs.js +0 -59
- package/dist/chunks/server-nextjs.js.map +0 -1
- package/dist/chunks/server-tanstack.d.ts +0 -22
- package/dist/chunks/server-tanstack.js +0 -40
- package/dist/chunks/server-tanstack.js.map +0 -1
- package/dist/collection-DU7gREXS.d.ts +0 -4062
- package/dist/ecomiq-oauth-password-plugin-B_gMdGuc.d.ts +0 -3631
- package/dist/ecomiq-shell-ui-SWIFLQVM.js +0 -8
- package/dist/ecomiq-shell-ui-SWIFLQVM.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.js +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.server.d.ts +0 -9
- package/dist/index.server.js +0 -8
- package/dist/index.server.js.map +0 -1
- package/dist/nextjs/api.d.ts +0 -45
- package/dist/nextjs/api.js +0 -32
- package/dist/nextjs/api.js.map +0 -1
- package/dist/nextjs/globals.css +0 -13
- package/dist/nextjs.d.ts +0 -2
- package/dist/nextjs.js +0 -2
- package/dist/nextjs.js.map +0 -1
- package/dist/nextjs.server.d.ts +0 -18
- package/dist/nextjs.server.js +0 -1
- package/dist/nextjs.server.js.map +0 -1
- package/dist/postcss.config.mjs +0 -6
- package/dist/react/checkout-page.css +0 -855
- package/dist/react/client.css +0 -984
- package/dist/react/payment-provider.css +0 -91
- package/dist/react/thank-you-page.css +0 -105
- package/dist/react.d.ts +0 -1
- package/dist/react.js +0 -2
- package/dist/react.js.map +0 -1
- package/dist/store-proxy-handler-CNzKlTFA.d.ts +0 -10
- package/dist/tanstack/api.d.ts +0 -41
- package/dist/tanstack/api.js +0 -30
- package/dist/tanstack/api.js.map +0 -1
- package/dist/tanstack.d.ts +0 -1
- package/dist/tanstack.js +0 -2
- package/dist/tanstack.js.map +0 -1
- package/dist/tanstack.server.d.ts +0 -18
- package/dist/tanstack.server.js +0 -1
- package/dist/tanstack.server.js.map +0 -1
package/src/index.ts
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API pública de @ecomiq/storefront.
|
|
3
|
+
* Todo lo que el usuario importa sale de aquí o de '@ecomiq/storefront/ui'.
|
|
4
|
+
* Las herramientas internas (router, bundler, SSR) no se exponen.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactNode } from 'react'
|
|
7
|
+
import type { StorefrontSettings } from '@ecomiq/core'
|
|
8
|
+
import type { PublicEcomiqConfig } from './public-config'
|
|
9
|
+
|
|
10
|
+
export type {
|
|
11
|
+
EcomiqAnalyticsConfig,
|
|
12
|
+
EcomiqConfig,
|
|
13
|
+
EcomiqLayoutConfig,
|
|
14
|
+
EcomiqSeoConfig,
|
|
15
|
+
EcomiqThemeConfig,
|
|
16
|
+
PublicEcomiqConfig,
|
|
17
|
+
} from './public-config'
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
createApi,
|
|
21
|
+
createStorefrontClient,
|
|
22
|
+
ApiError,
|
|
23
|
+
DEFAULT_API_BASE_URL,
|
|
24
|
+
DEFAULT_API_VERSION,
|
|
25
|
+
} from '@ecomiq/core'
|
|
26
|
+
export type { Api, CreateApiOptions, StorefrontClient } from '@ecomiq/core'
|
|
27
|
+
export type { CreateApiOptions as CreateClientOptions } from '@ecomiq/core'
|
|
28
|
+
export {
|
|
29
|
+
CollectionSchema,
|
|
30
|
+
CollectionsSchema,
|
|
31
|
+
CountrySchema,
|
|
32
|
+
CountriesSchema,
|
|
33
|
+
DepartmentSchema,
|
|
34
|
+
DepartmentsSchema,
|
|
35
|
+
DistrictSchema,
|
|
36
|
+
DistrictsSchema,
|
|
37
|
+
GeoDivisionSchema,
|
|
38
|
+
GeoDivisionTypeSchema,
|
|
39
|
+
GetDepartmentsParamsSchema,
|
|
40
|
+
GetDistrictsParamsSchema,
|
|
41
|
+
GetProvincesParamsSchema,
|
|
42
|
+
ProvinceSchema,
|
|
43
|
+
ProvincesSchema,
|
|
44
|
+
StorefrontSettingsSchema,
|
|
45
|
+
StorefrontMenuSchema,
|
|
46
|
+
StorefrontMenuItemSchema,
|
|
47
|
+
getMenuItems,
|
|
48
|
+
} from '@ecomiq/core'
|
|
49
|
+
export { getCountries, getDepartments, getDistricts, getProvinces } from '@ecomiq/core'
|
|
50
|
+
export {
|
|
51
|
+
AddCartItemParamsSchema,
|
|
52
|
+
CreateCartParamsSchema,
|
|
53
|
+
CreateProductReviewParamsSchema,
|
|
54
|
+
GetCartParamsSchema,
|
|
55
|
+
ListProductReviewsParamsSchema,
|
|
56
|
+
ProductDetailParamsSchema,
|
|
57
|
+
ProductDetailSchema,
|
|
58
|
+
ProductAutocompleteParamsSchema,
|
|
59
|
+
ProductSuggestionSchema,
|
|
60
|
+
ProductSuggestionsSchema,
|
|
61
|
+
ProductSummarySchema,
|
|
62
|
+
ProductsParamsSchema,
|
|
63
|
+
ProductsResponseSchema,
|
|
64
|
+
RelatedProductsParamsSchema,
|
|
65
|
+
RemoveCartItemParamsSchema,
|
|
66
|
+
StorefrontCartSchema,
|
|
67
|
+
StorefrontReviewSchema,
|
|
68
|
+
StorefrontReviewsSchema,
|
|
69
|
+
CartCheckoutReadParamsSchema,
|
|
70
|
+
CartCheckoutParamsSchema,
|
|
71
|
+
GetShippingOptionsParamsSchema,
|
|
72
|
+
PutShippingMethodParamsSchema,
|
|
73
|
+
CartBuyerParamsSchema,
|
|
74
|
+
CartAddressesParamsSchema,
|
|
75
|
+
CartPromotionCodeParamsSchema,
|
|
76
|
+
UpdateCartItemQuantityParamsSchema,
|
|
77
|
+
cartVariantIdFromProduct,
|
|
78
|
+
sanitizeHtml,
|
|
79
|
+
stripHtml,
|
|
80
|
+
} from '@ecomiq/core'
|
|
81
|
+
export type {
|
|
82
|
+
Collection,
|
|
83
|
+
Collections,
|
|
84
|
+
Countries,
|
|
85
|
+
Country,
|
|
86
|
+
Department,
|
|
87
|
+
Departments,
|
|
88
|
+
District,
|
|
89
|
+
Districts,
|
|
90
|
+
GeoDivision,
|
|
91
|
+
GeoDivisionType,
|
|
92
|
+
GetDepartmentsParams,
|
|
93
|
+
GetDistrictsParams,
|
|
94
|
+
GetProvincesParams,
|
|
95
|
+
Province,
|
|
96
|
+
Provinces,
|
|
97
|
+
StorefrontSettings,
|
|
98
|
+
StorefrontMenu,
|
|
99
|
+
StorefrontMenuItem,
|
|
100
|
+
AddCartItemParams,
|
|
101
|
+
BundleItem,
|
|
102
|
+
CreateCartParams,
|
|
103
|
+
CreateProductReviewParams,
|
|
104
|
+
GetCartParams,
|
|
105
|
+
ListProductReviewsParams,
|
|
106
|
+
ProductDetail,
|
|
107
|
+
ProductDetailParams,
|
|
108
|
+
Product,
|
|
109
|
+
ProductAutocompleteParams,
|
|
110
|
+
ProductImage,
|
|
111
|
+
ProductPriceTier,
|
|
112
|
+
ProductSuggestion,
|
|
113
|
+
ProductSummary,
|
|
114
|
+
ProductsParams,
|
|
115
|
+
ProductsParamsInput,
|
|
116
|
+
ProductsResponse,
|
|
117
|
+
RelatedProductsParams,
|
|
118
|
+
RemoveCartItemParams,
|
|
119
|
+
SearchFilter,
|
|
120
|
+
SizeGuide,
|
|
121
|
+
SpecItem,
|
|
122
|
+
StorefrontCart,
|
|
123
|
+
StorefrontCartItem,
|
|
124
|
+
CartCheckoutReadParams,
|
|
125
|
+
CartCheckoutParams,
|
|
126
|
+
CartCheckoutResponse,
|
|
127
|
+
CartPaymentMethod,
|
|
128
|
+
GetShippingOptionsParams,
|
|
129
|
+
PutShippingMethodParams,
|
|
130
|
+
CartBuyerParams,
|
|
131
|
+
CartAddressesParams,
|
|
132
|
+
CartPromotionCodeParams,
|
|
133
|
+
ShippingRate,
|
|
134
|
+
ShippingOptionGroup,
|
|
135
|
+
ShippingOptionsResponse,
|
|
136
|
+
StorefrontOrderBump,
|
|
137
|
+
StorefrontReview,
|
|
138
|
+
StorefrontReviews,
|
|
139
|
+
UpdateCartItemQuantityParams,
|
|
140
|
+
VariantItem,
|
|
141
|
+
} from '@ecomiq/core'
|
|
142
|
+
export { createClient, getStorefrontClient } from './client'
|
|
143
|
+
export type {
|
|
144
|
+
AddProductToCartInput,
|
|
145
|
+
AddProductToCartResult,
|
|
146
|
+
ProductCartMutationResult,
|
|
147
|
+
} from './cart-session'
|
|
148
|
+
export type {
|
|
149
|
+
ProductReviewSubmissionError,
|
|
150
|
+
ProductReviewSubmissionResult,
|
|
151
|
+
} from './review-submission'
|
|
152
|
+
export { serverApi } from './server'
|
|
153
|
+
export { proxyStorefrontRequest } from './api-proxy'
|
|
154
|
+
export {
|
|
155
|
+
AppEcomiqProvider,
|
|
156
|
+
useApiQuery,
|
|
157
|
+
useAppConfig,
|
|
158
|
+
useSettings,
|
|
159
|
+
useMenu,
|
|
160
|
+
useSearchNavigate,
|
|
161
|
+
CartProvider,
|
|
162
|
+
useCart,
|
|
163
|
+
AuthProvider,
|
|
164
|
+
createStorefrontAuthClient,
|
|
165
|
+
useAuth,
|
|
166
|
+
type CartContextValue,
|
|
167
|
+
type CartLoadStatus,
|
|
168
|
+
type CartMutation,
|
|
169
|
+
type StorefrontAuthClientInstance,
|
|
170
|
+
type StorefrontSession,
|
|
171
|
+
} from './providers'
|
|
172
|
+
export { createStorefrontAuth } from './auth/create-storefront-auth'
|
|
173
|
+
export { ecomiqOAuthPassword } from './auth/ecomiq-oauth-password-plugin'
|
|
174
|
+
export { ecomiqOAuthPasswordClient } from './auth/ecomiq-oauth-password-client'
|
|
175
|
+
export { signInWithEcomiqCredentials } from './auth/ecomiq-storefront-sign-in'
|
|
176
|
+
export type { EcomiqSignInCredentials } from './auth/ecomiq-storefront-sign-in'
|
|
177
|
+
export * from './utils'
|
|
178
|
+
export { Link } from './link'
|
|
179
|
+
export { Await } from '@tanstack/react-router'
|
|
180
|
+
export { Hero, findHeroSection, type HeroProps, type HeroSlide } from './components/hero'
|
|
181
|
+
export { z } from 'zod'
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Contexto del loader. Por defecto el loader es isomorfo (SSR + cliente).
|
|
185
|
+
* Preferí `serverApi` para que el fetch a storefront solo corra en el servidor.
|
|
186
|
+
*/
|
|
187
|
+
export interface LoaderContext<
|
|
188
|
+
TParams = Record<string, string>,
|
|
189
|
+
TSearch extends Record<string, unknown> = Record<string, unknown>,
|
|
190
|
+
> {
|
|
191
|
+
params: TParams
|
|
192
|
+
search: TSearch
|
|
193
|
+
config: PublicEcomiqConfig
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Props que recibe el componente de una página. */
|
|
197
|
+
export interface PageProps<
|
|
198
|
+
TData = unknown,
|
|
199
|
+
TParams = Record<string, string>,
|
|
200
|
+
TSearch extends Record<string, unknown> = Record<string, unknown>,
|
|
201
|
+
> {
|
|
202
|
+
data: TData
|
|
203
|
+
params: TParams
|
|
204
|
+
search: TSearch
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface PageMeta {
|
|
208
|
+
title?: string
|
|
209
|
+
description?: string
|
|
210
|
+
/** Imagen OG específica de esta página (sobreescribe seo.ogImage). */
|
|
211
|
+
ogImage?: string
|
|
212
|
+
/** Marca la página como noindex,nofollow. */
|
|
213
|
+
noIndex?: boolean
|
|
214
|
+
/** Metas extra solo para esta página. */
|
|
215
|
+
meta?: Array<{ name?: string; property?: string; content: string }>
|
|
216
|
+
/** Datos estructurados (schema.org); se serializa como JSON-LD. */
|
|
217
|
+
jsonLd?: Record<string, unknown>
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Props que recibe un layout personalizado (layout.tsx en la raíz del proyecto). */
|
|
221
|
+
export interface LayoutProps {
|
|
222
|
+
children: ReactNode
|
|
223
|
+
config: PublicEcomiqConfig
|
|
224
|
+
/** Settings storefront (CMS, store, menus). Cargado en el root para todas las páginas. */
|
|
225
|
+
settings: StorefrontSettings
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Define el layout global de la tienda (opcional).
|
|
230
|
+
* Crea `layout.tsx` en la raíz del proyecto y el framework lo usa en lugar
|
|
231
|
+
* del layout por defecto. Recibe `children` (la página) y `config`.
|
|
232
|
+
*/
|
|
233
|
+
export function defineLayout(layout: (props: LayoutProps) => ReactNode) {
|
|
234
|
+
return layout
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface EcomiqPage<
|
|
238
|
+
TData = unknown,
|
|
239
|
+
TParams = Record<string, string>,
|
|
240
|
+
TSearch extends Record<string, unknown> = Record<string, unknown>,
|
|
241
|
+
> {
|
|
242
|
+
preload?: boolean
|
|
243
|
+
reloadOnSearch?: boolean
|
|
244
|
+
validateSearch?: (raw: Record<string, unknown>) => TSearch
|
|
245
|
+
/**
|
|
246
|
+
* Carga de datos. Corre en SSR y en navegaciones cliente.
|
|
247
|
+
* Usa `serverApi` (no `createClient`) para que el HTTP a storefront
|
|
248
|
+
* solo ocurra en el servidor.
|
|
249
|
+
*/
|
|
250
|
+
loader?: (ctx: LoaderContext<TParams, TSearch>) => Promise<TData> | TData
|
|
251
|
+
/** Metadatos del documento; recibe los datos ya cargados. */
|
|
252
|
+
meta?: (ctx: {
|
|
253
|
+
data: TData
|
|
254
|
+
params: TParams
|
|
255
|
+
search: TSearch
|
|
256
|
+
config: PublicEcomiqConfig
|
|
257
|
+
}) => PageMeta
|
|
258
|
+
/** UI de la página. */
|
|
259
|
+
component: (props: PageProps<TData, TParams, TSearch>) => ReactNode
|
|
260
|
+
/** UI mientras el loader está en curso (navegación cliente). */
|
|
261
|
+
loading?: () => ReactNode
|
|
262
|
+
/** UI cuando el loader falla. */
|
|
263
|
+
error?: (props: { error: Error; reset: () => void }) => ReactNode
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function definePage<
|
|
267
|
+
TData = unknown,
|
|
268
|
+
TParams = Record<string, string>,
|
|
269
|
+
TSearch extends Record<string, unknown> = Record<string, unknown>,
|
|
270
|
+
>(
|
|
271
|
+
page: EcomiqPage<TData, TParams, TSearch>,
|
|
272
|
+
): EcomiqPage<TData, TParams, TSearch> {
|
|
273
|
+
return page
|
|
274
|
+
}
|
package/src/link.tsx
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
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: '/account/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 cartId={checkoutId} />
|
|
60
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Página de agradecimiento cerrada (`/thank-you`).
|
|
3
|
+
* Tras un checkout exitoso (sin redirect del provider).
|
|
4
|
+
*/
|
|
5
|
+
import { CheckCircle2 } from 'lucide-react'
|
|
6
|
+
import { z } from 'zod'
|
|
7
|
+
import { definePage, Link } from '../index'
|
|
8
|
+
import { buttonVariants, cn } from '../ui'
|
|
9
|
+
|
|
10
|
+
const ThankYouSearchSchema = z.object({
|
|
11
|
+
cartId: z.string().optional(),
|
|
12
|
+
transactionId: z.string().optional(),
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
type ThankYouSearch = z.infer<typeof ThankYouSearchSchema>
|
|
16
|
+
|
|
17
|
+
export default definePage<undefined, Record<string, never>, ThankYouSearch>({
|
|
18
|
+
meta: () => ({
|
|
19
|
+
title: 'Gracias por tu compra',
|
|
20
|
+
description: 'Tu pedido fue recibido correctamente',
|
|
21
|
+
noIndex: true,
|
|
22
|
+
}),
|
|
23
|
+
validateSearch: (raw) => ThankYouSearchSchema.parse(raw),
|
|
24
|
+
component: ThankYouPage,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
function ThankYouPage({ search }: { search: ThankYouSearch }) {
|
|
28
|
+
return (
|
|
29
|
+
<section className="mx-auto flex min-h-[55vh] w-full max-w-lg flex-col items-center justify-center px-4 py-16 text-center">
|
|
30
|
+
<div className="mb-5 flex size-14 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-600">
|
|
31
|
+
<CheckCircle2 className="size-7" aria-hidden="true" />
|
|
32
|
+
</div>
|
|
33
|
+
<h1 className="text-3xl font-semibold tracking-tight">¡Gracias por tu compra!</h1>
|
|
34
|
+
<p className="mt-3 text-sm leading-6 text-muted-foreground">
|
|
35
|
+
Recibimos tu pedido y lo estamos procesando. Te enviaremos una confirmación por
|
|
36
|
+
correo cuando esté listo.
|
|
37
|
+
</p>
|
|
38
|
+
{search.transactionId ? (
|
|
39
|
+
<p className="mt-4 rounded-md bg-muted px-3 py-2 font-mono text-xs text-muted-foreground">
|
|
40
|
+
Ref. {search.transactionId}
|
|
41
|
+
</p>
|
|
42
|
+
) : null}
|
|
43
|
+
<div className="mt-8 flex flex-wrap justify-center gap-3">
|
|
44
|
+
<Link to="/" className={cn(buttonVariants({ size: 'lg' }))}>
|
|
45
|
+
Volver a la tienda
|
|
46
|
+
</Link>
|
|
47
|
+
<Link to="/products" className={cn(buttonVariants({ size: 'lg', variant: 'outline' }))}>
|
|
48
|
+
Seguir comprando
|
|
49
|
+
</Link>
|
|
50
|
+
</div>
|
|
51
|
+
</section>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth storefront: Better Auth client + sesión via context.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* const client = createStorefrontAuthClient()
|
|
6
|
+
* <AuthProvider client={client}>
|
|
7
|
+
* <App />
|
|
8
|
+
* </AuthProvider>
|
|
9
|
+
*
|
|
10
|
+
* const { session, signIn, signOut } = useAuth()
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
import { createContext, useContext, type ReactNode } from 'react'
|
|
14
|
+
import { createAuthClient } from 'better-auth/react'
|
|
15
|
+
import { ecomiqOAuthPasswordClient } from '../auth/ecomiq-oauth-password-client'
|
|
16
|
+
import {
|
|
17
|
+
signInWithEcomiqCredentials,
|
|
18
|
+
type EcomiqSignInCredentials,
|
|
19
|
+
} from '../auth/ecomiq-storefront-sign-in'
|
|
20
|
+
import { useSettings } from './ecomiq'
|
|
21
|
+
|
|
22
|
+
export type CreateStorefrontAuthClientOptions = {
|
|
23
|
+
baseURL?: string
|
|
24
|
+
basePath?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createStorefrontAuthClient(
|
|
28
|
+
options: CreateStorefrontAuthClientOptions = {},
|
|
29
|
+
) {
|
|
30
|
+
return createAuthClient({
|
|
31
|
+
...options,
|
|
32
|
+
plugins: [ecomiqOAuthPasswordClient()],
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type StorefrontAuthClientInstance = ReturnType<typeof createStorefrontAuthClient>
|
|
37
|
+
/** Sesión tipada de Better Auth (`{ user, session }`). */
|
|
38
|
+
export type StorefrontSession = StorefrontAuthClientInstance['$Infer']['Session']
|
|
39
|
+
|
|
40
|
+
const AuthContext = createContext<StorefrontAuthClientInstance | null>(null)
|
|
41
|
+
|
|
42
|
+
export function AuthProvider({
|
|
43
|
+
client,
|
|
44
|
+
children,
|
|
45
|
+
}: {
|
|
46
|
+
client: StorefrontAuthClientInstance
|
|
47
|
+
children: ReactNode
|
|
48
|
+
}) {
|
|
49
|
+
return <AuthContext.Provider value={client}>{children}</AuthContext.Provider>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Acceso a sesión y acciones de auth. Requiere `<AuthProvider>` + `<AppEcomiqProvider>`. */
|
|
53
|
+
export function useAuth() {
|
|
54
|
+
const client = useContext(AuthContext)
|
|
55
|
+
if (!client) throw new Error('useAuth debe usarse dentro de AuthProvider')
|
|
56
|
+
|
|
57
|
+
const sessionQuery = client.useSession()
|
|
58
|
+
const settings = useSettings()
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
client,
|
|
62
|
+
session: sessionQuery.data,
|
|
63
|
+
isPending: sessionQuery.isPending,
|
|
64
|
+
error: sessionQuery.error,
|
|
65
|
+
signIn: (credentials: EcomiqSignInCredentials) =>
|
|
66
|
+
signInWithEcomiqCredentials(client, settings, credentials),
|
|
67
|
+
signOut: () => client.signOut(),
|
|
68
|
+
}
|
|
69
|
+
}
|