2023-10-09 11:49:09 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2023-10-10 21:07:58 +00:00
|
|
|
import { useState, useCallback, useEffect } from '@wordpress/element';
|
2023-10-09 11:49:09 +00:00
|
|
|
import { AddressForm } from '@woocommerce/base-components/cart-checkout';
|
2023-11-09 16:25:28 +00:00
|
|
|
import {
|
|
|
|
useCheckoutAddress,
|
|
|
|
useStoreEvents,
|
|
|
|
useEditorContext,
|
|
|
|
} from '@woocommerce/base-context';
|
2023-10-09 11:49:09 +00:00
|
|
|
import type {
|
|
|
|
ShippingAddress,
|
|
|
|
AddressField,
|
|
|
|
AddressFields,
|
|
|
|
} from '@woocommerce/settings';
|
2023-10-10 21:07:58 +00:00
|
|
|
import { useSelect } from '@wordpress/data';
|
|
|
|
import { VALIDATION_STORE_KEY } from '@woocommerce/block-data';
|
2023-10-09 11:49:09 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import AddressWrapper from '../../address-wrapper';
|
|
|
|
import PhoneNumber from '../../phone-number';
|
|
|
|
import AddressCard from '../../address-card';
|
|
|
|
|
|
|
|
const CustomerAddress = ( {
|
|
|
|
addressFieldsConfig,
|
|
|
|
showPhoneField,
|
|
|
|
requirePhoneField,
|
|
|
|
}: {
|
|
|
|
addressFieldsConfig: Record< keyof AddressFields, Partial< AddressField > >;
|
|
|
|
showPhoneField: boolean;
|
|
|
|
requirePhoneField: boolean;
|
|
|
|
} ) => {
|
|
|
|
const {
|
|
|
|
defaultAddressFields,
|
|
|
|
shippingAddress,
|
|
|
|
setShippingAddress,
|
|
|
|
setBillingAddress,
|
|
|
|
setShippingPhone,
|
2023-10-20 10:41:57 +00:00
|
|
|
setBillingPhone,
|
2023-10-09 11:49:09 +00:00
|
|
|
useShippingAsBilling,
|
|
|
|
} = useCheckoutAddress();
|
|
|
|
const { dispatchCheckoutEvent } = useStoreEvents();
|
2023-11-09 16:25:28 +00:00
|
|
|
const { isEditor } = useEditorContext();
|
2023-10-10 21:07:58 +00:00
|
|
|
const hasAddress = !! (
|
|
|
|
shippingAddress.address_1 &&
|
|
|
|
( shippingAddress.first_name || shippingAddress.last_name )
|
|
|
|
);
|
2023-11-09 16:25:28 +00:00
|
|
|
const [ editing, setEditing ] = useState( ! hasAddress || isEditor );
|
2023-10-10 21:07:58 +00:00
|
|
|
|
|
|
|
// Forces editing state if store has errors.
|
|
|
|
const { hasValidationErrors, invalidProps } = useSelect( ( select ) => {
|
|
|
|
const store = select( VALIDATION_STORE_KEY );
|
|
|
|
return {
|
|
|
|
hasValidationErrors: store.hasValidationErrors(),
|
|
|
|
invalidProps: Object.keys( shippingAddress )
|
|
|
|
.filter( ( key ) => {
|
|
|
|
return (
|
|
|
|
store.getValidationError( 'shipping_' + key ) !==
|
|
|
|
undefined
|
|
|
|
);
|
|
|
|
} )
|
|
|
|
.filter( Boolean ),
|
|
|
|
};
|
|
|
|
} );
|
|
|
|
|
|
|
|
useEffect( () => {
|
|
|
|
if ( invalidProps.length > 0 && editing === false ) {
|
|
|
|
setEditing( true );
|
|
|
|
}
|
|
|
|
}, [ editing, hasValidationErrors, invalidProps.length ] );
|
|
|
|
|
2023-10-09 11:49:09 +00:00
|
|
|
const addressFieldKeys = Object.keys(
|
|
|
|
defaultAddressFields
|
|
|
|
) as ( keyof AddressFields )[];
|
|
|
|
|
|
|
|
const onChangeAddress = useCallback(
|
|
|
|
( values: Partial< ShippingAddress > ) => {
|
|
|
|
setShippingAddress( values );
|
|
|
|
if ( useShippingAsBilling ) {
|
|
|
|
// Sync billing with shipping. Ensure unwanted properties are omitted.
|
|
|
|
const { ...syncBilling } = values;
|
|
|
|
|
|
|
|
if ( ! showPhoneField ) {
|
|
|
|
delete syncBilling.phone;
|
|
|
|
}
|
|
|
|
|
|
|
|
setBillingAddress( syncBilling );
|
|
|
|
dispatchCheckoutEvent( 'set-billing-address' );
|
|
|
|
}
|
|
|
|
dispatchCheckoutEvent( 'set-shipping-address' );
|
|
|
|
},
|
|
|
|
[
|
|
|
|
dispatchCheckoutEvent,
|
|
|
|
setBillingAddress,
|
|
|
|
setShippingAddress,
|
|
|
|
useShippingAsBilling,
|
|
|
|
showPhoneField,
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
const renderAddressCardComponent = useCallback(
|
|
|
|
() => (
|
|
|
|
<AddressCard
|
|
|
|
address={ shippingAddress }
|
|
|
|
target="shipping"
|
|
|
|
onEdit={ () => {
|
|
|
|
setEditing( true );
|
|
|
|
} }
|
2023-11-09 16:25:28 +00:00
|
|
|
showPhoneField={ showPhoneField }
|
2023-10-09 11:49:09 +00:00
|
|
|
/>
|
|
|
|
),
|
2023-11-09 16:25:28 +00:00
|
|
|
[ shippingAddress, showPhoneField ]
|
2023-10-09 11:49:09 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
const renderAddressFormComponent = useCallback(
|
|
|
|
() => (
|
|
|
|
<>
|
|
|
|
<AddressForm
|
|
|
|
id="shipping"
|
|
|
|
type="shipping"
|
|
|
|
onChange={ onChangeAddress }
|
|
|
|
values={ shippingAddress }
|
|
|
|
fields={ addressFieldKeys }
|
|
|
|
fieldConfig={ addressFieldsConfig }
|
|
|
|
/>
|
|
|
|
{ showPhoneField && (
|
|
|
|
<PhoneNumber
|
|
|
|
id="shipping-phone"
|
|
|
|
errorId={ 'shipping_phone' }
|
|
|
|
isRequired={ requirePhoneField }
|
|
|
|
value={ shippingAddress.phone }
|
|
|
|
onChange={ ( value ) => {
|
|
|
|
setShippingPhone( value );
|
|
|
|
dispatchCheckoutEvent( 'set-phone-number', {
|
|
|
|
step: 'shipping',
|
|
|
|
} );
|
2023-10-20 10:41:57 +00:00
|
|
|
if ( useShippingAsBilling ) {
|
|
|
|
setBillingPhone( value );
|
|
|
|
dispatchCheckoutEvent( 'set-phone-number', {
|
|
|
|
step: 'billing',
|
|
|
|
} );
|
|
|
|
}
|
2023-10-09 11:49:09 +00:00
|
|
|
} }
|
|
|
|
/>
|
|
|
|
) }
|
|
|
|
</>
|
|
|
|
),
|
|
|
|
[
|
|
|
|
addressFieldKeys,
|
|
|
|
addressFieldsConfig,
|
|
|
|
dispatchCheckoutEvent,
|
|
|
|
onChangeAddress,
|
|
|
|
requirePhoneField,
|
2023-11-09 16:25:28 +00:00
|
|
|
setBillingPhone,
|
2023-10-09 11:49:09 +00:00
|
|
|
setShippingPhone,
|
|
|
|
shippingAddress,
|
|
|
|
showPhoneField,
|
2023-11-09 16:25:28 +00:00
|
|
|
useShippingAsBilling,
|
2023-10-09 11:49:09 +00:00
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<AddressWrapper
|
|
|
|
isEditing={ editing }
|
|
|
|
addressCard={ renderAddressCardComponent }
|
|
|
|
addressForm={ renderAddressFormComponent }
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default CustomerAddress;
|