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": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_billingSameAsShipping": {
"context": "checkbox label", "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": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_customerAddress": {
"context": "address type", "context": "address type",
@ -4065,13 +4069,24 @@
"context": "address type", "context": "address type",
"string": "Add new address" "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": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_shippingAddressDescription": {
"context": "dialog content", "context": "dialog content",
"string": "This customer doesnt have any shipping addresses. Provide address for order:" "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": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_title": {
"context": "dialog header", "context": "dialog header",
"string": "Shipping address for order" "string": "Change address for order"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerChangeDialog_dot_changeAddress": { "src_dot_orders_dot_components_dot_OrderCustomerChangeDialog_dot_changeAddress": {
"context": "option label", "context": "option label",
@ -6748,6 +6763,9 @@
"context": "select all options, button", "context": "select all options, button",
"string": "Select All" "string": "Select All"
}, },
"src_dot_selected": {
"string": "Selected"
},
"src_dot_send": { "src_dot_send": {
"context": "button", "context": "button",
"string": "Send" "string": "Send"

6
package-lock.json generated
View file

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

View file

@ -27,7 +27,7 @@
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.58", "@material-ui/lab": "^4.0.0-alpha.58",
"@material-ui/styles": "^4.11.4", "@material-ui/styles": "^4.11.4",
"@saleor/macaw-ui": "^0.2.6", "@saleor/macaw-ui": "^0.2.7",
"@sentry/react": "^6.0.0", "@sentry/react": "^6.0.0",
"@types/faker": "^5.1.6", "@types/faker": "^5.1.6",
"@uiw/react-color-hue": "0.0.34", "@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 AddressFormatter from "@saleor/components/AddressFormatter";
import { commonMessages } from "@saleor/intl";
import { EditIcon } from "@saleor/macaw-ui";
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React from "react";
import { useIntl } from "react-intl";
import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses"; import { CustomerAddresses_user_addresses } from "../../types/CustomerAddresses";
import { useStyles } from "./styles"; import { useStyles } from "./styles";
export interface CustomerAddressChoiceCardProps { export interface CustomerAddressChoiceCardProps {
address: CustomerAddresses_user_addresses; address: CustomerAddresses_user_addresses;
selected: boolean; selected?: boolean;
onSelect: () => void; editable?: boolean;
onSelect?: () => void;
onEditClick?: () => void;
} }
const CustomerAddressChoiceCard: React.FC<CustomerAddressChoiceCardProps> = props => { const CustomerAddressChoiceCard: React.FC<CustomerAddressChoiceCardProps> = props => {
const { address, selected, onSelect } = props; const { address, selected, editable, onSelect, onEditClick } = props;
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl();
return ( return (
<Card <Card
className={classNames(classes.card, { className={classNames(classes.card, {
[classes.cardSelected]: selected [classes.cardSelected]: selected,
[classes.selectableCard]: !editable
})} })}
onClick={onSelect} onClick={onSelect}
> >
<CardContent> <CardContent className={classes.cardContent}>
<AddressFormatter address={address} /> <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> </CardContent>
</Card> </Card>
); );

View file

@ -3,13 +3,37 @@ import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles( export const useStyles = makeStyles(
theme => ({ theme => ({
card: { card: {
cursor: "pointer",
padding: "1px" padding: "1px"
}, },
cardSelected: { cardSelected: {
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
borderWidth: "2px", borderWidth: "2px",
padding: "0" 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" } { name: "CustomerAddressChoiceCard" }

View file

@ -84,6 +84,9 @@ export const commonMessages = defineMessages({
savedChanges: { savedChanges: {
defaultMessage: "Saved changes" defaultMessage: "Saved changes"
}, },
selected: {
defaultMessage: "Selected"
},
sessionExpired: { sessionExpired: {
defaultMessage: "Your session has expired. Please log in again to continue." 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}`; 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 React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { getById } from "../OrderReturnPage/utils";
import { AddressInputOptionEnum } from "./form"; import { AddressInputOptionEnum } from "./form";
import { addressEditMessages } from "./messages"; import { addressEditMessages } from "./messages";
import { useStyles } from "./styles"; import { useStyles } from "./styles";
@ -24,15 +25,13 @@ export interface OrderCustomerAddressEditProps {
addressInputOption: AddressInputOptionEnum; addressInputOption: AddressInputOptionEnum;
addressInputName: string; addressInputName: string;
onChangeAddressInputOption: FormChange; onChangeAddressInputOption: FormChange;
customerAddressId: string; selectedCustomerAddressId: string;
formAddress: AddressTypeInput; formAddress: AddressTypeInput;
formAddressCountryDisplayName: string; formAddressCountryDisplayName: string;
formErrors: Array<AccountErrorFragment | OrderErrorFragment>; formErrors: Array<AccountErrorFragment | OrderErrorFragment>;
onChangeCustomerAddress: (
customerAddress: CustomerAddresses_user_addresses
) => void;
onChangeFormAddress: (event: React.ChangeEvent<any>) => void; onChangeFormAddress: (event: React.ChangeEvent<any>) => void;
onChangeFormAddressCountry: (event: React.ChangeEvent<any>) => void; onChangeFormAddressCountry: (event: React.ChangeEvent<any>) => void;
onEdit?: () => void;
} }
const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props => { const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props => {
@ -43,13 +42,13 @@ const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props
addressInputOption, addressInputOption,
addressInputName, addressInputName,
onChangeAddressInputOption, onChangeAddressInputOption,
customerAddressId, selectedCustomerAddressId,
formAddress, formAddress,
formAddressCountryDisplayName, formAddressCountryDisplayName,
formErrors, formErrors,
onChangeCustomerAddress,
onChangeFormAddress, onChangeFormAddress,
onChangeFormAddressCountry onChangeFormAddressCountry,
onEdit
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
@ -92,19 +91,15 @@ const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props
className={classes.optionLabel} className={classes.optionLabel}
/> />
{addressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS && ( {addressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS && (
<div className={classes.scrollableWrapper}> <>
{customerAddresses.map(customerAddress => (
<React.Fragment key={customerAddress.id}>
<CardSpacer /> <CardSpacer />
<CustomerAddressChoiceCard <CustomerAddressChoiceCard
address={customerAddress} address={customerAddresses.find(getById(selectedCustomerAddressId))}
selected={customerAddress.id === customerAddressId} editable
onSelect={() => onChangeCustomerAddress(customerAddress)} onEditClick={onEdit}
/> />
</React.Fragment>
))}
<FormSpacer /> <FormSpacer />
</div> </>
)} )}
<FormControlLabel <FormControlLabel
value={AddressInputOptionEnum.NEW_ADDRESS} value={AddressInputOptionEnum.NEW_ADDRESS}

View file

@ -36,8 +36,14 @@ import OrderCustomerAddressesEditForm, {
} from "./form"; } from "./form";
import { dialogMessages } from "./messages"; import { dialogMessages } from "./messages";
import OrderCustomerAddressEdit from "./OrderCustomerAddressEdit"; import OrderCustomerAddressEdit from "./OrderCustomerAddressEdit";
import OrderCustomerAddressesSearch from "./OrderCustomerAddressesSearch";
import { useStyles } from "./styles"; import { useStyles } from "./styles";
import { validateDefaultAddress } from "./utils";
export interface OrderCustomerSearchAddressState {
open: boolean;
type: AddressTypeEnum;
}
export interface OrderCustomerAddressesEditDialogOutput { export interface OrderCustomerAddressesEditDialogOutput {
shippingAddress: AddressInput; shippingAddress: AddressInput;
billingAddress: AddressInput; billingAddress: AddressInput;
@ -56,6 +62,11 @@ export interface OrderCustomerAddressesEditDialogProps {
onConfirm(data: OrderCustomerAddressesEditDialogOutput): SubmitPromise; onConfirm(data: OrderCustomerAddressesEditDialogOutput): SubmitPromise;
} }
const defaultSearchState: OrderCustomerSearchAddressState = {
open: false,
type: undefined
};
const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialogProps> = props => { const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialogProps> = props => {
const { const {
open, open,
@ -85,9 +96,11 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
open open
); );
const getCustomerAddress = (customerAddressId: string): AddressInput => const getCustomerAddress = (
selectedCustomerAddressID: string
): AddressInput =>
transformAddressToAddressInput( transformAddressToAddressInput(
customerAddresses.find(getById(customerAddressId)) customerAddresses.find(getById(selectedCustomerAddressID))
); );
const handleAddressesSubmit = (data: OrderCustomerAddressesEditFormData) => { const handleAddressesSubmit = (data: OrderCustomerAddressesEditFormData) => {
@ -127,15 +140,60 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
const countryChoices = mapCountriesToChoices(countries); const countryChoices = mapCountriesToChoices(countries);
const [addressSearchState, setAddressSearchState] = React.useState<
OrderCustomerSearchAddressState
>(defaultSearchState);
const validatedDefaultShippingAddress = validateDefaultAddress(
defaultShippingAddress,
customerAddresses
);
const validatedDefaultBillingAddress = validateDefaultAddress(
defaultBillingAddress,
customerAddresses
);
return ( return (
<Dialog onClose={onClose} open={open}> <Dialog
onClose={() => {
setAddressSearchState(defaultSearchState);
onClose();
}}
open={open}
fullWidth
>
<OrderCustomerAddressesEditForm <OrderCustomerAddressesEditForm
countryChoices={countryChoices} countryChoices={countryChoices}
defaultShippingAddress={defaultShippingAddress} defaultShippingAddress={validatedDefaultShippingAddress}
defaultBillingAddress={defaultBillingAddress} defaultBillingAddress={validatedDefaultBillingAddress}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
{({ change, data, handlers }) => ( {({ change, data, handlers }) => (
<>
{addressSearchState.open ? (
<OrderCustomerAddressesSearch
type={addressSearchState?.type}
customerAddresses={customerAddresses}
selectedCustomerAddressId={
addressSearchState.type === AddressTypeEnum.SHIPPING
? data.customerShippingAddress?.id
: data.customerBillingAddress?.id
}
onChangeCustomerShippingAddress={customerAddress =>
handlers.changeCustomerAddress(
customerAddress,
"customerShippingAddress"
)
}
onChangeCustomerBillingAddress={customerAddress =>
handlers.changeCustomerAddress(
customerAddress,
"customerBillingAddress"
)
}
exitSearch={() => setAddressSearchState(defaultSearchState)}
/>
) : (
<> <>
<DialogTitle> <DialogTitle>
<FormattedMessage {...dialogMessages.title} /> <FormattedMessage {...dialogMessages.title} />
@ -160,22 +218,24 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
addressInputName="shippingAddressInputOption" addressInputName="shippingAddressInputOption"
onChangeAddressInputOption={change} onChangeAddressInputOption={change}
customerAddresses={customerAddresses} customerAddresses={customerAddresses}
customerAddressId={data.customerShippingAddress?.id} selectedCustomerAddressId={data.customerShippingAddress?.id}
formAddress={data.shippingAddress} formAddress={data.shippingAddress}
formAddressCountryDisplayName={data.shippingCountryDisplayName} formAddressCountryDisplayName={
data.shippingCountryDisplayName
}
formErrors={dialogErrors.filter( formErrors={dialogErrors.filter(
error => error.addressType === AddressTypeEnum.SHIPPING error => error.addressType === AddressTypeEnum.SHIPPING
)} )}
onChangeCustomerAddress={customerAddress =>
handlers.changeCustomerAddress(
customerAddress,
"customerShippingAddress"
)
}
onChangeFormAddress={event => onChangeFormAddress={event =>
handlers.changeFormAddress(event, "shippingAddress") handlers.changeFormAddress(event, "shippingAddress")
} }
onChangeFormAddressCountry={handlers.selectShippingCountry} onChangeFormAddressCountry={handlers.selectShippingCountry}
onEdit={() =>
setAddressSearchState({
open: true,
type: AddressTypeEnum.SHIPPING
})
}
/> />
<FormSpacer /> <FormSpacer />
<Divider /> <Divider />
@ -196,7 +256,9 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
data-test="billingSameAsShipping" data-test="billingSameAsShipping"
/> />
} }
label={intl.formatMessage(dialogMessages.billingSameAsShipping)} label={intl.formatMessage(
dialogMessages.billingSameAsShipping
)}
/> />
{!data.billingSameAsShipping && ( {!data.billingSameAsShipping && (
<> <>
@ -220,7 +282,9 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
addressInputName="billingAddressInputOption" addressInputName="billingAddressInputOption"
onChangeAddressInputOption={change} onChangeAddressInputOption={change}
customerAddresses={customerAddresses} customerAddresses={customerAddresses}
customerAddressId={data.customerBillingAddress?.id} selectedCustomerAddressId={
data.customerBillingAddress?.id
}
formAddress={data.billingAddress} formAddress={data.billingAddress}
formAddressCountryDisplayName={ formAddressCountryDisplayName={
data.billingCountryDisplayName data.billingCountryDisplayName
@ -228,16 +292,18 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
formErrors={dialogErrors.filter( formErrors={dialogErrors.filter(
error => error.addressType === AddressTypeEnum.BILLING error => error.addressType === AddressTypeEnum.BILLING
)} )}
onChangeCustomerAddress={customerAddress =>
handlers.changeCustomerAddress(
customerAddress,
"customerBillingAddress"
)
}
onChangeFormAddress={event => onChangeFormAddress={event =>
handlers.changeFormAddress(event, "billingAddress") handlers.changeFormAddress(event, "billingAddress")
} }
onChangeFormAddressCountry={handlers.selectBillingCountry} onChangeFormAddressCountry={
handlers.selectBillingCountry
}
onEdit={() =>
setAddressSearchState({
open: true,
type: AddressTypeEnum.BILLING
})
}
/> />
</> </>
)} )}
@ -250,11 +316,13 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
type="submit" type="submit"
data-test="submit" data-test="submit"
> >
<FormattedMessage {...buttonMessages.select} /> <FormattedMessage {...buttonMessages.save} />
</ConfirmButton> </ConfirmButton>
</DialogActions> </DialogActions>
</> </>
)} )}
</>
)}
</OrderCustomerAddressesEditForm> </OrderCustomerAddressesEditForm>
</Dialog> </Dialog>
); );

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({ export const dialogMessages = defineMessages({
title: { title: {
defaultMessage: "Shipping address for order", defaultMessage: "Change address for order",
description: "dialog header" description: "dialog header"
}, },
billingSameAsShipping: { billingSameAsShipping: {
defaultMessage: "Billing address same as shipping address", defaultMessage: "Set the same for billing address",
description: "checkbox label" description: "checkbox label"
}, },
shippingAddressDescription: { shippingAddressDescription: {
@ -39,3 +39,21 @@ export const addressEditMessages = defineMessages({
description: "address type" 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"; import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles( export const useStyles = makeStyles(
{ theme => ({
scrollableContent: { scrollableContent: {
maxHeight: `calc(100vh - 250px)`, maxHeight: `calc(100vh - 250px)`,
overflow: "scroll" overflowY: "scroll",
overflowX: "hidden"
}, },
scrollableWrapper: { scrollableWrapper: {
maxHeight: 400, maxHeight: 400,
overflow: "scroll" overflowY: "scroll"
}, },
container: { container: {
display: "block" display: "block"
@ -18,7 +19,11 @@ export const useStyles = makeStyles(
}, },
overflow: { overflow: {
overflowY: "visible" overflowY: "visible"
}
}, },
searchInput: {
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2)
}
}),
{ name: "OrderCustomerAddressesEditDialog" } { 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;
}