@asteby/storefront-react 0.16.1 → 0.18.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 +58 -1
- package/dist/checkout.cjs +108 -20
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.d.cts +15 -1
- package/dist/checkout.d.ts +15 -1
- package/dist/checkout.js +107 -21
- package/dist/checkout.js.map +1 -1
- package/dist/{chunk-ISEOCKIR.js → chunk-MNLROALF.js} +96 -8
- package/dist/chunk-MNLROALF.js.map +1 -0
- package/dist/{choices-DEPE9KRq.d.cts → csp-DZPuExzu.d.cts} +93 -55
- package/dist/{choices-DEPE9KRq.d.ts → csp-DZPuExzu.d.ts} +93 -55
- package/dist/payments-server.cjs +159 -12
- package/dist/payments-server.cjs.map +1 -1
- package/dist/payments-server.d.cts +23 -3
- package/dist/payments-server.d.ts +23 -3
- package/dist/payments-server.js +70 -13
- package/dist/payments-server.js.map +1 -1
- package/dist/payments.cjs +784 -160
- package/dist/payments.cjs.map +1 -1
- package/dist/payments.css +84 -0
- package/dist/payments.d.cts +227 -8
- package/dist/payments.d.ts +227 -8
- package/dist/payments.js +628 -102
- package/dist/payments.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-ISEOCKIR.js.map +0 -1
package/README.md
CHANGED
|
@@ -525,6 +525,63 @@ export const pay = createEmbeddedPayHandlers({
|
|
|
525
525
|
- `cashPointsVisible={4}` deja a la vista los primeros puntos de efectivo y manda el resto a «Ver más puntos de pago» (se abre solo si el punto recordado está ahí).
|
|
526
526
|
- Botón: «Generar ficha de pago $X» con efectivo (`placeOrderCash`) y «Elige la entrega para continuar» mientras falta la entrega (`chooseDelivery`; al pulsarlo señala el campo que falta en vez de pagar).
|
|
527
527
|
|
|
528
|
+
### Cobro resiliente: reintento, verificación, formulario estable y aviso con salida
|
|
529
|
+
|
|
530
|
+
Un cobro con tarjeta puede quedar **indeterminado** (la respuesta se perdió, el proxy devolvió HTML, OPS tardó). La regla que nunca cambia: **un resultado indeterminado jamás se cobra con otra clave de idempotencia**; se repite con la MISMA clave o se consulta el estado. Lo de abajo se adopta de forma opcional, pero **no es solo aditivo** (ver «Cambios que se notan» al final de esta sección), y está pensado para que ninguna tienda tenga que copiar código de otra.
|
|
531
|
+
|
|
532
|
+
**1. Servidor (`/payments/server`).** `createEmbeddedPayHandlers` ya responde SIEMPRE JSON `{ error: { code, message, retryable, kind } }` (clasificado con `classifyPaymentError` de `@asteby/storefront`) y devuelve `500` en lugar de `502-504` (Traefik/CDN los cambian por HTML y se pierde el `Set-Cookie` de la sesión de pago). `/estado` sin cookie `sf_pay` responde `200 { payment: { status: 'none' }, recoverable }` en vez de `404`: `none` no es un resultado, así que un cobro indeterminado sigue con la misma clave; `recoverable` dice si la tienda aún tiene el carrito del comprador. Para rutas propias: `jsonPayRoute(run, 'pay', { locale, messages })`. Los textos de los errores salen del catálogo `error.<kind>` y se reemplazan con `errorMessages` (o `messages`/`locale` de `jsonPayRoute`).
|
|
533
|
+
|
|
534
|
+
**2. Navegador.**
|
|
535
|
+
|
|
536
|
+
```tsx
|
|
537
|
+
import { PaymentMethodSection, PaymentRecovery, alternativeChoices, createResilientTransport, pickChoice, usePaymentRecovery } from '@asteby/storefront-react/payments'
|
|
538
|
+
|
|
539
|
+
const recovery = usePaymentRecovery({ scope: cart.id, onRevalidate: revalidate })
|
|
540
|
+
const transport = useMemo(
|
|
541
|
+
() => createResilientTransport({ payUrl: '/pagar/cobrar', statusUrl: '/pagar/estado', fields: () => formFields(formRef.current), onIssue: recovery.onIssue }),
|
|
542
|
+
[],
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
<PaymentRecovery
|
|
546
|
+
notice={recovery.notice} alternatives={alternativeChoices(choices.choices)}
|
|
547
|
+
onRetry={recovery.retry} onRefresh={() => location.assign('/pagar')}
|
|
548
|
+
onPick={(c) => { recovery.clear(); pickChoice(formRef.current, c.value) }} />
|
|
549
|
+
<PaymentMethodSection key={recovery.sectionKey} transport={transport} brickProps={{ autoRetries: 0 }} onEvent={recovery.onEvent} ... />
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
- `createResilientTransport({ payUrl, statusUrl, fields, onIssue, retries = 2, backoffMs = [800, 2000], statusPolls = 3, statusPollMs = 2000, headers, locale, messages, fetch, sleep })`: reintenta los fallos transitorios con la misma clave, consulta `/estado` y, si sigue sin resolverse, lanza `PaymentHttpError(503, 'network', …, { retryable: true, kind: 'transient' })` (el hook conserva el intento y deja «Reintentar»). Con `retryable: false` de OPS hace UNA petición. Un cuerpo que no es JSON se trata como transitorio. `PaymentHttpError` expone `retryable` y `kind`. Con `autoRetries: 0` en `brickProps` el hook no duplica la política del transporte.
|
|
553
|
+
- `usePaymentRecovery({ scope, storage, formAutoRetries = 2, onRevalidate })` devuelve `notice`, `sectionKey` (úsalo como `key` de la sección), `onIssue`, `onEvent`, `retry()` y `clear()`. `retry()` quita la clave del intento y monta una sección nueva SOLO tras un fallo definitivo; mientras el resultado es desconocido (confirmando, verificando, transitorio) no hace nada y devuelve `false`. Un formulario de tarjeta que no carga se vuelve a montar solo `formAutoRetries` veces antes de mostrar el aviso.
|
|
554
|
+
- `<PaymentRecovery notice alternatives onRetry onPick onRefresh locale messages alternativeLabel classNames testIds>`: nunca deja un panel vacío; un fallo del comercio (credenciales, configuración) no culpa al comprador y pone primero las alternativas, que salen de las `choices` publicadas (nada de marcas en los textos). Textos en español de México por omisión (`en*` disponible), todos reemplazables con `messages` (`notice.<tipo>.title|body`, `action.retry|retry_form|refresh|alternative` con `{label}`, `error.<tipo>`; ver `paymentRecoveryMessages`), clases con `classNames` (`root title body actions primary secondary`).
|
|
555
|
+
- **Consulta de estado tras «verificando»**: `useEmbeddedPayment` sigue leyendo `transport.refresh` con espera creciente (2 s, 4 s, 8 s… hasta 15 s) durante `verifyTimeoutMs` (120 s). Solo un resultado resuelto (pagado, rechazado, ficha, reto, redirección) lo termina; `pending` y `none` no. Expone `verifying` y la clave del intento no cambia. Opciones: `verifyTimeoutMs`, `verifyIntervalMs`.
|
|
556
|
+
- `checkoutShouldRevalidate({ paths: ['/pagar'] })`: `shouldRevalidate` para las rutas de ALREDEDOR (root, encabezado…): el `fetcher.submit` de la entrega a `/pagar` ya no vuelve a ejecutar todos los loaders. No lo uses en la ruta de checkout (su loader debe releer el total).
|
|
557
|
+
|
|
558
|
+
**3. Formulario de tarjeta estable.** El contenedor se monta UNA vez por vida del formulario:
|
|
559
|
+
|
|
560
|
+
- Un cambio de total NO remonta: el hook llama `MountedPaymentForm.update({ amount })` (en Mercado Pago, `controller.update` del Card Payment Brick). Si el proveedor no lo tiene o falla, se remonta UNA vez, en cola. Varios cambios seguidos se fusionan en el último.
|
|
561
|
+
- Nunca se actualiza ni remonta mientras `onSubmit` va en vuelo: se aplica después de la respuesta, y un `submit` espera a que termine la actualización en curso.
|
|
562
|
+
- Escribir el correo ya no remonta (se usa al crear el formulario).
|
|
563
|
+
- `PaymentMethodSection`: mientras `syncDelivery` no termina, el formulario se queda montado con la capa «Actualizando el total…» (`data-sf-pay-updating`, `payments.css`) y el botón bloqueado (`aria-disabled`).
|
|
564
|
+
- Cola de montajes (`mountQueued` / `withMountQueue`, la usa el hook con cualquier adaptador): uno a la vez por contenedor, `await unmount` antes del siguiente `create`, nodo interno con id único, cancelación (`signal` en `PaymentFormMountOptions`) del montaje viejo y de uno cuyo contenedor salió del DOM, y tiempo límite de `create` (`createTimeoutMs`, por omisión `sdkTimeoutMs`) para que la cadena no se cuelgue (cae a la alternativa de redirección).
|
|
565
|
+
- Adaptadores propios: implementa `update?` si el proveedor sabe cambiar el monto; si no, el hook remonta.
|
|
566
|
+
|
|
567
|
+
**4. `AddressFields` (checkout).** Un solo escritor en cola; no escribe en un campo con foco ni en uno que el comprador editó (evento `input` con `isTrusted`); la colonia solo se rellena si el código postal tiene UNA sola; los eventos `input` que dispara el autollenado llevan `event.sfSynthetic === true` (`isAddressFillEvent(event)`) para que la tienda los ignore.
|
|
568
|
+
|
|
569
|
+
**Migrar una tienda que copió esto** (p. ej. `store-protekauto`): borra su `pay-errors`, `pay-transport`, `pay-route.server`, `brick-adapter` y `pay-recovery`; usa `createResilientTransport`, `usePaymentRecovery`, `<PaymentRecovery>` y los handlers del servidor; quita el `key` de `PaymentBrick` si lo tenía y el adaptador envuelto. Los textos propios van en `messages`.
|
|
570
|
+
|
|
571
|
+
### Cambios que se notan
|
|
572
|
+
|
|
573
|
+
Al subir el pin de `@asteby/storefront` / `@asteby/storefront-react` (en 0.x un caret no adopta un minor solo):
|
|
574
|
+
|
|
575
|
+
- Los handlers de pago responden **500** donde antes respondían 502-504 (y `payErrorResponse({})` pasa de 503 a 500). La señal de «indeterminado» viaja en el cuerpo (`retryable`, `kind`); un 5xx sin cuerpo legible se trata como indeterminado (misma clave), nunca como rechazo.
|
|
576
|
+
- Los mensajes salen del catálogo es-MX/en y no del texto de OPS para los tipos transitorio, en curso, límite de intentos, configuración, rechazo del proveedor, pedido ya registrado y tienda cerrada. Los textos propios de la tienda (`messages.tooManyRequests`, `fillFields`, etc.) se conservan.
|
|
577
|
+
- `/estado` sin sesión o con un checkout inexistente responde **200** `{ status: 'none', recoverable }` en lugar de 404. `'none'` se suma a `PaymentAttemptStatus`.
|
|
578
|
+
- Escribir el correo ya no remonta el formulario de tarjeta: un invitado que lo escribe después del montaje ve el campo de correo dentro del Brick.
|
|
579
|
+
- Un cambio de total actualiza el formulario en su sitio (no lo remonta) y el formulario queda dentro de un `div` interno: un contenedor propio de `useEmbeddedPayment` no debe tener hijos renderizados por React (`replaceChildren`).
|
|
580
|
+
- Con resultado indeterminado, PAGAR reintenta con los mismos datos y la misma clave (no vuelve a tokenizar), y un cambio de total espera a que se resuelva.
|
|
581
|
+
- `createTimeoutMs` toma por omisión `sdkTimeoutMs`.
|
|
582
|
+
- `AddressFields` rellena la colonia cuando el código postal tiene una sola, rellena un campo enfocado solo si está vacío y no lo editó el comprador, y marca sus eventos (`isAddressFillEvent`).
|
|
583
|
+
- Tipos: `PayErrorBody` exige `retryable` y `kind`; `paymentErrorView` trata un 5xx (salvo `retryable:false`) como `network`.
|
|
584
|
+
|
|
528
585
|
## Entrega y marcas (`@asteby/storefront-react/checkout`)
|
|
529
586
|
|
|
530
587
|
Las tarifas de envío y las sucursales de «Entrega» como radios nativos, iguales en todas las tiendas: marca y nombre comercial de la paquetería (`fedex` → FedEx), servicio sin repetir la paquetería, llegada en días hábiles («Llega en 4 días hábiles»), insignias discretas de «Más económico» / «Más rápido» (solo si una tarifa lo es estrictamente) y el precio tal cual lo manda OPS. La vigencia de la cotización es UNA sola nota para toda la lista, en la zona horaria del comprador («Precios de envío vigentes hasta las 9:39 p.m.»), que aparece tras hidratar en una línea reservada (sin salto de diseño). `loading` pinta filas esqueleto al recotizar.
|
|
@@ -690,7 +747,7 @@ La cookie, el formulario y el redirect son de la tienda; `safeRedirect` valida e
|
|
|
690
747
|
| `defineSection`, `SectionRegistry`, `createSectionRegistry`, `Sections`, `resolveSectionProps`, `sectionJsonSchema` | Content sections |
|
|
691
748
|
| `themeToCss`, `themeToCssVariables`, `ThemeStyle`, `themeFontsHref`, `tokenName` | SSR theme |
|
|
692
749
|
| `ProductImage`, `ProductMockup`, `MockupProvider`, `createMockupRegistry`, `registerMockup`, `BUILTIN_MOCKUPS`, `MockupFrame`, `MockupText`, `TireMockup`, `PartMockup`, `SupplementMockup`, `GenericMockup` | Mockups de respaldo (subpath `/mockups`) |
|
|
693
|
-
| `PaymentMethodSection`, `buildPayChoices`, `createEmbeddedPayHandlers` (`/payments/server`), `PaymentBrick`, `useEmbeddedPayment`, `VoucherCard`, `PaymentChallenge`, `createFetchTransport`, `mercadoPagoBricksAdapter`, `loadPaymentSdk`, `paymentCsp`, `readPaymentTheme` | Pago integrado (subpath `/payments`) |
|
|
750
|
+
| `PaymentMethodSection`, `buildPayChoices`, `createEmbeddedPayHandlers` + `jsonPayRoute` (`/payments/server`), `createResilientTransport`, `usePaymentRecovery`, `PaymentRecovery`, `mountQueued`, `checkoutShouldRevalidate`, `PaymentBrick`, `useEmbeddedPayment`, `VoucherCard`, `PaymentChallenge`, `createFetchTransport`, `mercadoPagoBricksAdapter`, `loadPaymentSdk`, `paymentCsp`, `readPaymentTheme` | Pago integrado (subpath `/payments`) |
|
|
694
751
|
| `PromoPopup`, `CartProgressBars`, `PromoProgressBar`, `CrossSellModal`, `OrderNote`, `isExcludedPath`, `frequencyAllows`, `isCrawler` | Promociones y venta cruzada (subpath `/promotions`) |
|
|
695
752
|
|
|
696
753
|
## `@asteby/storefront-react/ui`
|
package/dist/checkout.cjs
CHANGED
|
@@ -236,6 +236,7 @@ var ES2 = {
|
|
|
236
236
|
address2: "N\xFAmero interior o departamento",
|
|
237
237
|
reference: "Referencias para la entrega",
|
|
238
238
|
provincePlaceholder: "Elige tu estado",
|
|
239
|
+
neighborhoodPlaceholder: "Elige tu colonia",
|
|
239
240
|
address1Hint: "Por ejemplo: Av. Insurgentes Sur 1234",
|
|
240
241
|
referenceHint: "Entre qu\xE9 calles, color de la fachada\u2026",
|
|
241
242
|
emailHint: "Te enviamos aqu\xED la confirmaci\xF3n y el seguimiento.",
|
|
@@ -255,6 +256,7 @@ var EN2 = {
|
|
|
255
256
|
address2: "Apartment, suite",
|
|
256
257
|
reference: "Delivery notes",
|
|
257
258
|
provincePlaceholder: "Choose your state",
|
|
259
|
+
neighborhoodPlaceholder: "Choose your neighborhood",
|
|
258
260
|
address1Hint: "For example: 1234 Main St",
|
|
259
261
|
referenceHint: "Cross streets, building color\u2026",
|
|
260
262
|
emailHint: "We send the confirmation and tracking here.",
|
|
@@ -297,6 +299,10 @@ function lockField(el) {
|
|
|
297
299
|
el.readOnly = true;
|
|
298
300
|
}
|
|
299
301
|
}
|
|
302
|
+
var ADDRESS_FILL_EVENT_FLAG = "sfSynthetic";
|
|
303
|
+
function isAddressFillEvent(event) {
|
|
304
|
+
return event[ADDRESS_FILL_EVENT_FLAG] === true;
|
|
305
|
+
}
|
|
300
306
|
function AddressFields(props) {
|
|
301
307
|
const { defaults, names = {}, errors = {}, countryCode, provinces, postalCode, lookupPostalCode, lockProvinceOnFill, locale, messages, fieldMessages: fm, classNames: cx = {}, children } = props;
|
|
302
308
|
const showNeighborhood = props.neighborhood ?? true;
|
|
@@ -307,10 +313,36 @@ function AddressFields(props) {
|
|
|
307
313
|
const listId = react.useId();
|
|
308
314
|
const [colonias, setColonias] = react.useState([]);
|
|
309
315
|
const [filled, setFilled] = react.useState(false);
|
|
316
|
+
const [coloniaSelect, setColoniaSelect] = react.useState(false);
|
|
317
|
+
const coloniaSelectRef = react.useRef(false);
|
|
318
|
+
const coloniaCarry = react.useRef(null);
|
|
310
319
|
const lastZip = react.useRef(null);
|
|
311
320
|
const zipRef = react.useRef(null);
|
|
312
321
|
const lookupRef = react.useRef(lookupPostalCode);
|
|
313
322
|
lookupRef.current = lookupPostalCode;
|
|
323
|
+
const writer = react.useRef(Promise.resolve());
|
|
324
|
+
const enqueue = (task) => {
|
|
325
|
+
writer.current = writer.current.then(task).catch(() => {
|
|
326
|
+
});
|
|
327
|
+
};
|
|
328
|
+
react.useEffect(() => {
|
|
329
|
+
const form = zipRef.current?.closest("form");
|
|
330
|
+
if (!form) return;
|
|
331
|
+
const mark = (e) => {
|
|
332
|
+
if (!e.isTrusted) return;
|
|
333
|
+
const el = e.target;
|
|
334
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLSelectElement) {
|
|
335
|
+
el.dataset.sfUserEdited = "1";
|
|
336
|
+
delete el.dataset.sfAutofilled;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
form.addEventListener("input", mark, true);
|
|
340
|
+
form.addEventListener("change", mark, true);
|
|
341
|
+
return () => {
|
|
342
|
+
form.removeEventListener("input", mark, true);
|
|
343
|
+
form.removeEventListener("change", mark, true);
|
|
344
|
+
};
|
|
345
|
+
}, []);
|
|
314
346
|
const runLookup = (zip) => {
|
|
315
347
|
const lookup = lookupRef.current;
|
|
316
348
|
if (!lookup || !zip || zip === lastZip.current) return;
|
|
@@ -319,28 +351,65 @@ function AddressFields(props) {
|
|
|
319
351
|
const form = zipRef.current?.closest("form");
|
|
320
352
|
void lookup(zip).then(
|
|
321
353
|
(info) => {
|
|
322
|
-
if (!info
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
354
|
+
if (!info) return;
|
|
355
|
+
enqueue(() => {
|
|
356
|
+
if (lastZip.current !== zip) return;
|
|
357
|
+
const fill2 = (name, value, lock) => {
|
|
358
|
+
const el2 = form?.elements.namedItem(name);
|
|
359
|
+
if (!el2 || !value || "length" in el2 && !(el2 instanceof HTMLSelectElement)) return false;
|
|
360
|
+
if (el2.dataset.sfLocked === "1") return false;
|
|
361
|
+
if (el2.dataset.sfUserEdited === "1" || el2.ownerDocument.activeElement === el2 && el2.value) return false;
|
|
362
|
+
if (el2.value && el2.dataset.sfAutofilled !== "1") return false;
|
|
363
|
+
if (el2 instanceof HTMLSelectElement && ![...el2.options].some((o) => o.value === value)) return false;
|
|
364
|
+
el2.value = value;
|
|
365
|
+
el2.dataset.sfAutofilled = "1";
|
|
366
|
+
const event = new Event("input", { bubbles: true });
|
|
367
|
+
Object.defineProperty(event, ADDRESS_FILL_EVENT_FLAG, { value: true });
|
|
368
|
+
el2.dispatchEvent(event);
|
|
369
|
+
if (lock) lockField(el2);
|
|
370
|
+
return true;
|
|
371
|
+
};
|
|
372
|
+
const a = fill2(n("province"), info.province, lockProvinceOnFill);
|
|
373
|
+
const b = fill2(n("city"), info.city);
|
|
374
|
+
const only = info.neighborhoods?.length === 1 ? info.neighborhoods[0] : null;
|
|
375
|
+
const c = showNeighborhood ? fill2(n("neighborhood"), only) : false;
|
|
376
|
+
setColonias(info.neighborhoods ?? []);
|
|
377
|
+
const list = info.neighborhoods ?? [];
|
|
378
|
+
const nameEl = form?.elements.namedItem(n("neighborhood"));
|
|
379
|
+
const el = nameEl && !("length" in nameEl && !(nameEl instanceof HTMLSelectElement)) ? nameEl : null;
|
|
380
|
+
const typed = el ? el.value : "";
|
|
381
|
+
const wantSelect = props.neighborhoodList === "select" && showNeighborhood !== false && list.length > 1 && (!typed || list.includes(typed));
|
|
382
|
+
let carried = false;
|
|
383
|
+
if (showNeighborhood && wantSelect !== coloniaSelectRef.current) {
|
|
384
|
+
const userEdited = el?.dataset.sfUserEdited === "1";
|
|
385
|
+
const value = only && !userEdited ? only : wantSelect ? list.includes(typed) ? typed : "" : typed;
|
|
386
|
+
coloniaCarry.current = { value, autofilled: !userEdited && (!!only || el?.dataset.sfAutofilled === "1"), userEdited };
|
|
387
|
+
carried = !!only && !userEdited;
|
|
388
|
+
}
|
|
389
|
+
coloniaSelectRef.current = wantSelect;
|
|
390
|
+
setColoniaSelect(wantSelect);
|
|
391
|
+
setFilled(a || b || c || carried);
|
|
392
|
+
});
|
|
339
393
|
},
|
|
340
394
|
() => {
|
|
341
395
|
}
|
|
342
396
|
);
|
|
343
397
|
};
|
|
398
|
+
react.useEffect(() => {
|
|
399
|
+
const carry = coloniaCarry.current;
|
|
400
|
+
coloniaCarry.current = null;
|
|
401
|
+
if (!carry || !carry.value) return;
|
|
402
|
+
const form = zipRef.current?.closest("form");
|
|
403
|
+
const el = form?.elements.namedItem(n("neighborhood"));
|
|
404
|
+
if (!el || "length" in el && !(el instanceof HTMLSelectElement)) return;
|
|
405
|
+
if (el instanceof HTMLSelectElement && ![...el.options].some((o) => o.value === carry.value)) return;
|
|
406
|
+
el.value = carry.value;
|
|
407
|
+
if (carry.userEdited) el.dataset.sfUserEdited = "1";
|
|
408
|
+
else if (carry.autofilled) el.dataset.sfAutofilled = "1";
|
|
409
|
+
const event = new Event("input", { bubbles: true });
|
|
410
|
+
Object.defineProperty(event, ADDRESS_FILL_EVENT_FLAG, { value: true });
|
|
411
|
+
el.dispatchEvent(event);
|
|
412
|
+
}, [coloniaSelect]);
|
|
344
413
|
react.useEffect(() => {
|
|
345
414
|
const zip = defaults?.zip;
|
|
346
415
|
if (zip) runLookup(zip);
|
|
@@ -374,7 +443,24 @@ function AddressFields(props) {
|
|
|
374
443
|
provinces.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.name, children: p.name }, p.code ?? p.name))
|
|
375
444
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(CheckoutField, { ...common, name: n("province"), label: m.province, autoComplete: "address-level1", defaultValue: defaults?.province ?? "", error: errors[n("province")] ?? null, classNames: field(cx, "half") }),
|
|
376
445
|
/* @__PURE__ */ jsxRuntime.jsx(CheckoutField, { ...common, name: n("city"), label: m.city, autoComplete: "address-level2", defaultValue: defaults?.city ?? "", error: errors[n("city")] ?? null, classNames: field(cx, "half") }),
|
|
377
|
-
showNeighborhood ? /* @__PURE__ */ jsxRuntime.
|
|
446
|
+
showNeighborhood && coloniaSelect ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
447
|
+
CheckoutField,
|
|
448
|
+
{
|
|
449
|
+
...common,
|
|
450
|
+
as: "select",
|
|
451
|
+
name: n("neighborhood"),
|
|
452
|
+
label: m.neighborhood,
|
|
453
|
+
autoComplete: "address-level3",
|
|
454
|
+
optional: showNeighborhood !== "required",
|
|
455
|
+
defaultValue: colonias.includes(defaults?.neighborhood ?? "") ? defaults?.neighborhood ?? "" : "",
|
|
456
|
+
error: errors[n("neighborhood")] ?? null,
|
|
457
|
+
classNames: field(cx, "half"),
|
|
458
|
+
children: [
|
|
459
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: showNeighborhood === "required", children: m.neighborhoodPlaceholder }),
|
|
460
|
+
colonias.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c, children: c }, c))
|
|
461
|
+
]
|
|
462
|
+
}
|
|
463
|
+
) : showNeighborhood ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
378
464
|
CheckoutField,
|
|
379
465
|
{
|
|
380
466
|
...common,
|
|
@@ -385,10 +471,10 @@ function AddressFields(props) {
|
|
|
385
471
|
defaultValue: defaults?.neighborhood ?? "",
|
|
386
472
|
error: errors[n("neighborhood")] ?? null,
|
|
387
473
|
classNames: field(cx, "half"),
|
|
388
|
-
...colonias.length ? { list: listId } : {}
|
|
474
|
+
...colonias.length && !coloniaSelect ? { list: listId } : {}
|
|
389
475
|
}
|
|
390
476
|
) : null,
|
|
391
|
-
colonias.length ? /* @__PURE__ */ jsxRuntime.jsx("datalist", { id: listId, children: colonias.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c }, c)) }) : null,
|
|
477
|
+
colonias.length && !coloniaSelect ? /* @__PURE__ */ jsxRuntime.jsx("datalist", { id: listId, children: colonias.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c }, c)) }) : null,
|
|
392
478
|
/* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", "aria-live": "polite", "data-sf-zip-status": true, style: filled ? void 0 : { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" }, className: filled ? join(cx.hint, cx.wide) : void 0, children: filled ? m.lookupFilled : "" }),
|
|
393
479
|
/* @__PURE__ */ jsxRuntime.jsx(CheckoutField, { ...common, name: n("address1"), label: m.address1, autoComplete: "address-line1", defaultValue: defaults?.address1 ?? "", error: errors[n("address1")] ?? null, hint: m.address1Hint, classNames: field(cx, "wide") }),
|
|
394
480
|
/* @__PURE__ */ jsxRuntime.jsx(CheckoutField, { ...common, name: n("address2"), label: m.address2, autoComplete: "address-line2", optional: true, defaultValue: defaults?.address2 ?? "", error: errors[n("address2")] ?? null, classNames: field(cx, "half") }),
|
|
@@ -1163,6 +1249,7 @@ function DeliveryOptions(props) {
|
|
|
1163
1249
|
] });
|
|
1164
1250
|
}
|
|
1165
1251
|
|
|
1252
|
+
exports.ADDRESS_FILL_EVENT_FLAG = ADDRESS_FILL_EVENT_FLAG;
|
|
1166
1253
|
exports.AddressFields = AddressFields;
|
|
1167
1254
|
exports.BrandMark = BrandMark;
|
|
1168
1255
|
exports.CheckoutField = CheckoutField;
|
|
@@ -1180,6 +1267,7 @@ exports.deliveryMessages = deliveryMessages;
|
|
|
1180
1267
|
exports.fieldMessages = fieldMessages;
|
|
1181
1268
|
exports.findBrand = findBrand;
|
|
1182
1269
|
exports.flatRates = flatRates;
|
|
1270
|
+
exports.isAddressFillEvent = isAddressFillEvent;
|
|
1183
1271
|
exports.isQuoteChange = isQuoteChange;
|
|
1184
1272
|
exports.listBrands = listBrands;
|
|
1185
1273
|
exports.matchRate = matchRate;
|