@vtex/faststore 0.0.4 → 0.0.6
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/CHANGELOG.md +67 -0
- package/dist/actions.json +181 -0
- package/dist/catalog.json +1595 -481
- package/dist/client.d.ts +2 -1
- package/dist/client.js +2 -1
- package/dist/components.d.ts +31 -7
- package/dist/components.js +35 -13
- package/dist/content-types.json +73 -0
- package/dist/docs/access-code-sign-in.md +1 -1
- package/dist/docs/account-access-gate.md +153 -0
- package/dist/docs/account-entry-point.md +1 -2
- package/dist/docs/account-menu.md +164 -0
- package/dist/docs/address-summary.md +24 -3
- package/dist/docs/alert-dialog.md +204 -0
- package/dist/docs/billing-address.md +284 -0
- package/dist/docs/buy-button.md +1 -1
- package/dist/docs/card-entry.md +250 -0
- package/dist/docs/cardholder-document.md +142 -0
- package/dist/docs/cart.md +1 -1
- package/dist/docs/client-preferences-fieldset.md +148 -0
- package/dist/docs/client-preferences.md +2 -1
- package/dist/docs/client-profile-fieldset.md +184 -0
- package/dist/docs/client-profile.md +1 -1
- package/dist/docs/document-field.md +58 -4
- package/dist/docs/field.md +317 -0
- package/dist/docs/form.md +307 -0
- package/dist/docs/identity-edit.md +6 -6
- package/dist/docs/identity-summary.md +5 -5
- package/dist/docs/installments.md +248 -0
- package/dist/docs/my-account-profile.md +1 -0
- package/dist/docs/order-placed-payment-summary.md +138 -0
- package/dist/docs/payment-method-badges.md +232 -0
- package/dist/docs/payment-methods.md +1 -0
- package/dist/docs/payment.md +218 -0
- package/dist/docs/phone-field.md +58 -4
- package/dist/docs/place-order.md +157 -0
- package/dist/docs/save-card.md +186 -0
- package/dist/docs/saved-cards.md +218 -0
- package/dist/docs/select.md +465 -0
- package/dist/docs/session.md +1 -1
- package/dist/docs/shipping-address.md +110 -258
- package/dist/docs/sign-in-redirect.md +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/internal/client.d.ts +3 -1
- package/dist/internal/client.js +3 -1
- package/dist/matter/access-code-sign-in/access-code-sign-in.js +9 -8
- package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
- package/dist/matter/account-access-gate/account-access-gate.d.ts +86 -0
- package/dist/matter/account-access-gate/account-access-gate.js +283 -0
- package/dist/matter/account-access-gate/account-access-gate.js.map +1 -0
- package/dist/matter/account-access-gate/sign-out-intent.d.ts +17 -0
- package/dist/matter/account-access-gate/sign-out-intent.js +62 -0
- package/dist/matter/account-access-gate/sign-out-intent.js.map +1 -0
- package/dist/matter/account-entry-point/account-entry-point.js +2 -2
- package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
- package/dist/matter/account-menu/account-menu.d.ts +88 -0
- package/dist/matter/account-menu/account-menu.js +246 -0
- package/dist/matter/account-menu/account-menu.js.map +1 -0
- package/dist/matter/billing-address/billing-address.d.ts +165 -0
- package/dist/matter/billing-address/billing-address.js +408 -0
- package/dist/matter/billing-address/billing-address.js.map +1 -0
- package/dist/matter/card-entry/card-capture.d.ts +25 -0
- package/dist/matter/card-entry/card-capture.js +25 -0
- package/dist/matter/card-entry/card-capture.js.map +1 -0
- package/dist/matter/card-entry/card-entry.d.ts +120 -0
- package/dist/matter/card-entry/card-entry.js +832 -0
- package/dist/matter/card-entry/card-entry.js.map +1 -0
- package/dist/matter/cardholder-document/cardholder-document.d.ts +45 -0
- package/dist/matter/cardholder-document/cardholder-document.js +70 -0
- package/dist/matter/cardholder-document/cardholder-document.js.map +1 -0
- package/dist/matter/cart/cart.d.ts +1 -1
- package/dist/matter/cart/cart.js +2 -2
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +77 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.js +299 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -0
- package/dist/matter/client-preferences/client-preferences.d.ts +3 -5
- package/dist/matter/client-preferences/client-preferences.js +6 -6
- package/dist/matter/client-preferences/client-preferences.js.map +1 -1
- package/dist/matter/client-profile/client-profile.d.ts +4 -6
- package/dist/matter/client-profile/client-profile.js +9 -6
- package/dist/matter/client-profile/client-profile.js.map +1 -1
- package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -0
- package/dist/matter/client-profile-form/client-profile-fieldset.js +488 -0
- package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -0
- package/dist/matter/delivery-options/delivery-options.js.map +1 -1
- package/dist/matter/identity-edit/identity-edit.js +2 -2
- package/dist/matter/identity-edit/identity-edit.js.map +1 -1
- package/dist/matter/identity-summary/identity-summary.js +2 -2
- package/dist/matter/identity-summary/identity-summary.js.map +1 -1
- package/dist/matter/installments/installments-select.d.ts +42 -0
- package/dist/matter/installments/installments-select.js +167 -0
- package/dist/matter/installments/installments-select.js.map +1 -0
- package/dist/matter/installments/installments.d.ts +58 -0
- package/dist/matter/installments/installments.js +176 -0
- package/dist/matter/installments/installments.js.map +1 -0
- package/dist/matter/installments/use-installments-selection.d.ts +29 -0
- package/dist/matter/installments/use-installments-selection.js +95 -0
- package/dist/matter/installments/use-installments-selection.js.map +1 -0
- package/dist/matter/my-account-profile/my-account-profile.js +5 -5
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +63 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +305 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -0
- package/dist/matter/payment/payment-card-capture.d.ts +19 -0
- package/dist/matter/payment/payment-card-capture.js +129 -0
- package/dist/matter/payment/payment-card-capture.js.map +1 -0
- package/dist/matter/payment/payment-controller.d.ts +24 -0
- package/dist/matter/payment/payment-controller.js +107 -0
- package/dist/matter/payment/payment-controller.js.map +1 -0
- package/dist/matter/payment/payment-method-badges.d.ts +57 -0
- package/dist/matter/payment/payment-method-badges.js +192 -0
- package/dist/matter/payment/payment-method-badges.js.map +1 -0
- package/dist/matter/payment/payment.d.ts +89 -0
- package/dist/matter/payment/payment.js +263 -0
- package/dist/matter/payment/payment.js.map +1 -0
- package/dist/matter/payment/use-payment-method-selection.d.ts +302 -0
- package/dist/matter/payment/use-payment-method-selection.js +458 -0
- package/dist/matter/payment/use-payment-method-selection.js.map +1 -0
- package/dist/matter/payment/use-payment-submit-gate.d.ts +111 -0
- package/dist/matter/payment/use-payment-submit-gate.js +144 -0
- package/dist/matter/payment/use-payment-submit-gate.js.map +1 -0
- package/dist/matter/place-order/place-order.d.ts +74 -0
- package/dist/matter/place-order/place-order.js +379 -0
- package/dist/matter/place-order/place-order.js.map +1 -0
- package/dist/matter/product-list/product-list.d.ts +1 -0
- package/dist/matter/product-list/product-list.js.map +1 -1
- package/dist/matter/product-shelf/product-shelf.d.ts +1 -0
- package/dist/matter/product-shelf/product-shelf.js +2 -0
- package/dist/matter/product-shelf/product-shelf.js.map +1 -1
- package/dist/matter/region/region.js +1 -1
- package/dist/matter/region/region.js.map +1 -1
- package/dist/matter/save-card/save-card.d.ts +49 -0
- package/dist/matter/save-card/save-card.js +188 -0
- package/dist/matter/save-card/save-card.js.map +1 -0
- package/dist/matter/saved-cards/saved-cards.d.ts +79 -0
- package/dist/matter/saved-cards/saved-cards.js +387 -0
- package/dist/matter/saved-cards/saved-cards.js.map +1 -0
- package/dist/matter/shipping-address/country-descriptors.js +20 -10
- package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
- package/dist/matter/shipping-address/shipping-address.d.ts +113 -174
- package/dist/matter/shipping-address/shipping-address.js +481 -792
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
- package/dist/matter/variant-selector/variant-selector.js +1 -1
- package/dist/particles/address-summary/address-summary.d.ts +24 -0
- package/dist/particles/address-summary/address-summary.js +83 -2
- package/dist/particles/address-summary/address-summary.js.map +1 -1
- package/dist/particles/alert-dialog/alert-dialog.d.ts +68 -0
- package/dist/particles/alert-dialog/alert-dialog.js +187 -0
- package/dist/particles/alert-dialog/alert-dialog.js.map +1 -0
- package/dist/particles/checkbox/checkbox.d.ts +7 -3
- package/dist/particles/checkbox/checkbox.js +4 -1
- package/dist/particles/checkbox/checkbox.js.map +1 -1
- package/dist/particles/document-field/document-field.d.ts +48 -5
- package/dist/particles/document-field/document-field.js +189 -32
- package/dist/particles/document-field/document-field.js.map +1 -1
- package/dist/particles/field/bound-field-registry.js +52 -0
- package/dist/particles/field/bound-field-registry.js.map +1 -0
- package/dist/particles/field/field.d.ts +116 -0
- package/dist/particles/field/field.js +497 -6
- package/dist/particles/field/field.js.map +1 -1
- package/dist/particles/fieldset/fieldset.js +2 -2
- package/dist/particles/fieldset/fieldset.js.map +1 -1
- package/dist/particles/footer-social/footer-social.js +1 -1
- package/dist/particles/form/form.d.ts +104 -0
- package/dist/particles/form/form.js +485 -0
- package/dist/particles/form/form.js.map +1 -0
- package/dist/particles/incentives/incentives.js +1 -1
- package/dist/particles/phone-field/phone-field.d.ts +38 -10
- package/dist/particles/phone-field/phone-field.js +183 -39
- package/dist/particles/phone-field/phone-field.js.map +1 -1
- package/dist/particles/postal-code-field/postal-code-field.js +2 -2
- package/dist/particles/postal-code-field/postal-code-field.js.map +1 -1
- package/dist/particles/select/field-select.d.ts +43 -0
- package/dist/particles/select/field-select.js +150 -0
- package/dist/particles/select/field-select.js.map +1 -0
- package/dist/particles/select/select.d.ts +2 -0
- package/dist/particles/select/select.js.map +1 -1
- package/dist/particles/shared/compose-refs.js +21 -0
- package/dist/particles/shared/compose-refs.js.map +1 -0
- package/dist/schema.graphql +701 -2
- package/dist/sdk/actions/analytics-action-provider.d.ts +15 -0
- package/dist/sdk/actions/analytics-action-provider.js +19 -0
- package/dist/sdk/actions/analytics-action-provider.js.map +1 -0
- package/dist/sdk/actions/cart-action-provider.d.ts +1 -1
- package/dist/sdk/actions/cart-action-provider.js +1 -1
- package/dist/sdk/actions/cart-action-provider.js.map +1 -1
- package/dist/sdk/actions/index.d.ts +3 -1
- package/dist/sdk/actions/index.js +3 -1
- package/dist/sdk/actions/payment-action-provider.d.ts +22 -0
- package/dist/sdk/actions/payment-action-provider.js +26 -0
- package/dist/sdk/actions/payment-action-provider.js.map +1 -0
- package/dist/sdk/actions/session-action-provider.d.ts +1 -1
- package/dist/sdk/actions/session-action-provider.js +1 -1
- package/dist/sdk/actions/session-action-provider.js.map +1 -1
- package/dist/sdk/actions/shipping-action-provider.d.ts +1 -1
- package/dist/sdk/actions/shipping-action-provider.js +1 -1
- package/dist/sdk/actions/shipping-action-provider.js.map +1 -1
- package/dist/sdk/actions/types.d.ts +6 -2
- package/dist/sdk/analytics/analytics-destination.d.ts +43 -0
- package/dist/sdk/analytics/analytics-destination.js +90 -0
- package/dist/sdk/analytics/analytics-destination.js.map +1 -0
- package/dist/sdk/analytics/use-analytics-actions.d.ts +57 -0
- package/dist/sdk/analytics/use-analytics-actions.js +108 -0
- package/dist/sdk/analytics/use-analytics-actions.js.map +1 -0
- package/dist/sdk/cache/storefront-cache.js +1 -1
- package/dist/sdk/cache/storefront-cache.js.map +1 -1
- package/dist/sdk/cart/operations.d.ts +4 -4
- package/dist/sdk/cart/operations.js +1 -1
- package/dist/sdk/cart/operations.js.map +1 -1
- package/dist/sdk/cart/optimistic.js +1 -1
- package/dist/sdk/cart/optimistic.js.map +1 -1
- package/dist/sdk/cart/use-cart-actions.d.ts +1 -1
- package/dist/sdk/cart/use-cart-actions.js +4 -4
- package/dist/sdk/cart/use-cart-actions.js.map +1 -1
- package/dist/sdk/cart/use-cart-validate-on-open.js +1 -1
- package/dist/sdk/cart/use-cart-validate-on-open.js.map +1 -1
- package/dist/sdk/cart/write-gate.js +1 -1
- package/dist/sdk/cart/write-gate.js.map +1 -1
- package/dist/sdk/data/entity-resolution-context.js +31 -0
- package/dist/sdk/data/entity-resolution-context.js.map +1 -0
- package/dist/sdk/data/entity-subscriber.js +18 -8
- package/dist/sdk/data/entity-subscriber.js.map +1 -1
- package/dist/sdk/data/index.js +1 -0
- package/dist/sdk/data/page-reactive-data-root.d.ts +38 -2
- package/dist/sdk/data/page-reactive-data-root.js +4 -2
- package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
- package/dist/sdk/data/reactive-data-provider.d.ts +1 -1
- package/dist/sdk/data/reactive-data-provider.js +8 -4
- package/dist/sdk/data/reactive-data-provider.js.map +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.d.ts +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.js +13 -5
- package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
- package/dist/sdk/data/use-reactive-data-settled.js +39 -0
- package/dist/sdk/data/use-reactive-data-settled.js.map +1 -0
- package/dist/sdk/data/use-reactive-entity.js +6 -2
- package/dist/sdk/data/use-reactive-entity.js.map +1 -1
- package/dist/sdk/generated/fragment-types.js +2 -0
- package/dist/sdk/generated/fragment-types.js.map +1 -1
- package/dist/sdk/generated/gql.js +2 -1
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.js +85 -1
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/i18n/messages/en-US.js +3 -0
- package/dist/sdk/i18n/messages/en-US.json +3 -0
- package/dist/sdk/i18n/messages/en-US.json.d.ts +3 -0
- package/dist/sdk/i18n/messages/es-ES.js +3 -0
- package/dist/sdk/i18n/messages/es-ES.json +3 -0
- package/dist/sdk/i18n/messages/pt-BR.js +3 -0
- package/dist/sdk/i18n/messages/pt-BR.json +3 -0
- package/dist/sdk/identity/operations.js.map +1 -1
- package/dist/sdk/identity/use-identity-actions.d.ts +1 -1
- package/dist/sdk/identity/use-identity-actions.js +3 -2
- package/dist/sdk/identity/use-identity-actions.js.map +1 -1
- package/dist/sdk/payment/gateway-origin.js +35 -0
- package/dist/sdk/payment/gateway-origin.js.map +1 -0
- package/dist/sdk/payment/gateway.d.ts +85 -0
- package/dist/sdk/payment/gateway.js +183 -0
- package/dist/sdk/payment/gateway.js.map +1 -0
- package/dist/sdk/payment/operations.d.ts +52 -0
- package/dist/sdk/payment/operations.js +223 -0
- package/dist/sdk/payment/operations.js.map +1 -0
- package/dist/sdk/payment/security-code-mask.js +71 -0
- package/dist/sdk/payment/security-code-mask.js.map +1 -0
- package/dist/sdk/payment/submission-registry.d.ts +33 -0
- package/dist/sdk/payment/submission-registry.js +58 -0
- package/dist/sdk/payment/submission-registry.js.map +1 -0
- package/dist/sdk/payment/use-payment-actions.d.ts +78 -0
- package/dist/sdk/payment/use-payment-actions.js +276 -0
- package/dist/sdk/payment/use-payment-actions.js.map +1 -0
- package/dist/sdk/payment/use-place-order.d.ts +65 -0
- package/dist/sdk/payment/use-place-order.js +640 -0
- package/dist/sdk/payment/use-place-order.js.map +1 -0
- package/dist/sdk/session/auth-operations.d.ts +1 -1
- package/dist/sdk/session/auth-operations.js.map +1 -1
- package/dist/sdk/session/use-session-actions.d.ts +1 -1
- package/dist/sdk/session/use-session-actions.js.map +1 -1
- package/dist/sdk/session/use-session-revalidation.js +65 -0
- package/dist/sdk/session/use-session-revalidation.js.map +1 -0
- package/dist/sdk/shipping/option-select-operations.d.ts +1 -1
- package/dist/sdk/shipping/option-select-operations.js.map +1 -1
- package/dist/sdk/shipping/request-timeout.js +1 -1
- package/dist/sdk/shipping/request-timeout.js.map +1 -1
- package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
- package/dist/sdk/shipping/shipping-operations.js.map +1 -1
- package/dist/sdk/shipping/use-shipping-actions.d.ts +2 -2
- package/dist/sdk/shipping/use-shipping-actions.js +1 -1
- package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
- package/dist/sdk/state/cart.d.ts +3 -4
- package/dist/sdk/state/cart.js +6 -7
- package/dist/sdk/state/cart.js.map +1 -1
- package/dist/utils/loggable-error-name.js +49 -0
- package/dist/utils/loggable-error-name.js.map +1 -0
- package/dist/utils/resolve-authored-copy.js +18 -0
- package/dist/utils/resolve-authored-copy.js.map +1 -0
- package/dist/utils/safe-href.js +3 -3
- package/dist/utils/safe-href.js.map +1 -1
- package/dist/utils/substitute-template.js +33 -0
- package/dist/utils/substitute-template.js.map +1 -0
- package/package.json +2 -1
- package/src/styles/tailwind.css +34 -0
- package/dist/docs/client-profile-form.md +0 -192
- package/dist/matter/client-profile-form/client-profile-form.d.ts +0 -126
- package/dist/matter/client-profile-form/client-profile-form.js +0 -662
- package/dist/matter/client-profile-form/client-profile-form.js.map +0 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { ComponentDefinition } from "../../core/dist/define-component.js";
|
|
2
|
+
import "../../core/dist/index.js";
|
|
3
|
+
import { ComponentActions } from "../../sdk/actions/types.js";
|
|
4
|
+
import { ComponentPropsWithRef } from "react";
|
|
5
|
+
//#region src/matter/billing-address/billing-address.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* The `BillingAddress` matter (specs/billing-address.md, T11 of the Credit Card epic;
|
|
8
|
+
* docs/features/credit-card/rfc.md § D6). A "same as delivery" toggle (default on) plus, when off, a
|
|
9
|
+
* country-specific address form that persists via `$actions.payment.setBillingAddress`
|
|
10
|
+
* (already shipped by T4, zero consumers before this task).
|
|
11
|
+
*
|
|
12
|
+
* The shared `Address` composition RFC D6 names (shipping and billing as two control matters over
|
|
13
|
+
* one set of reusable particles) is DEFERRED — an implementation-time call the engineer resolved
|
|
14
|
+
* narrow (specs/billing-address.md Decision 1). This component builds its own controls and imports
|
|
15
|
+
* `countryDescriptors` from `../shipping-address/country-descriptors.js`; `matter/shipping-address/`
|
|
16
|
+
* is read, never modified — zero bytes of that directory change here.
|
|
17
|
+
*
|
|
18
|
+
* There is NO read-back path for a persisted billing address (specs/billing-address.md Decision 3):
|
|
19
|
+
* `type Payment` carries no `billingAddress` field, and `setBillingAddress` itself resolves only
|
|
20
|
+
* `userErrors`, discarding the mutation's own echo. This component declares no fragment and renders
|
|
21
|
+
* nothing below the toggle while "same as delivery" is checked, not even the delivery address
|
|
22
|
+
* (specs/stop-re-rendering-the-full-delivery.md, T175). The custom-address form always starts blank,
|
|
23
|
+
* and its own local state is the sole source of truth for what was typed.
|
|
24
|
+
*
|
|
25
|
+
* Only a boolean rides the shared `draft` (`billingAddressDifferent`, widened onto `PaymentDraft` by
|
|
26
|
+
* this task under sole ownership of `use-payment-method-selection.ts` — specs/billing-address.md
|
|
27
|
+
* Decision 5); the address itself is persisted server-side by this component's own action call, never
|
|
28
|
+
* passed through the draft. `setBillingAddress` is dispatched at exactly two points (Decision 9): the
|
|
29
|
+
* toggle's transition back to "same as delivery", and the custom form's explicit submit control —
|
|
30
|
+
* never on mount, never per keystroke.
|
|
31
|
+
*
|
|
32
|
+
* **`draft.billingAddressDifferent` only ever becomes `true` once `setBillingAddress` has actually
|
|
33
|
+
* persisted a different address (PR #544 N1).** Unchecking "same as delivery" opens the form
|
|
34
|
+
* (`formOpen`, local-only state below) but does not touch the draft — `gateway.ts`'s own doc comment
|
|
35
|
+
* ("`setBillingAddress` already persisted it … so the gateway reads it from there") is the contract
|
|
36
|
+
* this must not break: publishing `true` before anything is persisted would point the gateway at an
|
|
37
|
+
* `invoiceData` address nothing ever wrote. The engineer's own resolution for this (session of
|
|
38
|
+
* 2026-07-31): the new-address fields become required whenever "different" is selected, so this
|
|
39
|
+
* component's own submit blocks on an incomplete address (`isBillingAddressFormValid`) — but that
|
|
40
|
+
* alone cannot stop a shopper from declining "same as delivery" and clicking Place Order without ever
|
|
41
|
+
* opening the form's own submit control, which is why the draft write itself is gated on success, not
|
|
42
|
+
* merely the submit button.
|
|
43
|
+
*
|
|
44
|
+
* **The native `required` attribute below is cosmetic here, not an enforcement mechanism (PR #544
|
|
45
|
+
* N7).** This file renders no `aria-required` at all — only `required` (native HTML). There is no
|
|
46
|
+
* `<form>` in this render tree and the submit control is `type="button"`, so the browser's own
|
|
47
|
+
* constraint validation never runs — nothing in this file or its tests may claim this attribute itself
|
|
48
|
+
* prevents a shopper from placing an order with an unsaved address; the actual submit gate is
|
|
49
|
+
* `isBillingAddressFormValid`, read at the submit control's own `disabled` below. Since T38
|
|
50
|
+
* (specs/harden-and-verify-accessibility-across-the.md FR-6) each rendered field does carry a
|
|
51
|
+
* `Field.Error`, but it is driven by a local blur-tracked `touched` set and this same non-blank rule,
|
|
52
|
+
* never by `required`'s own native `ValidityState` — so this paragraph's point is unchanged: `required`
|
|
53
|
+
* itself still enforces nothing here. What this component owes, and
|
|
54
|
+
* does: make the gap **visible** — an inline `role="status"` notice while the form is open and nothing
|
|
55
|
+
* has been persisted yet — and publish that same fact (`setDraftBillingAddressUnsaved`) onto the
|
|
56
|
+
* shared draft, from the identical expression the notice renders from (T24,
|
|
57
|
+
* specs/cardholder-document-submit-gate.md FR-6). **The actual hard block lives elsewhere**: T24's
|
|
58
|
+
* `usePaymentSubmitGate()` reads that published fact and refuses Place Order client-side — this
|
|
59
|
+
* component's own job stops at publishing the fact and showing the notice, never at gating the CTA
|
|
60
|
+
* itself. See `specs/billing-address.md`'s Documentation Gaps #1 for the deferral this closes.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* Local-only values for the "different" address form, keyed by the country descriptor's own
|
|
64
|
+
* canonical field names — a plain string index signature, not a closed set of properties, so a future
|
|
65
|
+
* country descriptor introducing a new field name needs no type change here (mirrors
|
|
66
|
+
* `FieldDescriptor.name: string`'s own deliberately untyped choice; specs/billing-address.md
|
|
67
|
+
* Decision 7). `postalCode` and `country` are named explicitly because this component's own logic
|
|
68
|
+
* reads both directly. Never carries a `recipientName` key: `PaymentBillingAddressUpdateInput` has no
|
|
69
|
+
* such field (Decision 4) — the render loop filters it out of whatever the descriptor lists.
|
|
70
|
+
*/
|
|
71
|
+
export type BillingAddressFormDraft = Readonly<{
|
|
72
|
+
postalCode: string;
|
|
73
|
+
country: string;
|
|
74
|
+
[fieldName: string]: string;
|
|
75
|
+
}>;
|
|
76
|
+
export type BillingAddressProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {
|
|
77
|
+
/** The "same as delivery" toggle's own accessible label. */
|
|
78
|
+
sameAsDeliveryLabel?: string;
|
|
79
|
+
/** ISO 3166-1 alpha-3 default country for the custom-address form's country selector. */
|
|
80
|
+
defaultCountry?: string;
|
|
81
|
+
/** Label for the country selector inside the custom-address form. */
|
|
82
|
+
countryLabel?: string;
|
|
83
|
+
/** Label for the control that persists a custom billing address. */
|
|
84
|
+
submitLabel?: string;
|
|
85
|
+
/** Label on the submit control while the mutation is in flight. */
|
|
86
|
+
submitPendingLabel?: string;
|
|
87
|
+
/** Shown when the selected method's validator marks a billing address required. */
|
|
88
|
+
requiredNoticeLabel?: string;
|
|
89
|
+
/** Label for the recipient-name descriptor field. Not rendered by the custom-address form today
|
|
90
|
+
* (`OMITTED_FORM_FIELD_NAMES` — the write contract has no matching input slot, Decision 4); kept
|
|
91
|
+
* for parity with the other seven field labels this same map covers. */
|
|
92
|
+
recipientNameLabel?: string;
|
|
93
|
+
/** Label for the street descriptor field. */
|
|
94
|
+
streetLabel?: string;
|
|
95
|
+
/** Label for the street-number descriptor field. */
|
|
96
|
+
numberLabel?: string;
|
|
97
|
+
/** Label for the complement descriptor field. Its "(optional)" suffix is the only signal that the
|
|
98
|
+
* field is not required — `OPTIONAL_FORM_FIELD_NAMES` drives the actual `required` attribute
|
|
99
|
+
* independently, so authoring this without the suffix loses the optionality cue, not the behavior
|
|
100
|
+
* (R7). */
|
|
101
|
+
complementLabel?: string;
|
|
102
|
+
/** Label for the neighborhood descriptor field. Unused when the active country's descriptor has no
|
|
103
|
+
* `neighborhood` field (e.g. USA). */
|
|
104
|
+
neighborhoodLabel?: string;
|
|
105
|
+
/** Label for the city descriptor field. */
|
|
106
|
+
cityLabel?: string;
|
|
107
|
+
/** Label for the state descriptor field. */
|
|
108
|
+
stateLabel?: string;
|
|
109
|
+
/** Label for the postal-code descriptor field. */
|
|
110
|
+
postalCodeLabel?: string;
|
|
111
|
+
/** Shown while the form is open and no address has been persisted yet (PR #544 N7) — the shopper
|
|
112
|
+
* declined "same as delivery" but has not (or not yet again) had a submit succeed, so Place Order
|
|
113
|
+
* will bill the delivery address unless they save one first. Rendered `role="status"`, not
|
|
114
|
+
* `role="alert"`: it is a persistent, advisory condition of the form being open, not a one-off
|
|
115
|
+
* announcement reacting to a rejected action (see this file's own existing `-error` alert for that
|
|
116
|
+
* shape) — see `specs/billing-address.md`'s Accessibility Contract for the site count this choice is
|
|
117
|
+
* measured against. */
|
|
118
|
+
unsavedAddressNoticeLabel?: string;
|
|
119
|
+
/** Always-on billing-match disclaimer (requirement 12.20): tells the shopper their billing address
|
|
120
|
+
* must match the one their card issuer or bank statement holds, so an AVS mismatch does not get the
|
|
121
|
+
* order declined with no explanation on screen. Gated on this prop and `billingMatchNoticeVisible`
|
|
122
|
+
* only — reads no `usePayment()` value, no local state, no `$data` (FR-4), unlike the two notices
|
|
123
|
+
* above. */
|
|
124
|
+
billingMatchNoticeLabel?: string;
|
|
125
|
+
/** Switches the billing-match disclaimer above on or off. Defaults to `true` (Decision 5 — the
|
|
126
|
+
* requirement's own intent); subordinate to `billingMatchNoticeLabel.trim()`, which alone can still
|
|
127
|
+
* suppress an empty notice (FR-3) regardless of this prop's value. */
|
|
128
|
+
billingMatchNoticeVisible?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Shown as a `Field.Error` under a required, blank, touched field (T38, FR-6/FR-7) — the same
|
|
131
|
+
* non-blank rule `isBillingAddressFormValid` already applies, except `complement`
|
|
132
|
+
* (`OPTIONAL_FORM_FIELD_NAMES`), which is never marked invalid. One prop, not one per field: every
|
|
133
|
+
* rendered field shares the same rule, so seven props would be seven ways to say the same sentence.
|
|
134
|
+
* Resolved through `resolveAuthoredCopy`.
|
|
135
|
+
*/
|
|
136
|
+
fieldRequiredMessage?: string;
|
|
137
|
+
/** Disables the toggle, every form field, the country selector, and the submit control. */
|
|
138
|
+
disabled?: boolean;
|
|
139
|
+
/** Compiler-injected action surface; carries `payment.setBillingAddress`. */
|
|
140
|
+
$actions?: ComponentActions;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Whether the toggle reads as "same as delivery" — `true` whenever the published flag is `null`
|
|
144
|
+
* (never touched, the default) or `false` (explicitly reverted); only an explicit `true` means the
|
|
145
|
+
* shopper is on the custom-address path. Takes no closure over component state, so it is safe to call
|
|
146
|
+
* fresh on every render.
|
|
147
|
+
*/
|
|
148
|
+
export declare function resolveSameAsDelivery(billingAddressDifferent: boolean | null | undefined): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Whether the custom-address form may be submitted. Stricter than the server's own
|
|
151
|
+
* `MISSING_BILLING_ADDRESS_FIELDS` floor ("sameAsDelivery is false but postalCode **or country** was
|
|
152
|
+
* omitted" [source: `core.graphql:2857-2858`]) — every field the active country's descriptor renders,
|
|
153
|
+
* except `complement` (`OPTIONAL_FORM_FIELD_NAMES`), must be non-blank (PR #544 N1 item 1: the
|
|
154
|
+
* engineer's own rule that the new-address fields are required once "different" is selected). Resolves
|
|
155
|
+
* the descriptor from `formDraft.country` itself rather than taking a second parameter, so this stays a
|
|
156
|
+
* single self-contained fact about one `BillingAddressFormDraft` value. `country` itself is not part of
|
|
157
|
+
* `descriptor.fields` (Decision 7) and is never checked here — it always carries a non-blank value from
|
|
158
|
+
* `defaultCountry` or the selector's own current value (review F12), so it can never be the field this
|
|
159
|
+
* gate would fail on.
|
|
160
|
+
*/
|
|
161
|
+
export declare function isBillingAddressFormValid(formDraft: BillingAddressFormDraft): boolean;
|
|
162
|
+
export declare const BillingAddressDefinition: ComponentDefinition<BillingAddressProps>;
|
|
163
|
+
export declare function BillingAddress({ sameAsDeliveryLabel, defaultCountry, countryLabel, submitLabel, submitPendingLabel, requiredNoticeLabel, unsavedAddressNoticeLabel, billingMatchNoticeLabel, billingMatchNoticeVisible, recipientNameLabel, streetLabel, numberLabel, complementLabel, neighborhoodLabel, cityLabel, stateLabel, postalCodeLabel, fieldRequiredMessage, disabled, className, $actions: $actionsProp, ...props }: BillingAddressProps): import("react/jsx-runtime").JSX.Element | null;
|
|
164
|
+
//#endregion
|
|
165
|
+
//# sourceMappingURL=billing-address.d.ts.map
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { defineComponent } from "../../core/dist/define-component.js";
|
|
3
|
+
import "../../core/dist/index.js";
|
|
4
|
+
import { Checkbox } from "../../particles/checkbox/checkbox.js";
|
|
5
|
+
import { Field } from "../../particles/field/field.js";
|
|
6
|
+
import { useActionsOverride } from "../../sdk/actions/actions-override-context.js";
|
|
7
|
+
import { resolveAuthoredCopy } from "../../utils/resolve-authored-copy.js";
|
|
8
|
+
import { Icon } from "../../particles/icon/icon.js";
|
|
9
|
+
import { Select } from "../../particles/select/select.js";
|
|
10
|
+
import { usePayment } from "../payment/payment.js";
|
|
11
|
+
import { getAvailableCountries, getCountryDescriptor } from "../shipping-address/country-descriptors.js";
|
|
12
|
+
import { useEffect, useId, useState } from "react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
//#region src/matter/billing-address/billing-address.tsx
|
|
15
|
+
/** Canonical field names the custom-address form never renders, even when a country descriptor
|
|
16
|
+
* lists them — because the write contract has no matching input slot (Decision 4). */
|
|
17
|
+
const OMITTED_FORM_FIELD_NAMES = /* @__PURE__ */ new Set(["recipientName"]);
|
|
18
|
+
/** Descriptor field names the custom-address form renders but never marks required, even while
|
|
19
|
+
* "different" is selected (PR #544 N1 item 1 — the engineer's own rule, verbatim: "if the person says
|
|
20
|
+
* the address is different, the new address fields must be required"). `complement` is the one
|
|
21
|
+
* exception: `complementLabel`'s own default already labels it optional ("Complement (optional)"),
|
|
22
|
+
* and neither a real-world address nor the server's own write contract treats it as load-bearing.
|
|
23
|
+
* Shared between the `required` attribute below and `isBillingAddressFormValid`, so the two never
|
|
24
|
+
* drift apart. An authored `complementLabel` that drops the "(optional)" suffix loses that visual cue
|
|
25
|
+
* without changing this set — the two are independent (R7, billing-address.md). */
|
|
26
|
+
const OPTIONAL_FORM_FIELD_NAMES = /* @__PURE__ */ new Set(["complement"]);
|
|
27
|
+
/**
|
|
28
|
+
* English override for the descriptor's own country display names (`'Brasil'`, `'Estados Unidos'`) —
|
|
29
|
+
* `country-descriptors.ts` is shared with `shipping-address` and out of this task's write scope
|
|
30
|
+
* (`matter/shipping-address/` is read-only here), and its labels are Portuguese regardless of country.
|
|
31
|
+
* T31 (specs/make-the-payment-components-authored-copy.md, Decision 5) turned the eight *field* labels
|
|
32
|
+
* this same reasoning used to justify (`FIELD_LABELS_EN`) into schema props instead, but deliberately
|
|
33
|
+
* left this one a plain map: a prop per ISO code does not scale the way eight named field-label props
|
|
34
|
+
* do, and the epic block's own wording covers field labels only. Keyed by the descriptor's own ISO
|
|
35
|
+
* code.
|
|
36
|
+
*/
|
|
37
|
+
const COUNTRY_LABELS_EN = {
|
|
38
|
+
BRA: "Brazil",
|
|
39
|
+
USA: "United States"
|
|
40
|
+
};
|
|
41
|
+
/** Resolves a country's English display name, falling back to the descriptor's own label (then the
|
|
42
|
+
* raw ISO code) for a country this map does not yet cover. */
|
|
43
|
+
function getCountryLabel(iso) {
|
|
44
|
+
return COUNTRY_LABELS_EN[iso] ?? getCountryDescriptor(iso)?.label ?? iso;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether the toggle reads as "same as delivery" — `true` whenever the published flag is `null`
|
|
48
|
+
* (never touched, the default) or `false` (explicitly reverted); only an explicit `true` means the
|
|
49
|
+
* shopper is on the custom-address path. Takes no closure over component state, so it is safe to call
|
|
50
|
+
* fresh on every render.
|
|
51
|
+
*/
|
|
52
|
+
function resolveSameAsDelivery(billingAddressDifferent) {
|
|
53
|
+
return billingAddressDifferent !== true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Whether the custom-address form may be submitted. Stricter than the server's own
|
|
57
|
+
* `MISSING_BILLING_ADDRESS_FIELDS` floor ("sameAsDelivery is false but postalCode **or country** was
|
|
58
|
+
* omitted" [source: `core.graphql:2857-2858`]) — every field the active country's descriptor renders,
|
|
59
|
+
* except `complement` (`OPTIONAL_FORM_FIELD_NAMES`), must be non-blank (PR #544 N1 item 1: the
|
|
60
|
+
* engineer's own rule that the new-address fields are required once "different" is selected). Resolves
|
|
61
|
+
* the descriptor from `formDraft.country` itself rather than taking a second parameter, so this stays a
|
|
62
|
+
* single self-contained fact about one `BillingAddressFormDraft` value. `country` itself is not part of
|
|
63
|
+
* `descriptor.fields` (Decision 7) and is never checked here — it always carries a non-blank value from
|
|
64
|
+
* `defaultCountry` or the selector's own current value (review F12), so it can never be the field this
|
|
65
|
+
* gate would fail on.
|
|
66
|
+
*/
|
|
67
|
+
function isBillingAddressFormValid(formDraft) {
|
|
68
|
+
const descriptor = getCountryDescriptor(formDraft.country);
|
|
69
|
+
if (!descriptor) return false;
|
|
70
|
+
return descriptor.fields.filter((field) => !OMITTED_FORM_FIELD_NAMES.has(field.name) && !OPTIONAL_FORM_FIELD_NAMES.has(field.name)).every((field) => (formDraft[field.name] ?? "").trim().length > 0);
|
|
71
|
+
}
|
|
72
|
+
const BillingAddressDefinition = defineComponent({
|
|
73
|
+
name: "BillingAddress",
|
|
74
|
+
importPath: "@vtex/faststore/components",
|
|
75
|
+
component: BillingAddress,
|
|
76
|
+
requiresContext: "payment",
|
|
77
|
+
actions: { payment: ["setBillingAddress"] },
|
|
78
|
+
editor: {
|
|
79
|
+
label: "Billing Address",
|
|
80
|
+
category: "commerce"
|
|
81
|
+
},
|
|
82
|
+
schema: {
|
|
83
|
+
sameAsDeliveryLabel: {
|
|
84
|
+
type: "string",
|
|
85
|
+
default: "Billing address same as delivery address",
|
|
86
|
+
label: "Same-as-delivery toggle label"
|
|
87
|
+
},
|
|
88
|
+
defaultCountry: {
|
|
89
|
+
type: "string",
|
|
90
|
+
default: "BRA",
|
|
91
|
+
label: "Default country (ISO-3)"
|
|
92
|
+
},
|
|
93
|
+
countryLabel: {
|
|
94
|
+
type: "string",
|
|
95
|
+
default: "Country",
|
|
96
|
+
label: "Country field label"
|
|
97
|
+
},
|
|
98
|
+
submitLabel: {
|
|
99
|
+
type: "string",
|
|
100
|
+
default: "Save billing address",
|
|
101
|
+
label: "Submit button label"
|
|
102
|
+
},
|
|
103
|
+
submitPendingLabel: {
|
|
104
|
+
type: "string",
|
|
105
|
+
default: "Saving...",
|
|
106
|
+
label: "Submit button pending label"
|
|
107
|
+
},
|
|
108
|
+
requiredNoticeLabel: {
|
|
109
|
+
type: "string",
|
|
110
|
+
default: "This payment method requires a billing address.",
|
|
111
|
+
label: "Required-billing-address notice label"
|
|
112
|
+
},
|
|
113
|
+
unsavedAddressNoticeLabel: {
|
|
114
|
+
type: "string",
|
|
115
|
+
default: "No billing address has been saved yet — the delivery address will be used unless you save one below.",
|
|
116
|
+
label: "Unsaved-address notice label"
|
|
117
|
+
},
|
|
118
|
+
billingMatchNoticeLabel: {
|
|
119
|
+
type: "string",
|
|
120
|
+
default: "Your billing address must match the one on your card or bank statement, or your bank may decline the order.",
|
|
121
|
+
label: "Billing-match disclaimer"
|
|
122
|
+
},
|
|
123
|
+
billingMatchNoticeVisible: {
|
|
124
|
+
type: "boolean",
|
|
125
|
+
default: true,
|
|
126
|
+
label: "Show the billing-match disclaimer"
|
|
127
|
+
},
|
|
128
|
+
recipientNameLabel: {
|
|
129
|
+
type: "string",
|
|
130
|
+
default: "Recipient's name",
|
|
131
|
+
label: "Recipient name field label"
|
|
132
|
+
},
|
|
133
|
+
streetLabel: {
|
|
134
|
+
type: "string",
|
|
135
|
+
default: "Street",
|
|
136
|
+
label: "Street field label"
|
|
137
|
+
},
|
|
138
|
+
numberLabel: {
|
|
139
|
+
type: "string",
|
|
140
|
+
default: "Number",
|
|
141
|
+
label: "Number field label"
|
|
142
|
+
},
|
|
143
|
+
complementLabel: {
|
|
144
|
+
type: "string",
|
|
145
|
+
default: "Complement (optional)",
|
|
146
|
+
label: "Complement field label"
|
|
147
|
+
},
|
|
148
|
+
neighborhoodLabel: {
|
|
149
|
+
type: "string",
|
|
150
|
+
default: "Neighborhood",
|
|
151
|
+
label: "Neighborhood field label"
|
|
152
|
+
},
|
|
153
|
+
cityLabel: {
|
|
154
|
+
type: "string",
|
|
155
|
+
default: "City",
|
|
156
|
+
label: "City field label"
|
|
157
|
+
},
|
|
158
|
+
stateLabel: {
|
|
159
|
+
type: "string",
|
|
160
|
+
default: "State",
|
|
161
|
+
label: "State field label"
|
|
162
|
+
},
|
|
163
|
+
postalCodeLabel: {
|
|
164
|
+
type: "string",
|
|
165
|
+
default: "Postal code",
|
|
166
|
+
label: "Postal code field label"
|
|
167
|
+
},
|
|
168
|
+
fieldRequiredMessage: {
|
|
169
|
+
type: "string",
|
|
170
|
+
default: "This field is required.",
|
|
171
|
+
label: "Field required message"
|
|
172
|
+
},
|
|
173
|
+
disabled: {
|
|
174
|
+
type: "boolean",
|
|
175
|
+
default: false,
|
|
176
|
+
label: "Disabled"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
guideline: "Same-as-delivery toggle (default on) for the billing address; when off, mounts a country-specific address form (reusing shipping-address's country descriptors) and persists it via $actions.payment.setBillingAddress on an explicit submit control. Re-checking the toggle dispatches sameAsDelivery: true only once an address was actually persisted (otherwise it's a local no-op — there's nothing on the server to revert). Publishes only a boolean (never the address) onto the shared payment draft for usePlaceOrder to read. While the form is open and nothing has been saved yet, renders an inline status notice — the required attribute on the form's own fields is not a submit gate; Place Order can still complete billed to the delivery address if the shopper never saves. Must be inside a PaymentSectionRoot or PaymentSectionController; place as a sibling of CardEntry/SavedCards/Installments/SaveCard/PlaceOrder, never nested inside any of them. See billing-address.md."
|
|
180
|
+
});
|
|
181
|
+
function BillingAddress({ sameAsDeliveryLabel = "Billing address same as delivery address", defaultCountry = "BRA", countryLabel = "Country", submitLabel = "Save billing address", submitPendingLabel = "Saving...", requiredNoticeLabel = "This payment method requires a billing address.", unsavedAddressNoticeLabel = "No billing address has been saved yet — the delivery address will be used unless you save one below.", billingMatchNoticeLabel = "Your billing address must match the one on your card or bank statement, or your bank may decline the order.", billingMatchNoticeVisible = true, recipientNameLabel = "Recipient's name", streetLabel = "Street", numberLabel = "Number", complementLabel = "Complement (optional)", neighborhoodLabel = "Neighborhood", cityLabel = "City", stateLabel = "State", postalCodeLabel = "Postal code", fieldRequiredMessage, disabled = false, className, $actions: $actionsProp, ...props }) {
|
|
182
|
+
const $actions = useActionsOverride($actionsProp);
|
|
183
|
+
const fieldLabels = {
|
|
184
|
+
recipientName: recipientNameLabel,
|
|
185
|
+
street: streetLabel,
|
|
186
|
+
number: numberLabel,
|
|
187
|
+
complement: complementLabel,
|
|
188
|
+
neighborhood: neighborhoodLabel,
|
|
189
|
+
city: cityLabel,
|
|
190
|
+
state: stateLabel,
|
|
191
|
+
postalCode: postalCodeLabel
|
|
192
|
+
};
|
|
193
|
+
const { draft, selectedMethod, setDraftBillingAddressDifferent, setDraftBillingAddressUnsaved, placing, cardPaymentCollapsed } = usePayment();
|
|
194
|
+
const instanceKey = useId();
|
|
195
|
+
const panelId = useId();
|
|
196
|
+
const [formDraft, setFormDraft] = useState(() => ({
|
|
197
|
+
postalCode: "",
|
|
198
|
+
country: getCountryDescriptor(defaultCountry) ? defaultCountry : getAvailableCountries()[0] ?? defaultCountry,
|
|
199
|
+
street: "",
|
|
200
|
+
number: "",
|
|
201
|
+
complement: "",
|
|
202
|
+
neighborhood: "",
|
|
203
|
+
city: "",
|
|
204
|
+
state: ""
|
|
205
|
+
}));
|
|
206
|
+
const [pending, setPending] = useState(false);
|
|
207
|
+
const [error, setError] = useState(null);
|
|
208
|
+
const [touched, setTouched] = useState(() => /* @__PURE__ */ new Set());
|
|
209
|
+
const [formOpen, setFormOpen] = useState(() => !resolveSameAsDelivery(draft.billingAddressDifferent));
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
setFormOpen(!resolveSameAsDelivery(draft.billingAddressDifferent));
|
|
212
|
+
}, [draft.billingAddressDifferent]);
|
|
213
|
+
const sameAsDelivery = !formOpen;
|
|
214
|
+
const unsavedAddressNoticeVisible = !cardPaymentCollapsed && formOpen && draft.billingAddressDifferent !== true;
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
setDraftBillingAddressUnsaved(instanceKey, unsavedAddressNoticeVisible);
|
|
217
|
+
return () => setDraftBillingAddressUnsaved(instanceKey, false);
|
|
218
|
+
}, [
|
|
219
|
+
instanceKey,
|
|
220
|
+
unsavedAddressNoticeVisible,
|
|
221
|
+
setDraftBillingAddressUnsaved
|
|
222
|
+
]);
|
|
223
|
+
const billingAddressRequired = selectedMethod?.validator.billingAddressRequired ?? false;
|
|
224
|
+
const fieldsDisabled = disabled || pending;
|
|
225
|
+
const requiredMessage = resolveAuthoredCopy(fieldRequiredMessage, "This field is required.");
|
|
226
|
+
const renderableFields = (getCountryDescriptor(formDraft.country)?.fields ?? []).filter((field) => !OMITTED_FORM_FIELD_NAMES.has(field.name));
|
|
227
|
+
async function handleSameAsDeliveryChange(nextSameAsDelivery) {
|
|
228
|
+
setError(null);
|
|
229
|
+
if (!nextSameAsDelivery) {
|
|
230
|
+
setFormOpen(true);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (draft.billingAddressDifferent !== true) {
|
|
234
|
+
setFormOpen(false);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const setBillingAddress = $actions?.payment?.setBillingAddress;
|
|
238
|
+
if (!setBillingAddress) return;
|
|
239
|
+
setPending(true);
|
|
240
|
+
try {
|
|
241
|
+
const [firstError] = await setBillingAddress({ sameAsDelivery: true });
|
|
242
|
+
if (firstError) setError(firstError.message);
|
|
243
|
+
else {
|
|
244
|
+
setDraftBillingAddressDifferent(false);
|
|
245
|
+
setFormOpen(false);
|
|
246
|
+
}
|
|
247
|
+
} finally {
|
|
248
|
+
setPending(false);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function handleFieldChange(name, value) {
|
|
252
|
+
setFormDraft((prev) => ({
|
|
253
|
+
...prev,
|
|
254
|
+
[name]: value
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
/** Marks a field touched on its first blur (T38, FR-6). Idempotent on every later blur — a `Set`
|
|
258
|
+
* already containing `name` is returned unchanged, so this never triggers a redundant re-render. */
|
|
259
|
+
function handleFieldBlur(name) {
|
|
260
|
+
setTouched((prev) => prev.has(name) ? prev : new Set(prev).add(name));
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* A rendered field is invalid once the shopper has touched it, it is blank, and it is not
|
|
264
|
+
* `complement` (T38, FR-6, INV-3/INV-4). Mirrors `isBillingAddressFormValid`'s own per-field rule —
|
|
265
|
+
* the two are never allowed to disagree about which fields are required, since both read
|
|
266
|
+
* `OPTIONAL_FORM_FIELD_NAMES`.
|
|
267
|
+
*/
|
|
268
|
+
function isFieldInvalid(name) {
|
|
269
|
+
return touched.has(name) && !OPTIONAL_FORM_FIELD_NAMES.has(name) && (formDraft[name] ?? "").trim().length === 0;
|
|
270
|
+
}
|
|
271
|
+
function handleCountryChange(value) {
|
|
272
|
+
setFormDraft((prev) => ({
|
|
273
|
+
...prev,
|
|
274
|
+
country: value ?? prev.country
|
|
275
|
+
}));
|
|
276
|
+
}
|
|
277
|
+
async function handleSubmit() {
|
|
278
|
+
const setBillingAddress = $actions?.payment?.setBillingAddress;
|
|
279
|
+
setError(null);
|
|
280
|
+
if (!setBillingAddress) return;
|
|
281
|
+
const isFieldActive = (name) => renderableFields.some((field) => field.name === name);
|
|
282
|
+
setPending(true);
|
|
283
|
+
try {
|
|
284
|
+
const [firstError] = await setBillingAddress({
|
|
285
|
+
sameAsDelivery: false,
|
|
286
|
+
postalCode: formDraft.postalCode,
|
|
287
|
+
country: formDraft.country,
|
|
288
|
+
...isFieldActive("street") ? { street: formDraft.street } : {},
|
|
289
|
+
...isFieldActive("number") ? { number: formDraft.number } : {},
|
|
290
|
+
...isFieldActive("complement") ? { complement: formDraft.complement } : {},
|
|
291
|
+
...isFieldActive("neighborhood") ? { neighborhood: formDraft.neighborhood } : {},
|
|
292
|
+
...isFieldActive("city") ? { city: formDraft.city } : {},
|
|
293
|
+
...isFieldActive("state") ? { state: formDraft.state } : {}
|
|
294
|
+
});
|
|
295
|
+
if (firstError) setError(firstError.message);
|
|
296
|
+
else setDraftBillingAddressDifferent(true);
|
|
297
|
+
} finally {
|
|
298
|
+
setPending(false);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (cardPaymentCollapsed) return null;
|
|
302
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
303
|
+
...props,
|
|
304
|
+
className,
|
|
305
|
+
inert: placing,
|
|
306
|
+
"aria-disabled": placing || void 0,
|
|
307
|
+
"data-fs-billing-address": "",
|
|
308
|
+
...placing ? { "data-fs-billing-address-placing": "" } : {},
|
|
309
|
+
...pending ? { "data-fs-billing-address-pending": "" } : {},
|
|
310
|
+
...sameAsDelivery ? { "data-fs-billing-address-same-as-delivery": "" } : {},
|
|
311
|
+
children: [
|
|
312
|
+
billingMatchNoticeVisible && billingMatchNoticeLabel.trim() ? /* @__PURE__ */ jsx("p", {
|
|
313
|
+
"data-fs-billing-address-match-notice": "",
|
|
314
|
+
children: billingMatchNoticeLabel
|
|
315
|
+
}) : null,
|
|
316
|
+
/* @__PURE__ */ jsxs("div", {
|
|
317
|
+
"data-fs-billing-address-toggle-field": "",
|
|
318
|
+
children: [/* @__PURE__ */ jsx(Field.Root, {
|
|
319
|
+
disabled: fieldsDisabled,
|
|
320
|
+
"data-fs-field-root": "",
|
|
321
|
+
children: /* @__PURE__ */ jsxs(Field.Label, { children: [/* @__PURE__ */ jsx(Checkbox.Root, {
|
|
322
|
+
checked: sameAsDelivery,
|
|
323
|
+
onCheckedChange: handleSameAsDeliveryChange,
|
|
324
|
+
disabled: fieldsDisabled,
|
|
325
|
+
"aria-controls": panelId,
|
|
326
|
+
children: /* @__PURE__ */ jsx(Checkbox.Indicator, { children: /* @__PURE__ */ jsx(Icon, { name: "checked" }) })
|
|
327
|
+
}), sameAsDeliveryLabel] })
|
|
328
|
+
}), billingAddressRequired ? /* @__PURE__ */ jsx("p", {
|
|
329
|
+
"data-fs-billing-address-required-note": "",
|
|
330
|
+
children: requiredNoticeLabel
|
|
331
|
+
}) : null]
|
|
332
|
+
}),
|
|
333
|
+
/* @__PURE__ */ jsx("div", {
|
|
334
|
+
id: panelId,
|
|
335
|
+
"data-fs-billing-address-panel": "",
|
|
336
|
+
hidden: sameAsDelivery,
|
|
337
|
+
children: sameAsDelivery ? null : /* @__PURE__ */ jsxs("div", {
|
|
338
|
+
"data-fs-billing-address-form": "",
|
|
339
|
+
children: [
|
|
340
|
+
unsavedAddressNoticeVisible ? /* @__PURE__ */ jsx("p", {
|
|
341
|
+
role: "status",
|
|
342
|
+
"data-fs-billing-address-unsaved-notice": "",
|
|
343
|
+
children: unsavedAddressNoticeLabel
|
|
344
|
+
}) : null,
|
|
345
|
+
/* @__PURE__ */ jsxs(Field.Root, {
|
|
346
|
+
disabled: fieldsDisabled,
|
|
347
|
+
"data-fs-field-root": "",
|
|
348
|
+
"data-fs-billing-address-field": "country",
|
|
349
|
+
children: [/* @__PURE__ */ jsx(Field.Label, { children: countryLabel }), /* @__PURE__ */ jsxs(Select.Root, {
|
|
350
|
+
value: formDraft.country,
|
|
351
|
+
onValueChange: handleCountryChange,
|
|
352
|
+
disabled: fieldsDisabled,
|
|
353
|
+
required: true,
|
|
354
|
+
children: [/* @__PURE__ */ jsxs(Select.Trigger, { children: [/* @__PURE__ */ jsx(Select.Value, { children: (value) => value ? getCountryLabel(value) : value }), /* @__PURE__ */ jsx(Select.Icon, { children: "▾" })] }), /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, { children: /* @__PURE__ */ jsx(Select.Popup, { children: /* @__PURE__ */ jsx(Select.List, { children: getAvailableCountries().map((iso) => {
|
|
355
|
+
const label = getCountryLabel(iso);
|
|
356
|
+
return /* @__PURE__ */ jsx(Select.Item, {
|
|
357
|
+
value: iso,
|
|
358
|
+
label,
|
|
359
|
+
children: label
|
|
360
|
+
}, iso);
|
|
361
|
+
}) }) }) }) })]
|
|
362
|
+
})]
|
|
363
|
+
}),
|
|
364
|
+
renderableFields.map((field) => {
|
|
365
|
+
const invalid = isFieldInvalid(field.name);
|
|
366
|
+
return /* @__PURE__ */ jsxs(Field.Root, {
|
|
367
|
+
disabled: fieldsDisabled,
|
|
368
|
+
invalid,
|
|
369
|
+
"data-fs-field-root": "",
|
|
370
|
+
"data-fs-billing-address-field": field.name,
|
|
371
|
+
children: [
|
|
372
|
+
/* @__PURE__ */ jsx(Field.Label, { children: fieldLabels[field.name] || field.label }),
|
|
373
|
+
/* @__PURE__ */ jsx(Field.Control, {
|
|
374
|
+
value: formDraft[field.name] ?? "",
|
|
375
|
+
onChange: (event) => handleFieldChange(field.name, event.currentTarget.value),
|
|
376
|
+
onBlur: () => handleFieldBlur(field.name),
|
|
377
|
+
disabled: fieldsDisabled,
|
|
378
|
+
required: !OPTIONAL_FORM_FIELD_NAMES.has(field.name)
|
|
379
|
+
}),
|
|
380
|
+
invalid ? /* @__PURE__ */ jsx(Field.Error, {
|
|
381
|
+
match: true,
|
|
382
|
+
children: requiredMessage
|
|
383
|
+
}) : null
|
|
384
|
+
]
|
|
385
|
+
}, field.name);
|
|
386
|
+
}),
|
|
387
|
+
/* @__PURE__ */ jsx("button", {
|
|
388
|
+
type: "button",
|
|
389
|
+
onClick: handleSubmit,
|
|
390
|
+
disabled: fieldsDisabled || !isBillingAddressFormValid(formDraft),
|
|
391
|
+
"data-fs-billing-address-submit": "",
|
|
392
|
+
children: pending ? submitPendingLabel : submitLabel
|
|
393
|
+
})
|
|
394
|
+
]
|
|
395
|
+
})
|
|
396
|
+
}),
|
|
397
|
+
error ? /* @__PURE__ */ jsx("div", {
|
|
398
|
+
role: "alert",
|
|
399
|
+
"data-fs-billing-address-error": "",
|
|
400
|
+
children: error
|
|
401
|
+
}) : null
|
|
402
|
+
]
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
//#endregion
|
|
406
|
+
export { BillingAddress, BillingAddressDefinition, isBillingAddressFormValid, resolveSameAsDelivery };
|
|
407
|
+
|
|
408
|
+
//# sourceMappingURL=billing-address.js.map
|