@ecomiq/storefront 2.0.9 → 2.0.11
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/bin/ecomiq.mjs +1 -1
- package/package.json +2 -2
- package/src/api-proxy.ts +2 -2
- package/src/client.ts +2 -2
- package/src/codegen.mjs +3 -8
- package/src/components/checkout/checkout-contact-section.tsx +70 -0
- package/src/components/checkout/checkout-order-summary.tsx +11 -2
- package/src/components/checkout/checkout-payment-section.tsx +4 -7
- package/src/components/checkout/checkout-session-view.tsx +311 -68
- package/src/components/checkout/checkout-shipping-address-section.tsx +119 -90
- package/src/components/checkout/checkout-shipping-section.tsx +22 -14
- package/src/components/checkout/checkout-step-card.tsx +101 -0
- package/src/components/checkout/checkout-steps.ts +96 -0
- package/src/components/checkout/form-schema.ts +152 -100
- package/src/components/checkout/format.ts +0 -5
- package/src/components/checkout/index.ts +4 -50
- package/src/components/checkout/types.ts +0 -2
- package/src/components/checkout/use-checkout-session.ts +72 -15
- package/src/config.d.ts +6 -12
- package/src/config.mjs +5 -64
- package/src/index.ts +8 -4
- package/src/public-config.ts +10 -30
- package/src/runtime/default-layout.tsx +5 -21
- package/src/runtime/head.ts +70 -47
- package/src/runtime/page.tsx +45 -13
- package/src/server-config-store.ts +1 -1
- package/templates/__root.tsx +1 -1
package/bin/ecomiq.mjs
CHANGED
|
@@ -89,7 +89,7 @@ export async function runCli(command = process.argv[2] ?? 'help') {
|
|
|
89
89
|
runtime,
|
|
90
90
|
})
|
|
91
91
|
banner(
|
|
92
|
-
`dev · ${config.
|
|
92
|
+
`dev · ${config.domain ?? 'tienda'} ${c.dim(`· runtime ${runtime}`)} ${c.dim('http://localhost:' + port)}`,
|
|
93
93
|
)
|
|
94
94
|
const server = await createServer({
|
|
95
95
|
...viteConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecomiq/storefront",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.11",
|
|
4
4
|
"description": "Micro framework SSR de Ecomiq para storefronts. Páginas, datos y deploy sin configurar nada.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"vite": "^8.1.0",
|
|
77
77
|
"wrangler": "^4.100.0",
|
|
78
78
|
"zod": "^3.25.0",
|
|
79
|
-
"@ecomiq/core": "^0.1.
|
|
79
|
+
"@ecomiq/core": "^0.1.3"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"react": "^19.0.0",
|
package/src/api-proxy.ts
CHANGED
|
@@ -125,7 +125,7 @@ export async function proxyStorefrontRequest(
|
|
|
125
125
|
for (const [key, value] of Object.entries(config.api?.headers ?? {})) {
|
|
126
126
|
const lower = key.toLowerCase()
|
|
127
127
|
if (FORBIDDEN_SERVER_HEADERS.has(lower) || lower.startsWith('cf-')) continue
|
|
128
|
-
headers.set(key, value)
|
|
128
|
+
headers.set(key, value as string)
|
|
129
129
|
}
|
|
130
130
|
headers.set('accept', headers.get('accept') ?? 'application/json')
|
|
131
131
|
|
|
@@ -145,7 +145,7 @@ export async function proxyStorefrontRequest(
|
|
|
145
145
|
const isSettings = path === 'settings' || path.startsWith('settings/')
|
|
146
146
|
if (isSettings) {
|
|
147
147
|
headers.delete('x-Store-Id')
|
|
148
|
-
headers.set('x-Store-Domain', config.
|
|
148
|
+
headers.set('x-Store-Domain', config.domain)
|
|
149
149
|
} else {
|
|
150
150
|
try {
|
|
151
151
|
const storeId = await getStorefrontClient().getStoreId()
|
package/src/client.ts
CHANGED
|
@@ -20,7 +20,7 @@ export function getStorefrontClient(options: CreateApiOptions = {}): StorefrontC
|
|
|
20
20
|
if (lower === 'x-store-id' || lower === 'x-store-domain') delete headers[key]
|
|
21
21
|
}
|
|
22
22
|
sharedClient = createCoreClient({
|
|
23
|
-
|
|
23
|
+
domain: config.domain,
|
|
24
24
|
...config.api,
|
|
25
25
|
...options,
|
|
26
26
|
headers,
|
|
@@ -36,7 +36,7 @@ export function resetStorefrontClient(): void {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* Cliente storefront.
|
|
39
|
+
* Cliente storefront. Domain y defaults de API salen del config de la tienda.
|
|
40
40
|
*
|
|
41
41
|
* ```ts
|
|
42
42
|
* export const api = createClient()
|
package/src/codegen.mjs
CHANGED
|
@@ -106,14 +106,9 @@ export async function generateApp({ cwd, appDir, frameworkRoot, config }) {
|
|
|
106
106
|
)
|
|
107
107
|
|
|
108
108
|
const publicConfig = {
|
|
109
|
-
|
|
110
|
-
slug: config.slug,
|
|
109
|
+
domain: config.domain,
|
|
111
110
|
}
|
|
112
|
-
if (config.description !== undefined) publicConfig.description = config.description
|
|
113
111
|
if (config.theme !== undefined) publicConfig.theme = config.theme
|
|
114
|
-
if (config.seo !== undefined) publicConfig.seo = config.seo
|
|
115
|
-
if (config.analytics !== undefined) publicConfig.analytics = config.analytics
|
|
116
|
-
if (config.layout !== undefined) publicConfig.layout = config.layout
|
|
117
112
|
|
|
118
113
|
for (const serverOnlyKey of ['api', 'dev', 'deploy', 'env']) {
|
|
119
114
|
if (Object.hasOwn(publicConfig, serverOnlyKey)) {
|
|
@@ -141,7 +136,7 @@ export async function generateApp({ cwd, appDir, frameworkRoot, config }) {
|
|
|
141
136
|
` if (import.meta.env.DEV && config.dev?.apiBaseUrl) {\n` +
|
|
142
137
|
` api = { ...api, baseUrl: config.dev.apiBaseUrl }\n` +
|
|
143
138
|
` }\n` +
|
|
144
|
-
` registerServerConfig({
|
|
139
|
+
` registerServerConfig({ domain: config.domain, api })\n` +
|
|
145
140
|
` return startServer.fetch(...args)\n` +
|
|
146
141
|
` },\n` +
|
|
147
142
|
`})\n`,
|
|
@@ -223,7 +218,7 @@ export async function generateApp({ cwd, appDir, frameworkRoot, config }) {
|
|
|
223
218
|
reconcileGeneratedRoutes(appDir, routesDir, desiredRoutes)
|
|
224
219
|
|
|
225
220
|
// ── 9. wrangler.jsonc para deploy en Cloudflare Workers ──
|
|
226
|
-
const workerName = config.deploy?.name ?? slugify(config.
|
|
221
|
+
const workerName = config.deploy?.name ?? slugify(config.domain ?? 'ecomiq-store')
|
|
227
222
|
writeGeneratedFile(
|
|
228
223
|
join(appDir, 'wrangler.jsonc'),
|
|
229
224
|
JSON.stringify(
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Link } from '../../link'
|
|
2
|
+
import { Button, buttonVariants, cn } from '../../ui'
|
|
3
|
+
import type { CheckoutFieldId } from './form-schema'
|
|
4
|
+
import { CheckoutTextField } from './checkout-form-controls'
|
|
5
|
+
|
|
6
|
+
/** Paso 1: Mis datos (email). */
|
|
7
|
+
export function CheckoutContactSection({
|
|
8
|
+
email,
|
|
9
|
+
fieldError,
|
|
10
|
+
sessionLabel,
|
|
11
|
+
isSaving,
|
|
12
|
+
onEmailChange,
|
|
13
|
+
onClearError,
|
|
14
|
+
onContinueAsGuest,
|
|
15
|
+
}: {
|
|
16
|
+
email: string
|
|
17
|
+
fieldError?: string
|
|
18
|
+
sessionLabel?: string | null
|
|
19
|
+
isSaving?: boolean
|
|
20
|
+
onEmailChange: (value: string) => void
|
|
21
|
+
onClearError: (id: CheckoutFieldId) => void
|
|
22
|
+
onContinueAsGuest: () => void | Promise<void>
|
|
23
|
+
}) {
|
|
24
|
+
return (
|
|
25
|
+
<div className="space-y-5">
|
|
26
|
+
{sessionLabel ? (
|
|
27
|
+
<p className="text-sm text-muted-foreground">
|
|
28
|
+
Sesión como <span className="font-medium text-foreground">{sessionLabel}</span>
|
|
29
|
+
</p>
|
|
30
|
+
) : null}
|
|
31
|
+
|
|
32
|
+
<CheckoutTextField
|
|
33
|
+
id="email"
|
|
34
|
+
label="Correo electrónico"
|
|
35
|
+
error={fieldError}
|
|
36
|
+
value={email}
|
|
37
|
+
onChange={(v) => {
|
|
38
|
+
onClearError('email')
|
|
39
|
+
onEmailChange(v)
|
|
40
|
+
}}
|
|
41
|
+
type="email"
|
|
42
|
+
autoComplete="email"
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<div className="flex flex-col gap-3 sm:flex-row">
|
|
46
|
+
{!sessionLabel ? (
|
|
47
|
+
<Link
|
|
48
|
+
to="/account/login"
|
|
49
|
+
search={{
|
|
50
|
+
redirect:
|
|
51
|
+
typeof window !== 'undefined' ? window.location.pathname : '/checkout',
|
|
52
|
+
}}
|
|
53
|
+
className={cn(buttonVariants({ variant: 'default' }), 'uppercase tracking-wide')}
|
|
54
|
+
>
|
|
55
|
+
¿Ya tienes una cuenta?
|
|
56
|
+
</Link>
|
|
57
|
+
) : null}
|
|
58
|
+
<Button
|
|
59
|
+
type="button"
|
|
60
|
+
variant={sessionLabel ? 'default' : 'outline'}
|
|
61
|
+
className="uppercase tracking-wide"
|
|
62
|
+
disabled={isSaving}
|
|
63
|
+
onClick={() => void onContinueAsGuest()}
|
|
64
|
+
>
|
|
65
|
+
{sessionLabel ? 'Continuar' : 'Continuar como invitado'}
|
|
66
|
+
</Button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -17,6 +17,8 @@ export function CheckoutOrderSummary({
|
|
|
17
17
|
promotionCodes,
|
|
18
18
|
formError,
|
|
19
19
|
isSaving,
|
|
20
|
+
canSubmit = true,
|
|
21
|
+
submitLabel = 'Pagar',
|
|
20
22
|
onCouponCodeChange,
|
|
21
23
|
onApplyCoupon,
|
|
22
24
|
onRemoveCoupon,
|
|
@@ -34,6 +36,8 @@ export function CheckoutOrderSummary({
|
|
|
34
36
|
promotionCodes: string[]
|
|
35
37
|
formError: string | null
|
|
36
38
|
isSaving: boolean
|
|
39
|
+
canSubmit?: boolean
|
|
40
|
+
submitLabel?: string
|
|
37
41
|
onCouponCodeChange: (value: string) => void
|
|
38
42
|
onApplyCoupon: () => void
|
|
39
43
|
onRemoveCoupon: (code: string) => void
|
|
@@ -113,8 +117,13 @@ export function CheckoutOrderSummary({
|
|
|
113
117
|
</p>
|
|
114
118
|
) : null}
|
|
115
119
|
|
|
116
|
-
<Button
|
|
117
|
-
|
|
120
|
+
<Button
|
|
121
|
+
type="submit"
|
|
122
|
+
className="mt-6 w-full"
|
|
123
|
+
size="lg"
|
|
124
|
+
disabled={isSaving || !canSubmit}
|
|
125
|
+
>
|
|
126
|
+
{isSaving ? 'Procesando…' : submitLabel}
|
|
118
127
|
</Button>
|
|
119
128
|
</aside>
|
|
120
129
|
)
|
|
@@ -51,11 +51,8 @@ export function CheckoutPaymentSection({
|
|
|
51
51
|
onMarkProviderCompleted: () => void
|
|
52
52
|
onReceiptFileChange: (file: File | null) => void
|
|
53
53
|
}) {
|
|
54
|
-
const selectedMethod = methods.find((m) => m.code === selectedCode) ?? null
|
|
55
|
-
|
|
56
54
|
return (
|
|
57
|
-
<
|
|
58
|
-
<h2 className="text-xl font-semibold tracking-tight">Seleccionar método de pago</h2>
|
|
55
|
+
<div className="space-y-4">
|
|
59
56
|
<div className="space-y-3">
|
|
60
57
|
{methods.map((method) => {
|
|
61
58
|
const selected = selectedCode === method.code
|
|
@@ -137,7 +134,7 @@ export function CheckoutPaymentSection({
|
|
|
137
134
|
|
|
138
135
|
{!usesProviderUi && !method.isManual ? (
|
|
139
136
|
<p className="text-xs text-muted-foreground">
|
|
140
|
-
Continúa con
|
|
137
|
+
Continúa con Pagar para confirmar este método de pago.
|
|
141
138
|
</p>
|
|
142
139
|
) : null}
|
|
143
140
|
</div>
|
|
@@ -147,7 +144,7 @@ export function CheckoutPaymentSection({
|
|
|
147
144
|
})}
|
|
148
145
|
</div>
|
|
149
146
|
<FieldError message={fieldError} />
|
|
150
|
-
</
|
|
147
|
+
</div>
|
|
151
148
|
)
|
|
152
149
|
}
|
|
153
150
|
|
|
@@ -203,7 +200,7 @@ function ProviderPaymentPanel({
|
|
|
203
200
|
) : null}
|
|
204
201
|
{completed ? (
|
|
205
202
|
<p className="text-xs text-muted-foreground">
|
|
206
|
-
Pago confirmado. Pulsa
|
|
203
|
+
Pago confirmado. Pulsa Pagar para finalizar tu pedido.
|
|
207
204
|
</p>
|
|
208
205
|
) : (
|
|
209
206
|
<Button type="button" size="sm" variant="outline" onClick={onMarkCompleted}>
|