Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import {nanoid} from 'nanoid'
const CheckoutOneClick = () => {
const {formatMessage} = useIntl()
const navigate = useNavigation()
const {step, STEPS} = useCheckout()
const {step, STEPS, contactPhone} = useCheckout()
const showToast = useToast()
const [isLoading, setIsLoading] = useState(false)
const [enableUserRegistration, setEnableUserRegistration] = useState(false)
Expand Down Expand Up @@ -147,7 +147,8 @@ const CheckoutOneClick = () => {

// Form for billing address
const billingAddressForm = useForm({
mode: 'onChange',
mode: 'onTouched',
reValidateMode: 'onChange',
shouldUnregister: false,
defaultValues: {...selectedBillingAddress}
})
Expand Down Expand Up @@ -200,17 +201,66 @@ const CheckoutOneClick = () => {
let billingAddress
if (billingSameAsShipping && selectedShippingAddress) {
billingAddress = selectedShippingAddress
// Validate that shipping address has required address fields
if (!billingAddress?.address1) {
showError(
formatMessage({
id: 'checkout.error.billing_address_required',
defaultMessage: 'Please enter a billing address.'
})
)
return
}
} else {
const isFormValid = await billingAddressForm.trigger()
// Validate all required address fields (excluding phone for billing)
const fieldsToValidate = [
'address1',
'firstName',
'lastName',
'city',
'stateCode',
'postalCode',
'countryCode'
]

// First, mark all fields as touched so errors will be displayed when validation runs
// This must happen BEFORE trigger() so errors show immediately
fieldsToValidate.forEach((field) => {
const currentValue = billingAddressForm.getValues(field) || ''
billingAddressForm.setValue(field, currentValue, {
shouldValidate: false,
shouldTouch: true
})
})

// Now trigger validation - errors will show because fields are already touched
const isFormValid = await billingAddressForm.trigger(fieldsToValidate)

if (!isFormValid) {
// Payment section should already be open from onPlaceOrder
// Focus on the first name field (first field in the form)
setTimeout(() => {
billingAddressForm.setFocus('firstName')
}, 100)
return
}
billingAddress = billingAddressForm.getValues()

// Double-check that address is present
if (!billingAddress?.address1) {
showError(
formatMessage({
id: 'checkout.error.billing_address_required',
defaultMessage: 'Please enter a billing address.'
})
)
setIsEditingPayment(true)
return
}
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const {addressId, creationDate, lastModified, preferred, ...address} = billingAddress
const {addressId, creationDate, lastModified, preferred, phone, ...address} = billingAddress
const latestBasketId = currentBasketQuery.data?.basketId || basket.basketId
return await updateBillingAddressForBasket({
body: address,
Expand Down Expand Up @@ -370,15 +420,21 @@ const CheckoutOneClick = () => {
}
})
}
}

// Also persist billing phone as phoneHome
const phoneHome = order?.billingAddress?.phone
if (phoneHome) {
await updateCustomer.mutateAsync({
parameters: {customerId},
body: {phoneHome}
})
}
// Persist phone number as phoneHome from contact info, shipping address, or basket
// Priority: contactPhone (from contact info form) > shipping address phone > basket customerInfo phone
const phoneHome =
contactPhone && contactPhone.length > 0
? contactPhone
: deliveryShipments.length > 0
? deliveryShipments[0]?.shippingAddress?.phone
: basket?.customerInfo?.phone
if (phoneHome) {
await updateCustomer.mutateAsync({
parameters: {customerId},
body: {phoneHome}
})
}
} catch (_e) {
// Only surface error if shopper opted to register/save details; otherwise fail silently
Expand Down Expand Up @@ -442,6 +498,13 @@ const CheckoutOneClick = () => {
}
}

// Ensure payment section is open before validating billing address
// This ensures the billing form is rendered and visible when we validate
setIsEditingPayment(true)

// Wait for the payment section to open and billing form to render
await new Promise((resolve) => setTimeout(resolve, 0))

// If successful `onBillingSubmit` returns the updated basket. If the form was invalid on
// submit, `undefined` is returned.
const updatedBasket = await onBillingSubmit()
Expand Down
Loading
Loading