Searching addresses in order drafts (#1618) (#1655)

* Searching addresses in order drafts (#1618)

* wip search addresses

* wip fix query regex verification

* wip move dialog title & add selected label

* wip move dialog title

* wip edit icon

* wip address selection logic

* wip change messages

* wip message when search is unsuccesful

* wip add billing address change support

* wip default address validation & cleanup

* wip run tests & extract messages

* wip bump macaw

* tests & cleanup

* fix scrollbars

* remove address card wrapper

* apply code review suggestions

* remove comments

* Bump macaw to 0.2.7

* Fix outside modal click state retention
This commit is contained in:
Michał Droń 2021-12-03 12:59:29 +01:00 committed by GitHub
parent bdaeb8b621
commit 78f7b5d4fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 468 additions and 129 deletions

View file

@ -4047,7 +4047,11 @@
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_billingSameAsShipping": {
"context": "checkbox label",
"string": "Billing address same as shipping address"
"string": "Set the same for billing address"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_billingTitle": {
"context": "search modal billing title",
"string": "Billing address"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_customerAddress": {
"context": "address type",
@ -4065,13 +4069,24 @@
"context": "address type",
"string": "Add new address"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_noResultsFound": {
"string": "No results found"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_searchInfo": {
"context": "modal information under title",
"string": "Select an address you want to use from the list below"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_shippingAddressDescription": {
"context": "dialog content",
"string": "This customer doesnt have any shipping addresses. Provide address for order:"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_shippingTitle": {
"context": "search modal shipping title",
"string": "Shipping address"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_title": {
"context": "dialog header",
"string": "Shipping address for order"
"string": "Change address for order"
},
"src_dot_orders_dot_components_dot_OrderCustomerChangeDialog_dot_changeAddress": {
"context": "option label",
@ -6748,6 +6763,9 @@
"context": "select all options, button",
"string": "Select All"
},
"src_dot_selected": {
"string": "Selected"
},
"src_dot_send": {
"context": "button",
"string": "Send"

6
package-lock.json generated
View file

@ -5166,9 +5166,9 @@
}
},
"@saleor/macaw-ui": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.2.6.tgz",
"integrity": "sha512-ySEhWN9kyxX+5ATXVOg4siS5RwMRIMcLAybJHax0SpzS4E6FtLM5VDQhWliHaM2hicFPMVrLBa7MkTvAV1JcIA==",
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@saleor/macaw-ui/-/macaw-ui-0.2.7.tgz",
"integrity": "sha512-sRt193W5u1Vu+5893zEsRbkVF3H1uXuqNC+BEXwTg85i+J9YHmykJZjDneExgGH24nYe948S5dffsy3NMOGSTQ==",
"requires": {
"clsx": "^1.1.1",
"lodash": "^4.17.21",

View file

@ -27,7 +27,7 @@
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.58",
"@material-ui/styles": "^4.11.4",
"@saleor/macaw-ui": "^0.2.6",
"@saleor/macaw-ui": "^0.2.7",
"@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6",
"@uiw/react-color-hue": "0.0.34",

View file

@ -1,30 +1,48 @@
import { Card, CardContent } from "@material-ui/core";
import { Card, CardContent, Typography } from "@material-ui/core";
import AddressFormatter from "@saleor/components/AddressFormatter";
import { commonMessages } from "@saleor/intl";
import { EditIcon } from "@saleor/macaw-ui";
import classNames from "classnames";
import React from "react";
import { useIntl } from "react-intl";
import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses";
import { useStyles } from "./styles";
export interface CustomerAddressChoiceCardProps {
address: CustomerAddresses_user_addresses;
selected: boolean;
onSelect: () => void;
selected?: boolean;
editable?: boolean;
onSelect?: () => void;
onEditClick?: () => void;
}
const CustomerAddressChoiceCard: React.FC<CustomerAddressChoiceCardProps> = props => {
const { address, selected, onSelect } = props;
const { address, selected, editable, onSelect, onEditClick } = props;
const classes = useStyles(props);
const intl = useIntl();
return (
<Card
className={classNames(classes.card, {
[classes.cardSelected]: selected
[classes.cardSelected]: selected,
[classes.selectableCard]: !editable
})}
onClick={onSelect}
>
<CardContent>
<CardContent className={classes.cardContent}>
<AddressFormatter address={address} />
{editable && (
<div onClick={onEditClick}>
<EditIcon className={classes.editIcon} />
</div>
)}
{selected && (
<Typography color="primary" className={classes.selectedLabel}>
{intl.formatMessage(commonMessages.selected)}
</Typography>
)}
</CardContent>
</Card>
);

View file

@ -3,13 +3,37 @@ import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
theme => ({
card: {
cursor: "pointer",
padding: "1px"
},
cardSelected: {
borderColor: theme.palette.primary.main,
borderWidth: "2px",
padding: "0"
},
cardContent: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "flex-start"
},
selectableCard: {
"&:hover": {
cursor: "pointer",
borderColor: theme.palette.primary.main
}
},
selectedLabel: {
fontSize: "1.4rem",
lineHeight: "1.75",
fontWeight: 600,
textTransform: "uppercase"
},
editIcon: {
color: theme.palette.grey[600],
"&:hover": {
color: theme.palette.primary.main,
cursor: "pointer"
}
}
}),
{ name: "CustomerAddressChoiceCard" }

View file

@ -84,6 +84,9 @@ export const commonMessages = defineMessages({
savedChanges: {
defaultMessage: "Saved changes"
},
selected: {
defaultMessage: "Selected"
},
sessionExpired: {
defaultMessage: "Your session has expired. Please log in again to continue."
},

View file

@ -434,3 +434,18 @@ export function getFullName<T extends { firstName: string; lastName: string }>(
return `${data.firstName} ${data.lastName}`;
}
export const flatten = (obj: unknown) => {
// Be cautious that repeated keys are overwritten
const result = {};
Object.keys(obj).forEach(key => {
if (typeof obj[key] === "object" && obj[key] !== null) {
Object.assign(result, flatten(obj[key]));
} else {
result[key] = obj[key];
}
});
return result;
};

View file

@ -13,6 +13,7 @@ import { FormChange } from "@saleor/hooks/useForm";
import React from "react";
import { useIntl } from "react-intl";
import { getById } from "../OrderReturnPage/utils";
import { AddressInputOptionEnum } from "./form";
import { addressEditMessages } from "./messages";
import { useStyles } from "./styles";
@ -24,15 +25,13 @@ export interface OrderCustomerAddressEditProps {
addressInputOption: AddressInputOptionEnum;
addressInputName: string;
onChangeAddressInputOption: FormChange;
customerAddressId: string;
selectedCustomerAddressId: string;
formAddress: AddressTypeInput;
formAddressCountryDisplayName: string;
formErrors: Array<AccountErrorFragment | OrderErrorFragment>;
onChangeCustomerAddress: (
customerAddress: CustomerAddresses_user_addresses
) => void;
onChangeFormAddress: (event: React.ChangeEvent<any>) => void;
onChangeFormAddressCountry: (event: React.ChangeEvent<any>) => void;
onEdit?: () => void;
}
const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props => {
@ -43,13 +42,13 @@ const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props
addressInputOption,
addressInputName,
onChangeAddressInputOption,
customerAddressId,
selectedCustomerAddressId,
formAddress,
formAddressCountryDisplayName,
formErrors,
onChangeCustomerAddress,
onChangeFormAddress,
onChangeFormAddressCountry
onChangeFormAddressCountry,
onEdit
} = props;
const classes = useStyles(props);
@ -92,19 +91,15 @@ const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props
className={classes.optionLabel}
/>
{addressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS && (
<div className={classes.scrollableWrapper}>
{customerAddresses.map(customerAddress => (
<React.Fragment key={customerAddress.id}>
<CardSpacer />
<CustomerAddressChoiceCard
address={customerAddress}
selected={customerAddress.id === customerAddressId}
onSelect={() => onChangeCustomerAddress(customerAddress)}
/>
</React.Fragment>
))}
<>
<CardSpacer />
<CustomerAddressChoiceCard
address={customerAddresses.find(getById(selectedCustomerAddressId))}
editable
onEditClick={onEdit}
/>
<FormSpacer />
</div>
</>
)}
<FormControlLabel
value={AddressInputOptionEnum.NEW_ADDRESS}

View file

@ -36,8 +36,14 @@ import OrderCustomerAddressesEditForm, {
} from "./form";
import { dialogMessages } from "./messages";
import OrderCustomerAddressEdit from "./OrderCustomerAddressEdit";
import OrderCustomerAddressesSearch from "./OrderCustomerAddressesSearch";
import { useStyles } from "./styles";
import { validateDefaultAddress } from "./utils";
export interface OrderCustomerSearchAddressState {
open: boolean;
type: AddressTypeEnum;
}
export interface OrderCustomerAddressesEditDialogOutput {
shippingAddress: AddressInput;
billingAddress: AddressInput;
@ -56,6 +62,11 @@ export interface OrderCustomerAddressesEditDialogProps {
onConfirm(data: OrderCustomerAddressesEditDialogOutput): SubmitPromise;
}
const defaultSearchState: OrderCustomerSearchAddressState = {
open: false,
type: undefined
};
const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialogProps> = props => {
const {
open,
@ -85,9 +96,11 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
open
);
const getCustomerAddress = (customerAddressId: string): AddressInput =>
const getCustomerAddress = (
selectedCustomerAddressID: string
): AddressInput =>
transformAddressToAddressInput(
customerAddresses.find(getById(customerAddressId))
customerAddresses.find(getById(selectedCustomerAddressID))
);
const handleAddressesSubmit = (data: OrderCustomerAddressesEditFormData) => {
@ -127,88 +140,73 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
const countryChoices = mapCountriesToChoices(countries);
const [addressSearchState, setAddressSearchState] = React.useState<
OrderCustomerSearchAddressState
>(defaultSearchState);
const validatedDefaultShippingAddress = validateDefaultAddress(
defaultShippingAddress,
customerAddresses
);
const validatedDefaultBillingAddress = validateDefaultAddress(
defaultBillingAddress,
customerAddresses
);
return (
<Dialog onClose={onClose} open={open}>
<Dialog
onClose={() => {
setAddressSearchState(defaultSearchState);
onClose();
}}
open={open}
fullWidth
>
<OrderCustomerAddressesEditForm
countryChoices={countryChoices}
defaultShippingAddress={defaultShippingAddress}
defaultBillingAddress={defaultBillingAddress}
defaultShippingAddress={validatedDefaultShippingAddress}
defaultBillingAddress={validatedDefaultBillingAddress}
onSubmit={handleSubmit}
>
{({ change, data, handlers }) => (
<>
<DialogTitle>
<FormattedMessage {...dialogMessages.title} />
</DialogTitle>
<DialogContent className={classes.scrollableContent}>
<Typography>
{customerAddresses.length > 0 ? (
<FormattedMessage
{...dialogMessages.customerShippingAddressDescription}
/>
) : (
<FormattedMessage
{...dialogMessages.shippingAddressDescription}
/>
)}
</Typography>
<FormSpacer />
<OrderCustomerAddressEdit
loading={loading}
countryChoices={countryChoices}
addressInputOption={data.shippingAddressInputOption}
addressInputName="shippingAddressInputOption"
onChangeAddressInputOption={change}
{addressSearchState.open ? (
<OrderCustomerAddressesSearch
type={addressSearchState?.type}
customerAddresses={customerAddresses}
customerAddressId={data.customerShippingAddress?.id}
formAddress={data.shippingAddress}
formAddressCountryDisplayName={data.shippingCountryDisplayName}
formErrors={dialogErrors.filter(
error => error.addressType === AddressTypeEnum.SHIPPING
)}
onChangeCustomerAddress={customerAddress =>
selectedCustomerAddressId={
addressSearchState.type === AddressTypeEnum.SHIPPING
? data.customerShippingAddress?.id
: data.customerBillingAddress?.id
}
onChangeCustomerShippingAddress={customerAddress =>
handlers.changeCustomerAddress(
customerAddress,
"customerShippingAddress"
)
}
onChangeFormAddress={event =>
handlers.changeFormAddress(event, "shippingAddress")
onChangeCustomerBillingAddress={customerAddress =>
handlers.changeCustomerAddress(
customerAddress,
"customerBillingAddress"
)
}
onChangeFormAddressCountry={handlers.selectShippingCountry}
exitSearch={() => setAddressSearchState(defaultSearchState)}
/>
<FormSpacer />
<Divider />
<FormSpacer />
<FormControlLabel
control={
<Checkbox
checked={data.billingSameAsShipping}
name="billingSameAsShipping"
onChange={() =>
change({
target: {
name: "billingSameAsShipping",
value: !data.billingSameAsShipping
}
})
}
data-test="billingSameAsShipping"
/>
}
label={intl.formatMessage(dialogMessages.billingSameAsShipping)}
/>
{!data.billingSameAsShipping && (
<>
<FormSpacer />
) : (
<>
<DialogTitle>
<FormattedMessage {...dialogMessages.title} />
</DialogTitle>
<DialogContent className={classes.scrollableContent}>
<Typography>
{customerAddresses.length > 0 ? (
<FormattedMessage
{...dialogMessages.customerBillingAddressDescription}
{...dialogMessages.customerShippingAddressDescription}
/>
) : (
<FormattedMessage
{...dialogMessages.billingAddressDescription}
{...dialogMessages.shippingAddressDescription}
/>
)}
</Typography>
@ -216,43 +214,113 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
<OrderCustomerAddressEdit
loading={loading}
countryChoices={countryChoices}
addressInputOption={data.billingAddressInputOption}
addressInputName="billingAddressInputOption"
addressInputOption={data.shippingAddressInputOption}
addressInputName="shippingAddressInputOption"
onChangeAddressInputOption={change}
customerAddresses={customerAddresses}
customerAddressId={data.customerBillingAddress?.id}
formAddress={data.billingAddress}
selectedCustomerAddressId={data.customerShippingAddress?.id}
formAddress={data.shippingAddress}
formAddressCountryDisplayName={
data.billingCountryDisplayName
data.shippingCountryDisplayName
}
formErrors={dialogErrors.filter(
error => error.addressType === AddressTypeEnum.BILLING
error => error.addressType === AddressTypeEnum.SHIPPING
)}
onChangeCustomerAddress={customerAddress =>
handlers.changeCustomerAddress(
customerAddress,
"customerBillingAddress"
)
}
onChangeFormAddress={event =>
handlers.changeFormAddress(event, "billingAddress")
handlers.changeFormAddress(event, "shippingAddress")
}
onChangeFormAddressCountry={handlers.selectShippingCountry}
onEdit={() =>
setAddressSearchState({
open: true,
type: AddressTypeEnum.SHIPPING
})
}
onChangeFormAddressCountry={handlers.selectBillingCountry}
/>
</>
)}
</DialogContent>
<DialogActions>
<ConfirmButton
transitionState={confirmButtonState}
color="primary"
variant="contained"
type="submit"
data-test="submit"
>
<FormattedMessage {...buttonMessages.select} />
</ConfirmButton>
</DialogActions>
<FormSpacer />
<Divider />
<FormSpacer />
<FormControlLabel
control={
<Checkbox
checked={data.billingSameAsShipping}
name="billingSameAsShipping"
onChange={() =>
change({
target: {
name: "billingSameAsShipping",
value: !data.billingSameAsShipping
}
})
}
data-test="billingSameAsShipping"
/>
}
label={intl.formatMessage(
dialogMessages.billingSameAsShipping
)}
/>
{!data.billingSameAsShipping && (
<>
<FormSpacer />
<Typography>
{customerAddresses.length > 0 ? (
<FormattedMessage
{...dialogMessages.customerBillingAddressDescription}
/>
) : (
<FormattedMessage
{...dialogMessages.billingAddressDescription}
/>
)}
</Typography>
<FormSpacer />
<OrderCustomerAddressEdit
loading={loading}
countryChoices={countryChoices}
addressInputOption={data.billingAddressInputOption}
addressInputName="billingAddressInputOption"
onChangeAddressInputOption={change}
customerAddresses={customerAddresses}
selectedCustomerAddressId={
data.customerBillingAddress?.id
}
formAddress={data.billingAddress}
formAddressCountryDisplayName={
data.billingCountryDisplayName
}
formErrors={dialogErrors.filter(
error => error.addressType === AddressTypeEnum.BILLING
)}
onChangeFormAddress={event =>
handlers.changeFormAddress(event, "billingAddress")
}
onChangeFormAddressCountry={
handlers.selectBillingCountry
}
onEdit={() =>
setAddressSearchState({
open: true,
type: AddressTypeEnum.BILLING
})
}
/>
</>
)}
</DialogContent>
<DialogActions>
<ConfirmButton
transitionState={confirmButtonState}
color="primary"
variant="contained"
type="submit"
data-test="submit"
>
<FormattedMessage {...buttonMessages.save} />
</ConfirmButton>
</DialogActions>
</>
)}
</>
)}
</OrderCustomerAddressesEditForm>

View file

@ -0,0 +1,140 @@
import {
Button,
DialogActions,
DialogContent,
DialogTitle,
InputAdornment,
TextField
} from "@material-ui/core";
import CardSpacer from "@saleor/components/CardSpacer";
import { ConfirmButton } from "@saleor/components/ConfirmButton";
import CustomerAddressChoiceCard from "@saleor/customers/components/CustomerAddressChoiceCard";
import { CustomerAddresses_user_addresses } from "@saleor/customers/types/CustomerAddresses";
import { buttonMessages } from "@saleor/intl";
import { SearchIcon } from "@saleor/macaw-ui";
import { AddressTypeEnum } from "@saleor/types/globalTypes";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { getById } from "../OrderReturnPage/utils";
import { addressSearchMessages as messages } from "./messages";
import { useStyles } from "./styles";
import { parseQuery, stringifyAddress } from "./utils";
export interface OrderCustomerAddressesSearchProps {
type: AddressTypeEnum;
selectedCustomerAddressId?: string;
customerAddresses: CustomerAddresses_user_addresses[];
onChangeCustomerShippingAddress: (
customerAddress: CustomerAddresses_user_addresses
) => void;
onChangeCustomerBillingAddress: (
customerAddress: CustomerAddresses_user_addresses
) => void;
exitSearch();
}
const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps> = props => {
const {
type,
selectedCustomerAddressId,
customerAddresses,
onChangeCustomerShippingAddress,
onChangeCustomerBillingAddress,
exitSearch
} = props;
const intl = useIntl();
const classes = useStyles(props);
const initialAddress = customerAddresses.find(
getById(selectedCustomerAddressId)
);
const [query, setQuery] = React.useState("");
const [
temporarySelectedAddress,
setTemporarySelectedAddress
] = React.useState(initialAddress);
const handleSelect = () => {
if (type === AddressTypeEnum.SHIPPING) {
onChangeCustomerShippingAddress(temporarySelectedAddress);
} else {
onChangeCustomerBillingAddress(temporarySelectedAddress);
}
exitSearch();
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
};
const filteredCustomerAddresses = customerAddresses.filter(address => {
const parsedAddress = stringifyAddress(address);
return parsedAddress.search(new RegExp(parseQuery(query), "i")) >= 0;
});
return (
<>
<DialogTitle>
{type === AddressTypeEnum.SHIPPING ? (
<FormattedMessage {...messages.shippingTitle} />
) : (
<FormattedMessage {...messages.billingTitle} />
)}
</DialogTitle>
<DialogContent>
{intl.formatMessage(messages.searchInfo)}
<CardSpacer />
<TextField
value={query}
variant="outlined"
onChange={handleChange}
placeholder={"Search addresses"}
fullWidth
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
)
}}
inputProps={{ className: classes.searchInput }}
/>
<CardSpacer />
<div className={classes.scrollableWrapper}>
{filteredCustomerAddresses.length === 0
? intl.formatMessage(messages.noResultsFound)
: filteredCustomerAddresses?.map(address => (
<React.Fragment key={address.id}>
<CustomerAddressChoiceCard
selected={address.id === temporarySelectedAddress.id}
onSelect={() => setTemporarySelectedAddress(address)}
address={address}
/>
<CardSpacer />
</React.Fragment>
))}
</div>
</DialogContent>
<DialogActions>
<Button onClick={() => exitSearch()} color="primary">
<FormattedMessage {...buttonMessages.cancel} />
</Button>
<ConfirmButton
variant="contained"
color="primary"
transitionState="default"
onClick={handleSelect}
>
<FormattedMessage {...buttonMessages.select} />
</ConfirmButton>
</DialogActions>
</>
);
};
OrderCustomerAddressesSearch.displayName = "OrderCustomerAddressesSearch";
export default OrderCustomerAddressesSearch;

View file

@ -2,11 +2,11 @@ import { defineMessages } from "react-intl";
export const dialogMessages = defineMessages({
title: {
defaultMessage: "Shipping address for order",
defaultMessage: "Change address for order",
description: "dialog header"
},
billingSameAsShipping: {
defaultMessage: "Billing address same as shipping address",
defaultMessage: "Set the same for billing address",
description: "checkbox label"
},
shippingAddressDescription: {
@ -39,3 +39,21 @@ export const addressEditMessages = defineMessages({
description: "address type"
}
});
export const addressSearchMessages = defineMessages({
shippingTitle: {
defaultMessage: "Shipping address",
description: "search modal shipping title"
},
billingTitle: {
defaultMessage: "Billing address",
description: "search modal billing title"
},
searchInfo: {
defaultMessage: "Select an address you want to use from the list below",
description: "modal information under title"
},
noResultsFound: {
defaultMessage: "No results found"
}
});

View file

@ -1,14 +1,15 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles(
{
theme => ({
scrollableContent: {
maxHeight: `calc(100vh - 250px)`,
overflow: "scroll"
overflowY: "scroll",
overflowX: "hidden"
},
scrollableWrapper: {
maxHeight: 400,
overflow: "scroll"
overflowY: "scroll"
},
container: {
display: "block"
@ -18,7 +19,11 @@ export const useStyles = makeStyles(
},
overflow: {
overflowY: "visible"
},
searchInput: {
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2)
}
},
}),
{ name: "OrderCustomerAddressesEditDialog" }
);

View file

@ -0,0 +1,35 @@
import {
CustomerAddresses_user_addresses,
CustomerAddresses_user_defaultShippingAddress
} from "@saleor/customers/types/CustomerAddresses";
import { flatten } from "@saleor/misc";
import { getById } from "../OrderReturnPage/utils";
export const stringifyAddress = (
address: Partial<CustomerAddresses_user_addresses>
): string => {
const { id, ...addressWithoutId } = address;
return Object.values(flatten(addressWithoutId)).join(" ");
};
export const parseQuery = (query: string) =>
query.replace(/([.?*+\-=:^$\\[\]<>(){}|])/g, "\\$&");
export function validateDefaultAddress<
T extends CustomerAddresses_user_defaultShippingAddress
>(
defaultAddress: CustomerAddresses_user_defaultShippingAddress,
customerAddresses: T[]
): CustomerAddresses_user_defaultShippingAddress {
const fallbackAddress = {
id: customerAddresses[0]?.id
} as CustomerAddresses_user_defaultShippingAddress;
if (!defaultAddress) {
return fallbackAddress;
}
if (!customerAddresses.some(getById(defaultAddress.id))) {
return fallbackAddress;
}
return defaultAddress;
}