Refactor changing address in order details directly (#1697) (#1818)

* Refactor changing address in order details directly (#1697)

* wip change address edit buttons behaviour in order draft

* wip modify dialog view when customer is not changed

* wip remove old address edit modal

* wip rm old address modal storybook

* wip async search state change

* wip disable edit in draft when no customer is selected

* wip fix submitting issues

* wip allow single address mutation

* wip fix billing change & manual address change

* wip fix covered country dropdown

* wip add address clone checkbox

* wip normal order details & unconfirmed views

* wip messages minor fixes

* wip clone address checkbox on new address input option

* Storybook update

* cleanup

* billing same as shipping fixes

* improve stories titles & move to component folder

* improve continueToAddressSearchState readibility

* improve dialog title/description message descriptors

* change .then() to await

* Remove redundant onClick arrow function

Co-authored-by: Dominik Żegleń <flesz3@o2.pl>

* Improve OrderAddressFields component

* move shipping & billing address edit props outside file

* Update snapshots

* fix mutation output types in order views

* Fix confirm button state type

* fix skipping query when modal is refreshed

* fix cancel button to match designs

* update dialog headers

* fix customer address choice card styling for blue theme

* change hidecard to showcard

* export types to local file

* improve isAnyAddressEditModalOpen function

* fix cut hover effect on inputs

* fix submitting modal when modal is closed with x button

* fix validation & initial form data

* update messages

* Update partial mutation return type

* Add vertical spacer component

* Revert CardSpacer changes

* Change some of Form & Card spacers to Vertical Spacers

* Fix makeStyles import in VerticalSpacer

* Fix border color for AddressCards

* Add type to addressFieldsCommonProps object

* Change stories subtitles to lowercase

* Fix country choices type

* Fix setState react type

* Improve address change handlers

* Update snapshots

* Fix default country not showing up in address edit single autocomplete field

Co-authored-by: Dominik Żegleń <flesz3@o2.pl>
Co-authored-by: Magdalena Markusik <magdalena.markusik@mirumee.com>

* Fix linter issue

Co-authored-by: Dominik Żegleń <flesz3@o2.pl>
Co-authored-by: Magdalena Markusik <magdalena.markusik@mirumee.com>
This commit is contained in:
Michał Droń 2022-02-02 12:22:39 +01:00 committed by GitHub
parent 519b816afa
commit 394c33c0b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 865 additions and 770 deletions

View file

@ -4238,14 +4238,6 @@
"context": "section header", "context": "section header",
"string": "Sales channel" "string": "Sales channel"
}, },
"src_dot_orders_dot_components_dot_OrderAddressEditDialog_dot_3278396777": {
"context": "dialog header",
"string": "Edit Shipping Address"
},
"src_dot_orders_dot_components_dot_OrderAddressEditDialog_dot_3982060155": {
"context": "dialog header",
"string": "Edit Billing Address"
},
"src_dot_orders_dot_components_dot_OrderBulkCancelDialog_dot_1528036340": { "src_dot_orders_dot_components_dot_OrderBulkCancelDialog_dot_1528036340": {
"context": "dialog header", "context": "dialog header",
"string": "Cancel Orders" "string": "Cancel Orders"
@ -4267,9 +4259,13 @@
"context": "section header", "context": "section header",
"string": "Sales channel" "string": "Sales channel"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_billingAddressDescription": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_addressChangeDescription": {
"context": "dialog content", "context": "dialog content",
"string": "Add a new address:" "string": "Select method you want to use to change address"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_billingChangeTitle": {
"context": "dialog header",
"string": "Change customer billing address"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_billingSameAsShipping": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_billingSameAsShipping": {
"context": "checkbox label", "context": "checkbox label",
@ -4283,37 +4279,50 @@
"context": "address type", "context": "address type",
"string": "Use one of customer addresses" "string": "Use one of customer addresses"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_customerBillingAddressDescription": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_customerChangeBillingDescription": {
"context": "dialog content", "context": "dialog content",
"string": "Select one of customer addresses or add a new address:" "string": "Select one of customer addresses or add a new address:"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_customerShippingAddressDescription": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_customerChangeDescription": {
"context": "dialog content", "context": "dialog content",
"string": "Which address would you like to use as shipping address for selected customer:" "string": "Which address would you like to use as shipping address for selected customer:"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_customerChangeTitle": {
"context": "dialog header",
"string": "Change address for order"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_newAddress": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_newAddress": {
"context": "address type", "context": "address type",
"string": "Add new address" "string": "Add new address"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_noAddressBillingDescription": {
"context": "dialog content",
"string": "Add a new address:"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_noAddressDescription": {
"context": "dialog content",
"string": "This customer doesn't have any addresses in the address book. Provide address for order:"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_noResultsFound": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_noResultsFound": {
"context": "info when addresses search is unsuccessful",
"string": "No results found" "string": "No results found"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_searchInfo": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_searchInfo": {
"context": "modal information under title", "context": "modal information under title",
"string": "Select an address you want to use from the list below" "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_shippingChangeTitle": {
"context": "dialog content", "context": "dialog header",
"string": "This customer doesnt have any shipping addresses. Provide address for order:" "string": "Change customer shipping address"
},
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_shippingSameAsBilling": {
"context": "checkbox label",
"string": "Set the same for shipping address"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_shippingTitle": { "src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_shippingTitle": {
"context": "search modal shipping title", "context": "search modal shipping title",
"string": "Shipping address" "string": "Shipping address"
}, },
"src_dot_orders_dot_components_dot_OrderCustomerAddressesEditDialog_dot_title": {
"context": "dialog header",
"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",
"string": "Change address" "string": "Change address"

View file

@ -27,7 +27,7 @@ const CustomerAddressChoiceCard: React.FC<CustomerAddressChoiceCardProps> = prop
<Card <Card
className={classNames(classes.card, { className={classNames(classes.card, {
[classes.cardSelected]: selected, [classes.cardSelected]: selected,
[classes.selectableCard]: !editable [classes.selectableCard]: !editable && !selected
})} })}
onClick={onSelect} onClick={onSelect}
> >

View file

@ -3,12 +3,14 @@ import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles( export const useStyles = makeStyles(
theme => ({ theme => ({
card: { card: {
padding: "1px" padding: "1px",
borderColor: theme.palette.saleor.main[5],
borderStyle: "solid",
borderWidth: "2px"
}, },
cardSelected: { cardSelected: {
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
borderWidth: "2px", cursor: "pointer"
padding: "0"
}, },
cardContent: { cardContent: {
display: "flex", display: "flex",
@ -19,7 +21,7 @@ export const useStyles = makeStyles(
selectableCard: { selectableCard: {
"&:hover": { "&:hover": {
cursor: "pointer", cursor: "pointer",
borderColor: theme.palette.primary.main borderColor: theme.palette.saleor.active[3]
} }
}, },
selectedLabel: { selectedLabel: {

View file

@ -1,125 +0,0 @@
import {
Dialog,
DialogActions,
DialogContent,
DialogTitle
} from "@material-ui/core";
import AddressEdit from "@saleor/components/AddressEdit";
import BackButton from "@saleor/components/BackButton";
import ConfirmButton from "@saleor/components/ConfirmButton";
import Form from "@saleor/components/Form";
import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo";
import { AddressTypeInput } from "@saleor/customers/types";
import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment";
import useAddressValidation from "@saleor/hooks/useAddressValidation";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import useStateFromProps from "@saleor/hooks/useStateFromProps";
import { buttonMessages } from "@saleor/intl";
import { ConfirmButtonTransitionState, makeStyles } from "@saleor/macaw-ui";
import { AddressInput } from "@saleor/types/globalTypes";
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
import { mapCountriesToChoices } from "@saleor/utils/maps";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
const useStyles = makeStyles(
{
overflow: {
overflowY: "visible"
}
},
{ name: "OrderAddressEditDialog" }
);
interface OrderAddressEditDialogProps {
confirmButtonState: ConfirmButtonTransitionState;
address: AddressTypeInput;
open: boolean;
errors: OrderErrorFragment[];
variant: "billing" | "shipping" | string;
countries?: ShopInfo_shop_countries[];
onClose();
onConfirm(data: AddressInput);
}
const OrderAddressEditDialog: React.FC<OrderAddressEditDialogProps> = props => {
const {
address,
confirmButtonState,
open,
errors = [],
variant,
countries = [],
onClose,
onConfirm
} = props;
const classes = useStyles(props);
const intl = useIntl();
const [countryDisplayName, setCountryDisplayName] = useStateFromProps(
countries.find(country => address?.country === country.code)?.country
);
const {
errors: validationErrors,
submit: handleSubmit
} = useAddressValidation(onConfirm);
const dialogErrors = useModalDialogErrors(
[...errors, ...validationErrors],
open
);
const countryChoices = mapCountriesToChoices(countries);
return (
<Dialog onClose={onClose} open={open} classes={{ paper: classes.overflow }}>
<Form initial={address} onSubmit={handleSubmit}>
{({ change, data }) => {
const handleCountrySelect = createSingleAutocompleteSelectHandler(
change,
setCountryDisplayName,
countryChoices
);
return (
<>
<DialogTitle>
{variant === "billing"
? intl.formatMessage({
defaultMessage: "Edit Billing Address",
description: "dialog header"
})
: intl.formatMessage({
defaultMessage: "Edit Shipping Address",
description: "dialog header"
})}
</DialogTitle>
<DialogContent className={classes.overflow}>
<AddressEdit
countries={countryChoices}
countryDisplayValue={countryDisplayName}
data={data}
errors={dialogErrors}
onChange={change}
onCountryChange={handleCountrySelect}
/>
</DialogContent>
<DialogActions>
<BackButton onClick={onClose} />
<ConfirmButton
data-test-id="order-address-edit-dialog-confirm-button"
transitionState={confirmButtonState}
type="submit"
>
<FormattedMessage {...buttonMessages.confirm} />
</ConfirmButton>
</DialogActions>
</>
);
}}
</Form>
</Dialog>
);
};
OrderAddressEditDialog.displayName = "OrderAddressEditDialog";
export default OrderAddressEditDialog;

View file

@ -1,2 +0,0 @@
export { default } from "./OrderAddressEditDialog";
export * from "./OrderAddressEditDialog";

View file

@ -0,0 +1,89 @@
import { CustomerAddresses_user } from "@saleor/customers/types/CustomerAddresses";
import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment";
import { SubmitPromise } from "@saleor/hooks/useForm";
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
import { transformAddressToForm } from "@saleor/misc";
import {
OrderDetails_order_billingAddress,
OrderDetails_order_shippingAddress,
OrderDetails_shop_countries
} from "@saleor/orders/types/OrderDetails";
import React from "react";
import OrderCustomerAddressesEditDialog, {
OrderCustomerAddressesEditDialogProps
} from "../OrderCustomerAddressesEditDialog";
import {
AddressEditDialogVariant,
OrderCustomerAddressesEditDialogOutput
} from "../OrderCustomerAddressesEditDialog/types";
interface OrderAddressFieldsProps {
action: string;
isDraft: boolean;
customerAddressesLoading: boolean;
customer: CustomerAddresses_user;
countries: OrderDetails_shop_countries[];
onClose: () => void;
onConfirm: (data: OrderCustomerAddressesEditDialogOutput) => SubmitPromise;
confirmButtonState: ConfirmButtonTransitionState;
errors: OrderErrorFragment[];
orderShippingAddress: OrderDetails_order_shippingAddress;
orderBillingAddress: OrderDetails_order_billingAddress;
}
const OrderAddressFields: React.FC<OrderAddressFieldsProps> = ({
action,
isDraft,
customerAddressesLoading,
customer,
countries,
onClose,
onConfirm,
confirmButtonState,
errors,
orderShippingAddress,
orderBillingAddress
}) => {
const addressFieldCommonProps: Omit<
OrderCustomerAddressesEditDialogProps,
"open" | "variant"
> = {
loading: customerAddressesLoading,
confirmButtonState,
countries,
errors,
orderShippingAddress: transformAddressToForm(orderShippingAddress),
orderBillingAddress: transformAddressToForm(orderBillingAddress),
customerAddresses: customer?.addresses,
defaultShippingAddress: customer?.defaultShippingAddress,
defaultBillingAddress: customer?.defaultBillingAddress,
onClose,
onConfirm
};
return (
<>
{isDraft && (
<OrderCustomerAddressesEditDialog
open={action === "edit-customer-addresses"}
variant={AddressEditDialogVariant.CHANGE_CUSTOMER}
{...addressFieldCommonProps}
/>
)}
<OrderCustomerAddressesEditDialog
open={action === "edit-shipping-address"}
variant={AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS}
{...addressFieldCommonProps}
/>
<OrderCustomerAddressesEditDialog
open={action === "edit-billing-address"}
variant={AddressEditDialogVariant.CHANGE_BILLING_ADDRESS}
{...addressFieldCommonProps}
/>
</>
);
};
export default OrderAddressFields;

View file

@ -0,0 +1,2 @@
export { default } from "./OrderAddressFields";
export * from "./OrderAddressFields";

View file

@ -32,6 +32,7 @@ export interface OrderCustomerAddressEditProps {
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; onEdit?: () => void;
showCard?: boolean;
} }
const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props => { const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props => {
@ -48,7 +49,8 @@ const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props
formErrors, formErrors,
onChangeFormAddress, onChangeFormAddress,
onChangeFormAddressCountry, onChangeFormAddressCountry,
onEdit onEdit,
showCard = true
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
@ -90,11 +92,14 @@ const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props
label={intl.formatMessage(addressEditMessages.customerAddress)} label={intl.formatMessage(addressEditMessages.customerAddress)}
className={classes.optionLabel} className={classes.optionLabel}
/> />
{addressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS && ( {addressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS &&
showCard && (
<> <>
<CardSpacer /> <CardSpacer />
<CustomerAddressChoiceCard <CustomerAddressChoiceCard
address={customerAddresses.find(getById(selectedCustomerAddressId))} address={customerAddresses.find(
getById(selectedCustomerAddressId)
)}
editable editable
onEditClick={onEdit} onEditClick={onEdit}
/> />
@ -114,8 +119,6 @@ const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props
className={classes.optionLabel} className={classes.optionLabel}
/> />
{addressInputOption === AddressInputOptionEnum.NEW_ADDRESS && ( {addressInputOption === AddressInputOptionEnum.NEW_ADDRESS && (
<>
<FormSpacer />
<AddressEdit <AddressEdit
countries={countryChoices} countries={countryChoices}
countryDisplayValue={formAddressCountryDisplayName} countryDisplayValue={formAddressCountryDisplayName}
@ -124,7 +127,6 @@ const OrderCustomerAddressEdit: React.FC<OrderCustomerAddressEditProps> = props
onChange={onChangeFormAddress} onChange={onChangeFormAddress}
onCountryChange={onChangeFormAddressCountry} onCountryChange={onChangeFormAddressCountry}
/> />
</>
)} )}
</RadioGroup> </RadioGroup>
); );

View file

@ -5,25 +5,47 @@ import React from "react";
import { countries, order as orderFixture } from "../../fixtures"; import { countries, order as orderFixture } from "../../fixtures";
import OrderCustomerAddressesEditDialog, { import OrderCustomerAddressesEditDialog, {
OrderCustomerAddressesEditDialogProps OrderCustomerAddressesEditDialogProps
} from "./OrderCustomerAddressesEditDialog"; } from ".";
import { AddressEditDialogVariant } from "./types";
const order = orderFixture(""); const order = orderFixture("");
const props: OrderCustomerAddressesEditDialogProps = { const props: OrderCustomerAddressesEditDialogProps = {
confirmButtonState: "default", confirmButtonState: "default",
variant: AddressEditDialogVariant.CHANGE_CUSTOMER,
loading: false, loading: false,
onClose: () => undefined, onClose: () => undefined,
onConfirm: () => undefined, onConfirm: () => undefined,
open: true, open: true,
errors: undefined errors: undefined,
countries
}; };
storiesOf("Orders / OrderCustomerAddressesEditDialog", module) storiesOf("Orders / Changing address in order", module)
.addDecorator(Decorator) .addDecorator(Decorator)
.add("default", () => ( .add("address change when customer is changed", () => (
<OrderCustomerAddressesEditDialog <OrderCustomerAddressesEditDialog
{...props} {...props}
countries={countries} customerAddresses={[
order.shippingAddress,
{ ...order.billingAddress, id: "asdfghjfuunie" }
]}
/>
))
.add("shipping address change", () => (
<OrderCustomerAddressesEditDialog
{...props}
variant={AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS}
customerAddresses={[
order.shippingAddress,
{ ...order.billingAddress, id: "asdfghjfuunie" }
]}
/>
))
.add("billing address change", () => (
<OrderCustomerAddressesEditDialog
{...props}
variant={AddressEditDialogVariant.CHANGE_BILLING_ADDRESS}
customerAddresses={[ customerAddresses={[
order.shippingAddress, order.shippingAddress,
{ ...order.billingAddress, id: "asdfghjfuunie" } { ...order.billingAddress, id: "asdfghjfuunie" }
@ -31,11 +53,7 @@ storiesOf("Orders / OrderCustomerAddressesEditDialog", module)
/> />
)) ))
.add("no customer addresses", () => ( .add("no customer addresses", () => (
<OrderCustomerAddressesEditDialog <OrderCustomerAddressesEditDialog {...props} customerAddresses={[]} />
{...props}
countries={countries}
customerAddresses={[]}
/>
)) ))
.add("loading", () => ( .add("loading", () => (
<OrderCustomerAddressesEditDialog <OrderCustomerAddressesEditDialog

View file

@ -2,15 +2,16 @@ import {
Dialog, Dialog,
DialogActions, DialogActions,
DialogContent, DialogContent,
DialogTitle,
Divider, Divider,
FormControlLabel, FormControlLabel,
Typography Typography
} from "@material-ui/core"; } from "@material-ui/core";
import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import Checkbox from "@saleor/components/Checkbox"; import Checkbox from "@saleor/components/Checkbox";
import ConfirmButton from "@saleor/components/ConfirmButton"; import ConfirmButton from "@saleor/components/ConfirmButton";
import FormSpacer from "@saleor/components/FormSpacer"; import FormSpacer from "@saleor/components/FormSpacer";
import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo"; import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo";
import { AddressTypeInput } from "@saleor/customers/types";
import { import {
CustomerAddresses_user_addresses, CustomerAddresses_user_addresses,
CustomerAddresses_user_defaultBillingAddress, CustomerAddresses_user_defaultBillingAddress,
@ -21,11 +22,12 @@ import useAddressValidation from "@saleor/hooks/useAddressValidation";
import { SubmitPromise } from "@saleor/hooks/useForm"; import { SubmitPromise } from "@saleor/hooks/useForm";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui"; import { ConfirmButtonTransitionState, DialogHeader } from "@saleor/macaw-ui";
import { transformAddressToAddressInput } from "@saleor/misc"; import { transformAddressToAddressInput } from "@saleor/misc";
import { AddressInput, AddressTypeEnum } from "@saleor/types/globalTypes"; import { AddressInput, AddressTypeEnum } from "@saleor/types/globalTypes";
import { mapCountriesToChoices } from "@saleor/utils/maps"; import { mapCountriesToChoices } from "@saleor/utils/maps";
import React from "react"; import React from "react";
import { MessageDescriptor } from "react-intl";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { getById } from "../OrderReturnPage/utils"; import { getById } from "../OrderReturnPage/utils";
@ -37,28 +39,29 @@ import { dialogMessages } from "./messages";
import OrderCustomerAddressEdit from "./OrderCustomerAddressEdit"; import OrderCustomerAddressEdit from "./OrderCustomerAddressEdit";
import OrderCustomerAddressesSearch from "./OrderCustomerAddressesSearch"; import OrderCustomerAddressesSearch from "./OrderCustomerAddressesSearch";
import { useStyles } from "./styles"; import { useStyles } from "./styles";
import { validateDefaultAddress } from "./utils"; import {
AddressEditDialogVariant,
export interface OrderCustomerSearchAddressState { OrderCustomerAddressesEditDialogOutput,
open: boolean; OrderCustomerSearchAddressState
type: AddressTypeEnum; } from "./types";
} import { getAddressEditProps, validateDefaultAddress } from "./utils";
export interface OrderCustomerAddressesEditDialogOutput {
shippingAddress: AddressInput;
billingAddress: AddressInput;
}
export interface OrderCustomerAddressesEditDialogProps { export interface OrderCustomerAddressesEditDialogProps {
open: boolean; open: boolean;
variant: AddressEditDialogVariant;
loading: boolean; loading: boolean;
confirmButtonState: ConfirmButtonTransitionState; confirmButtonState: ConfirmButtonTransitionState;
errors: OrderErrorFragment[]; errors: OrderErrorFragment[];
orderShippingAddress?: AddressTypeInput;
orderBillingAddress?: AddressTypeInput;
countries?: ShopInfo_shop_countries[]; countries?: ShopInfo_shop_countries[];
customerAddresses?: CustomerAddresses_user_addresses[]; customerAddresses?: CustomerAddresses_user_addresses[];
defaultShippingAddress?: CustomerAddresses_user_defaultShippingAddress; defaultShippingAddress?: CustomerAddresses_user_defaultShippingAddress;
defaultBillingAddress?: CustomerAddresses_user_defaultBillingAddress; defaultBillingAddress?: CustomerAddresses_user_defaultBillingAddress;
onClose(); onClose();
onConfirm(data: OrderCustomerAddressesEditDialogOutput): SubmitPromise<any[]>; onConfirm(
data: Partial<OrderCustomerAddressesEditDialogOutput>
): SubmitPromise<any[]>;
} }
const defaultSearchState: OrderCustomerSearchAddressState = { const defaultSearchState: OrderCustomerSearchAddressState = {
@ -69,6 +72,7 @@ const defaultSearchState: OrderCustomerSearchAddressState = {
const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialogProps> = props => { const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialogProps> = props => {
const { const {
open, open,
variant,
loading, loading,
confirmButtonState, confirmButtonState,
errors = [], errors = [],
@ -77,11 +81,17 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
defaultShippingAddress, defaultShippingAddress,
defaultBillingAddress, defaultBillingAddress,
onClose, onClose,
onConfirm onConfirm,
orderShippingAddress,
orderBillingAddress
} = props; } = props;
const classes = useStyles(props); const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const hasCustomerChanged =
variant === AddressEditDialogVariant.CHANGE_CUSTOMER;
const { const {
errors: shippingValidationErrors, errors: shippingValidationErrors,
submit: handleShippingSubmit submit: handleShippingSubmit
@ -90,11 +100,32 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
errors: billingValidationErrors, errors: billingValidationErrors,
submit: handleBillingSubmit submit: handleBillingSubmit
} = useAddressValidation(address => address, AddressTypeEnum.BILLING); } = useAddressValidation(address => address, AddressTypeEnum.BILLING);
const dialogErrors = useModalDialogErrors( const dialogErrors = useModalDialogErrors(
[...errors, ...shippingValidationErrors, ...billingValidationErrors], [...errors, ...shippingValidationErrors, ...billingValidationErrors],
open open
); );
const continueToSearchAddressesState = (
data: OrderCustomerAddressesEditFormData
): boolean => {
if (hasCustomerChanged || addressSearchState.open) {
return false;
}
if (!customerAddresses.length) {
return false;
}
if (variant === AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS) {
return (
data.shippingAddressInputOption ===
AddressInputOptionEnum.CUSTOMER_ADDRESS
);
}
return (
data.billingAddressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS
);
};
const getCustomerAddress = ( const getCustomerAddress = (
selectedCustomerAddressID: string selectedCustomerAddressID: string
): AddressInput => ): AddressInput =>
@ -110,30 +141,75 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
? getCustomerAddress(data.customerShippingAddress.id) ? getCustomerAddress(data.customerShippingAddress.id)
: handleShippingSubmit(data.shippingAddress); : handleShippingSubmit(data.shippingAddress);
if (data.billingSameAsShipping) {
return {
shippingAddress,
billingAddress: shippingAddress
};
}
const billingAddress = const billingAddress =
customerAddresses.length > 0 && customerAddresses.length > 0 &&
data.billingAddressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS data.billingAddressInputOption === AddressInputOptionEnum.CUSTOMER_ADDRESS
? getCustomerAddress(data.customerBillingAddress.id) ? getCustomerAddress(data.customerBillingAddress.id)
: handleBillingSubmit(data.billingAddress); : handleBillingSubmit(data.billingAddress);
if (variant === AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS) {
return { return {
shippingAddress, shippingAddress,
...(data.cloneAddress && { billingAddress: shippingAddress })
};
}
if (variant === AddressEditDialogVariant.CHANGE_BILLING_ADDRESS) {
return {
...(data.cloneAddress && { shippingAddress: billingAddress }),
billingAddress billingAddress
}; };
}
return {
shippingAddress,
billingAddress: data.cloneAddress ? shippingAddress : billingAddress
};
}; };
const handleSubmit = (data: OrderCustomerAddressesEditFormData) => { const getDialogTitle = (): MessageDescriptor => {
const adressesInput = handleAddressesSubmit(data); if (addressSearchState.open) {
if (variant === AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS) {
return dialogMessages.shippingTitle;
}
if (variant === AddressEditDialogVariant.CHANGE_BILLING_ADDRESS) {
return dialogMessages.billingTitle;
}
}
if (variant === AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS) {
return dialogMessages.shippingChangeTitle;
}
if (variant === AddressEditDialogVariant.CHANGE_BILLING_ADDRESS) {
return dialogMessages.billingChangeTitle;
}
return dialogMessages.customerChangeTitle;
};
const getDialogDescription = (): MessageDescriptor => {
if (customerAddresses.length === 0) {
return dialogMessages.noAddressDescription;
}
if (variant === AddressEditDialogVariant.CHANGE_CUSTOMER) {
return dialogMessages.customerChangeDescription;
}
return dialogMessages.addressChangeDescription;
};
if (adressesInput.shippingAddress && adressesInput.billingAddress) { const handleContinue = (data: OrderCustomerAddressesEditFormData) => {
onConfirm(adressesInput as OrderCustomerAddressesEditDialogOutput); if (continueToSearchAddressesState(data)) {
setAddressSearchState({
open: true,
type:
variant === AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS
? AddressTypeEnum.SHIPPING
: AddressTypeEnum.BILLING
});
return;
}
handleSubmit(data);
};
const handleSubmit = async (data: OrderCustomerAddressesEditFormData) => {
const addressesInput = handleAddressesSubmit(data);
if (addressesInput) {
await onConfirm(addressesInput as OrderCustomerAddressesEditDialogOutput);
setAddressSearchState(defaultSearchState);
} }
return Promise.resolve([ return Promise.resolve([
@ -157,26 +233,63 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
customerAddresses customerAddresses
); );
return ( const addressEditCommonProps = {
<Dialog showCard: hasCustomerChanged,
onClose={() => { loading,
setAddressSearchState(defaultSearchState); countryChoices,
customerAddresses
};
const exitModal = () => {
onClose(); onClose();
}} setAddressSearchState(defaultSearchState);
open={open} };
fullWidth
> return (
<Dialog onClose={exitModal} open={open} fullWidth>
<DialogHeader onClose={exitModal}>
<FormattedMessage {...getDialogTitle()} />
</DialogHeader>
<OrderCustomerAddressesEditForm <OrderCustomerAddressesEditForm
countryChoices={countryChoices} countryChoices={countryChoices}
countries={countries}
defaultShippingAddress={validatedDefaultShippingAddress} defaultShippingAddress={validatedDefaultShippingAddress}
defaultBillingAddress={validatedDefaultBillingAddress} defaultBillingAddress={validatedDefaultBillingAddress}
onSubmit={handleSubmit} defaultCloneAddress={hasCustomerChanged}
initial={{
shippingAddress: orderShippingAddress,
billingAddress: orderBillingAddress
}}
onSubmit={handleContinue}
> >
{({ change, data, handlers }) => ( {({ change, data, handlers }) => {
const shippingAddressEditProps = getAddressEditProps(
"shipping",
data,
handlers,
change,
dialogErrors,
setAddressSearchState,
addressEditCommonProps
);
const billingAddressEditProps = getAddressEditProps(
"billing",
data,
handlers,
change,
dialogErrors,
setAddressSearchState,
addressEditCommonProps
);
return (
<> <>
{addressSearchState.open ? ( {addressSearchState.open ? (
<OrderCustomerAddressesSearch <OrderCustomerAddressesSearch
openFromCustomerChange={hasCustomerChanged}
type={addressSearchState?.type} type={addressSearchState?.type}
cloneAddress={data.cloneAddress}
formChange={change}
transitionState={confirmButtonState}
customerAddresses={customerAddresses} customerAddresses={customerAddresses}
selectedCustomerAddressId={ selectedCustomerAddressId={
addressSearchState.type === AddressTypeEnum.SHIPPING addressSearchState.type === AddressTypeEnum.SHIPPING
@ -199,47 +312,15 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
/> />
) : ( ) : (
<> <>
<DialogTitle> <DialogContent className={classes.dialogContent}>
<FormattedMessage {...dialogMessages.title} />
</DialogTitle>
<DialogContent className={classes.scrollableContent}>
<Typography> <Typography>
{customerAddresses.length > 0 ? ( <FormattedMessage {...getDialogDescription()} />
<FormattedMessage
{...dialogMessages.customerShippingAddressDescription}
/>
) : (
<FormattedMessage
{...dialogMessages.shippingAddressDescription}
/>
)}
</Typography> </Typography>
<FormSpacer /> <VerticalSpacer />
{hasCustomerChanged && (
<>
<OrderCustomerAddressEdit <OrderCustomerAddressEdit
loading={loading} {...shippingAddressEditProps}
countryChoices={countryChoices}
addressInputOption={data.shippingAddressInputOption}
addressInputName="shippingAddressInputOption"
onChangeAddressInputOption={change}
customerAddresses={customerAddresses}
selectedCustomerAddressId={data.customerShippingAddress?.id}
formAddress={data.shippingAddress}
formAddressCountryDisplayName={
data.shippingCountryDisplayName
}
formErrors={dialogErrors.filter(
error => error.addressType === AddressTypeEnum.SHIPPING
)}
onChangeFormAddress={event =>
handlers.changeFormAddress(event, "shippingAddress")
}
onChangeFormAddressCountry={handlers.selectShippingCountry}
onEdit={() =>
setAddressSearchState({
open: true,
type: AddressTypeEnum.SHIPPING
})
}
/> />
<FormSpacer /> <FormSpacer />
<Divider /> <Divider />
@ -247,13 +328,13 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
<FormControlLabel <FormControlLabel
control={ control={
<Checkbox <Checkbox
checked={data.billingSameAsShipping} checked={data.cloneAddress}
name="billingSameAsShipping" name="billingSameAsShipping"
onChange={() => onChange={() =>
change({ change({
target: { target: {
name: "billingSameAsShipping", name: "cloneAddress",
value: !data.billingSameAsShipping value: !data.cloneAddress
} }
}) })
} }
@ -264,51 +345,96 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
dialogMessages.billingSameAsShipping dialogMessages.billingSameAsShipping
)} )}
/> />
{!data.billingSameAsShipping && ( {!data.cloneAddress && (
<> <>
<FormSpacer /> <FormSpacer />
<Typography> <Typography>
{customerAddresses.length > 0 ? ( {customerAddresses.length > 0 ? (
<FormattedMessage <FormattedMessage
{...dialogMessages.customerBillingAddressDescription} {...dialogMessages.customerChangeBillingDescription}
/> />
) : ( ) : (
<FormattedMessage <FormattedMessage
{...dialogMessages.billingAddressDescription} {...dialogMessages.noAddressBillingDescription}
/> />
)} )}
</Typography> </Typography>
<FormSpacer /> <FormSpacer />
<OrderCustomerAddressEdit <OrderCustomerAddressEdit
loading={loading} {...billingAddressEditProps}
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") )}
{variant ===
AddressEditDialogVariant.CHANGE_SHIPPING_ADDRESS && (
<>
<OrderCustomerAddressEdit
{...shippingAddressEditProps}
/>
{data.shippingAddressInputOption ===
AddressInputOptionEnum.NEW_ADDRESS && (
<>
<FormSpacer />
<Divider />
<FormControlLabel
control={
<Checkbox
checked={data.cloneAddress}
name="billingSameAsShipping"
onChange={() =>
change({
target: {
name: "cloneAddress",
value: !data.cloneAddress
} }
onChangeFormAddressCountry={
handlers.selectBillingCountry
}
onEdit={() =>
setAddressSearchState({
open: true,
type: AddressTypeEnum.BILLING
}) })
} }
data-test="billingSameAsShipping"
/> />
}
label={intl.formatMessage(
dialogMessages.billingSameAsShipping
)}
/>
</>
)}
</>
)}
{variant ===
AddressEditDialogVariant.CHANGE_BILLING_ADDRESS && (
<>
<OrderCustomerAddressEdit
{...billingAddressEditProps}
/>
{data.shippingAddressInputOption ===
AddressInputOptionEnum.NEW_ADDRESS && (
<>
<FormSpacer />
<Divider />
<FormControlLabel
control={
<Checkbox
checked={data.cloneAddress}
name="shippingSameAsBilling"
onChange={() =>
change({
target: {
name: "cloneAddress",
value: !data.cloneAddress
}
})
}
data-test="billingSameAsShipping"
/>
}
label={intl.formatMessage(
dialogMessages.shippingSameAsBilling
)}
/>
</>
)}
</> </>
)} )}
</DialogContent> </DialogContent>
@ -319,13 +445,18 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
type="submit" type="submit"
data-test="submit" data-test="submit"
> >
<FormattedMessage {...buttonMessages.save} /> <FormattedMessage
{...(continueToSearchAddressesState(data)
? buttonMessages.continue
: buttonMessages.save)}
/>
</ConfirmButton> </ConfirmButton>
</DialogActions> </DialogActions>
</> </>
)} )}
</> </>
)} );
}}
</OrderCustomerAddressesEditForm> </OrderCustomerAddressesEditForm>
</Dialog> </Dialog>
); );

View file

@ -1,29 +1,38 @@
import { import {
Button, Checkbox,
DialogActions, DialogActions,
DialogContent, DialogContent,
DialogTitle, FormControlLabel,
InputAdornment, InputAdornment,
TextField TextField
} from "@material-ui/core"; } from "@material-ui/core";
import CardSpacer from "@saleor/components/CardSpacer"; import VerticalSpacer from "@saleor/apps/components/VerticalSpacer";
import { ConfirmButton } from "@saleor/components/ConfirmButton"; import { ConfirmButton } from "@saleor/components/ConfirmButton";
import CustomerAddressChoiceCard from "@saleor/customers/components/CustomerAddressChoiceCard"; import CustomerAddressChoiceCard from "@saleor/customers/components/CustomerAddressChoiceCard";
import { CustomerAddresses_user_addresses } from "@saleor/customers/types/CustomerAddresses"; import { CustomerAddresses_user_addresses } from "@saleor/customers/types/CustomerAddresses";
import { FormChange } from "@saleor/hooks/useForm";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { SearchIcon } from "@saleor/macaw-ui"; import {
Button,
ConfirmButtonTransitionState,
SearchIcon
} from "@saleor/macaw-ui";
import { AddressTypeEnum } from "@saleor/types/globalTypes"; import { AddressTypeEnum } from "@saleor/types/globalTypes";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { getById } from "../OrderReturnPage/utils"; import { getById } from "../OrderReturnPage/utils";
import { addressSearchMessages as messages } from "./messages"; import { dialogMessages as messages } from "./messages";
import { useStyles } from "./styles"; import { useStyles } from "./styles";
import { parseQuery, stringifyAddress } from "./utils"; import { parseQuery, stringifyAddress } from "./utils";
export interface OrderCustomerAddressesSearchProps { export interface OrderCustomerAddressesSearchProps {
type: AddressTypeEnum; type: AddressTypeEnum;
selectedCustomerAddressId?: string; cloneAddress: boolean;
formChange: FormChange;
openFromCustomerChange: boolean;
transitionState: ConfirmButtonTransitionState;
selectedCustomerAddressId: string;
customerAddresses: CustomerAddresses_user_addresses[]; customerAddresses: CustomerAddresses_user_addresses[];
onChangeCustomerShippingAddress: ( onChangeCustomerShippingAddress: (
customerAddress: CustomerAddresses_user_addresses customerAddress: CustomerAddresses_user_addresses
@ -37,6 +46,10 @@ export interface OrderCustomerAddressesSearchProps {
const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps> = props => { const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps> = props => {
const { const {
type, type,
cloneAddress,
formChange,
transitionState,
openFromCustomerChange,
selectedCustomerAddressId, selectedCustomerAddressId,
customerAddresses, customerAddresses,
onChangeCustomerShippingAddress, onChangeCustomerShippingAddress,
@ -63,7 +76,9 @@ const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps>
} else { } else {
onChangeCustomerBillingAddress(temporarySelectedAddress); onChangeCustomerBillingAddress(temporarySelectedAddress);
} }
if (openFromCustomerChange) {
exitSearch(); exitSearch();
}
}; };
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -78,16 +93,9 @@ const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps>
return ( return (
<> <>
<DialogTitle>
{type === AddressTypeEnum.SHIPPING ? (
<FormattedMessage {...messages.shippingTitle} />
) : (
<FormattedMessage {...messages.billingTitle} />
)}
</DialogTitle>
<DialogContent> <DialogContent>
{intl.formatMessage(messages.searchInfo)} {intl.formatMessage(messages.searchInfo)}
<CardSpacer /> <VerticalSpacer spacing={2} />
<TextField <TextField
value={query} value={query}
variant="outlined" variant="outlined"
@ -103,7 +111,7 @@ const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps>
}} }}
inputProps={{ className: classes.searchInput }} inputProps={{ className: classes.searchInput }}
/> />
<CardSpacer /> <VerticalSpacer spacing={2} />
<div className={classes.scrollableWrapper}> <div className={classes.scrollableWrapper}>
{filteredCustomerAddresses.length === 0 {filteredCustomerAddresses.length === 0
? intl.formatMessage(messages.noResultsFound) ? intl.formatMessage(messages.noResultsFound)
@ -114,18 +122,42 @@ const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps>
onSelect={() => setTemporarySelectedAddress(address)} onSelect={() => setTemporarySelectedAddress(address)}
address={address} address={address}
/> />
<CardSpacer /> <VerticalSpacer spacing={2} />
</React.Fragment> </React.Fragment>
))} ))}
</div> </div>
{!openFromCustomerChange && filteredCustomerAddresses.length !== 0 && (
<FormControlLabel
control={
<Checkbox
checked={cloneAddress}
name="cloneAddress"
onChange={() =>
formChange({
target: {
name: "cloneAddress",
value: !cloneAddress
}
})
}
/>
}
label={intl.formatMessage(
type === AddressTypeEnum.SHIPPING
? messages.billingSameAsShipping
: messages.shippingSameAsBilling
)}
/>
)}
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={() => exitSearch()} color="primary"> <Button onClick={exitSearch} variant="secondary">
<FormattedMessage {...buttonMessages.cancel} /> <FormattedMessage {...buttonMessages.cancel} />
</Button> </Button>
<ConfirmButton <ConfirmButton
variant="primary" variant="primary"
transitionState="default" transitionState={transitionState}
type={openFromCustomerChange ? undefined : "submit"}
onClick={handleSelect} onClick={handleSelect}
> >
<FormattedMessage {...buttonMessages.select} /> <FormattedMessage {...buttonMessages.select} />

View file

@ -1,4 +1,5 @@
import { useExitFormDialog } from "@saleor/components/Form/useExitFormDialog"; import { useExitFormDialog } from "@saleor/components/Form/useExitFormDialog";
import { ShopInfo_shop_countries } from "@saleor/components/Shop/types/ShopInfo";
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField"; import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import { AddressTypeInput } from "@saleor/customers/types"; import { AddressTypeInput } from "@saleor/customers/types";
import { import {
@ -21,7 +22,7 @@ export enum AddressInputOptionEnum {
} }
export interface OrderCustomerAddressesEditFormData { export interface OrderCustomerAddressesEditFormData {
billingSameAsShipping: boolean; cloneAddress: boolean;
shippingAddressInputOption: AddressInputOptionEnum; shippingAddressInputOption: AddressInputOptionEnum;
billingAddressInputOption: AddressInputOptionEnum; billingAddressInputOption: AddressInputOptionEnum;
customerShippingAddress: CustomerAddresses_user_defaultShippingAddress; customerShippingAddress: CustomerAddresses_user_defaultShippingAddress;
@ -59,42 +60,46 @@ interface UseOrderCustomerAddressesEditFormResult
interface UseOrderCustomerAddressesEditFormOpts { interface UseOrderCustomerAddressesEditFormOpts {
countryChoices: SingleAutocompleteChoiceType[]; countryChoices: SingleAutocompleteChoiceType[];
countries: ShopInfo_shop_countries[];
defaultShippingAddress: CustomerAddresses_user_defaultShippingAddress; defaultShippingAddress: CustomerAddresses_user_defaultShippingAddress;
defaultBillingAddress: CustomerAddresses_user_defaultBillingAddress; defaultBillingAddress: CustomerAddresses_user_defaultBillingAddress;
defaultCloneAddress: boolean;
} }
export interface OrderCustomerAddressesEditFormProps export interface OrderCustomerAddressesEditFormProps
extends UseOrderCustomerAddressesEditFormOpts { extends UseOrderCustomerAddressesEditFormOpts {
children: (props: UseOrderCustomerAddressesEditFormResult) => React.ReactNode; children: (props: UseOrderCustomerAddressesEditFormResult) => React.ReactNode;
initial?: Partial<OrderCustomerAddressesEditFormData>; initial?: Partial<OrderCustomerAddressesEditFormData>;
onSubmit: (data: OrderCustomerAddressesEditData) => SubmitPromise<any[]>; onSubmit: (data: OrderCustomerAddressesEditData) => void;
} }
const initialAddress: AddressTypeInput = { function useOrderCustomerAddressesEditForm(
providedInitialFormData: Partial<OrderCustomerAddressesEditFormData>,
onSubmit: (data: OrderCustomerAddressesEditData) => void,
opts: UseOrderCustomerAddressesEditFormOpts
): UseOrderCustomerAddressesEditFormResult {
const emptyAddress: AddressTypeInput = {
city: "", city: "",
country: "", country: "",
phone: "", phone: "",
postalCode: "", postalCode: "",
streetAddress1: "" streetAddress1: ""
}; };
const defaultInitialFormData: OrderCustomerAddressesEditFormData = {
const getDefaultInitialFormData = ( cloneAddress: opts.defaultCloneAddress,
opts: UseOrderCustomerAddressesEditFormOpts
): OrderCustomerAddressesEditFormData => ({
billingSameAsShipping: true,
shippingAddressInputOption: AddressInputOptionEnum.CUSTOMER_ADDRESS, shippingAddressInputOption: AddressInputOptionEnum.CUSTOMER_ADDRESS,
billingAddressInputOption: AddressInputOptionEnum.CUSTOMER_ADDRESS, billingAddressInputOption: AddressInputOptionEnum.CUSTOMER_ADDRESS,
customerShippingAddress: opts.defaultShippingAddress, customerShippingAddress: opts.defaultShippingAddress,
customerBillingAddress: opts.defaultBillingAddress, customerBillingAddress: opts.defaultBillingAddress,
shippingAddress: initialAddress, shippingAddress: emptyAddress,
billingAddress: initialAddress billingAddress: emptyAddress
}); };
const initialData = {
...defaultInitialFormData,
...providedInitialFormData
};
function useOrderCustomerAddressesEditForm(
initial: Partial<OrderCustomerAddressesEditFormData>,
onSubmit: (data: OrderCustomerAddressesEditData) => SubmitPromise<any[]>,
opts: UseOrderCustomerAddressesEditFormOpts
): UseOrderCustomerAddressesEditFormResult {
const { const {
handleChange, handleChange,
hasChanged, hasChanged,
@ -102,17 +107,20 @@ function useOrderCustomerAddressesEditForm(
data: formData, data: formData,
setChanged setChanged
} = useForm({ } = useForm({
...initial, ...initialData
...getDefaultInitialFormData(opts)
}); });
const { setExitDialogSubmitRef } = useExitFormDialog(); const { setExitDialogSubmitRef } = useExitFormDialog();
const [shippingCountryDisplayName, setShippingCountryDisplayName] = useState( const [shippingCountryDisplayName, setShippingCountryDisplayName] = useState(
"" opts.countries.find(
country => initialData.shippingAddress.country === country.code
)?.country
); );
const [billingCountryDisplayName, setBillingCountryDisplayName] = useState( const [billingCountryDisplayName, setBillingCountryDisplayName] = useState(
"" opts.countries.find(
country => initialData.billingAddress.country === country.code
)?.country
); );
const handleFormAddressChange = ( const handleFormAddressChange = (

View file

@ -1,46 +1,48 @@
import { defineMessages } from "react-intl"; import { defineMessages } from "react-intl";
export const dialogMessages = defineMessages({ export const dialogMessages = defineMessages({
title: { customerChangeTitle: {
defaultMessage: "Change address for order", defaultMessage: "Change address for order",
description: "dialog header" description: "dialog header"
}, },
shippingChangeTitle: {
defaultMessage: "Change customer shipping address",
description: "dialog header"
},
billingChangeTitle: {
defaultMessage: "Change customer billing address",
description: "dialog header"
},
billingSameAsShipping: { billingSameAsShipping: {
defaultMessage: "Set the same for billing address", defaultMessage: "Set the same for billing address",
description: "checkbox label" description: "checkbox label"
}, },
shippingAddressDescription: { shippingSameAsBilling: {
defaultMessage: "Set the same for shipping address",
description: "checkbox label"
},
addressChangeDescription: {
defaultMessage: "Select method you want to use to change address",
description: "dialog content"
},
noAddressDescription: {
defaultMessage: defaultMessage:
"This customer doesnt have any shipping addresses. Provide address for order:", "This customer doesn't have any addresses in the address book. Provide address for order:",
description: "dialog content" description: "dialog content"
}, },
billingAddressDescription: { customerChangeDescription: {
defaultMessage: "Add a new address:",
description: "dialog content"
},
customerShippingAddressDescription: {
defaultMessage: defaultMessage:
"Which address would you like to use as shipping address for selected customer:", "Which address would you like to use as shipping address for selected customer:",
description: "dialog content" description: "dialog content"
}, },
customerBillingAddressDescription: { customerChangeBillingDescription: {
defaultMessage: "Select one of customer addresses or add a new address:", defaultMessage: "Select one of customer addresses or add a new address:",
description: "dialog content" description: "dialog content"
}
});
export const addressEditMessages = defineMessages({
customerAddress: {
defaultMessage: "Use one of customer addresses",
description: "address type"
}, },
newAddress: { noAddressBillingDescription: {
defaultMessage: "Add new address", defaultMessage: "Add a new address:",
description: "address type" description: "dialog content"
} },
});
export const addressSearchMessages = defineMessages({
shippingTitle: { shippingTitle: {
defaultMessage: "Shipping address", defaultMessage: "Shipping address",
description: "search modal shipping title" description: "search modal shipping title"
@ -54,6 +56,18 @@ export const addressSearchMessages = defineMessages({
description: "modal information under title" description: "modal information under title"
}, },
noResultsFound: { noResultsFound: {
defaultMessage: "No results found" defaultMessage: "No results found",
description: "info when addresses search is unsuccessful"
}
});
export const addressEditMessages = defineMessages({
customerAddress: {
defaultMessage: "Use one of customer addresses",
description: "address type"
},
newAddress: {
defaultMessage: "Add new address",
description: "address type"
} }
}); });

View file

@ -2,10 +2,12 @@ import { makeStyles } from "@saleor/macaw-ui";
export const useStyles = makeStyles( export const useStyles = makeStyles(
theme => ({ theme => ({
scrollableContent: { dialogContent: {
maxHeight: `calc(100vh - 250px)`, maxHeight: `calc(100vh - 250px)`,
overflowY: "scroll", overflowY: "scroll",
overflowX: "hidden" overflowX: "hidden",
padding: "24px",
margin: 0
}, },
scrollableWrapper: { scrollableWrapper: {
maxHeight: 400, maxHeight: 400,
@ -17,9 +19,6 @@ export const useStyles = makeStyles(
optionLabel: { optionLabel: {
display: "block" display: "block"
}, },
overflow: {
overflowY: "visible"
},
searchInput: { searchInput: {
paddingTop: theme.spacing(2), paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2) paddingBottom: theme.spacing(2)

View file

@ -0,0 +1,15 @@
import { AddressInput, AddressTypeEnum } from "@saleor/types/globalTypes";
export interface OrderCustomerSearchAddressState {
open: boolean;
type: AddressTypeEnum;
}
export interface OrderCustomerAddressesEditDialogOutput {
shippingAddress: AddressInput;
billingAddress: AddressInput;
}
export enum AddressEditDialogVariant {
CHANGE_CUSTOMER,
CHANGE_SHIPPING_ADDRESS,
CHANGE_BILLING_ADDRESS
}

View file

@ -1,10 +1,28 @@
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
import { import {
CustomerAddresses_user_addresses, CustomerAddresses_user_addresses,
CustomerAddresses_user_defaultShippingAddress CustomerAddresses_user_defaultShippingAddress
} from "@saleor/customers/types/CustomerAddresses"; } from "@saleor/customers/types/CustomerAddresses";
import { AccountErrorFragment } from "@saleor/fragments/types/AccountErrorFragment";
import { OrderErrorFragment } from "@saleor/fragments/types/OrderErrorFragment";
import { FormChange } from "@saleor/hooks/useForm";
import { flatten } from "@saleor/misc"; import { flatten } from "@saleor/misc";
import { AddressTypeEnum } from "@saleor/types/globalTypes";
import { getById } from "../OrderReturnPage/utils"; import { getById } from "../OrderReturnPage/utils";
import {
OrderCustomerAddressesEditData,
OrderCustomerAddressesEditHandlers
} from "./form";
import { OrderCustomerAddressEditProps } from "./OrderCustomerAddressEdit";
import { OrderCustomerSearchAddressState } from "./types";
interface AddressEditCommonProps {
showCard: boolean;
loading: boolean;
countryChoices: SingleAutocompleteChoiceType[];
customerAddresses: CustomerAddresses_user_addresses[];
}
export const stringifyAddress = ( export const stringifyAddress = (
address: Partial<CustomerAddresses_user_addresses> address: Partial<CustomerAddresses_user_addresses>
@ -33,3 +51,58 @@ export function validateDefaultAddress<
} }
return defaultAddress; return defaultAddress;
} }
export const getAddressEditProps = (
variant: "shipping" | "billing",
data: OrderCustomerAddressesEditData,
handlers: OrderCustomerAddressesEditHandlers,
change: FormChange,
dialogErrors: Array<OrderErrorFragment | AccountErrorFragment>,
setAddressSearchState: React.Dispatch<
React.SetStateAction<OrderCustomerSearchAddressState>
>,
addressEditCommonProps: AddressEditCommonProps
): OrderCustomerAddressEditProps => {
if (variant === "shipping") {
return {
...addressEditCommonProps,
addressInputName: "shippingAddressInputOption",
formErrors: dialogErrors.filter(
error => error.addressType === AddressTypeEnum.SHIPPING
),
onEdit: () =>
setAddressSearchState({
open: true,
type: AddressTypeEnum.SHIPPING
}),
onChangeAddressInputOption: change,
addressInputOption: data.shippingAddressInputOption,
selectedCustomerAddressId: data.customerShippingAddress?.id,
formAddress: data.shippingAddress,
formAddressCountryDisplayName: data.shippingCountryDisplayName,
onChangeFormAddress: event =>
handlers.changeFormAddress(event, "shippingAddress"),
onChangeFormAddressCountry: handlers.selectShippingCountry
};
}
return {
...addressEditCommonProps,
addressInputName: "billingAddressInputOption",
formErrors: dialogErrors.filter(
error => error.addressType === AddressTypeEnum.BILLING
),
onEdit: () =>
setAddressSearchState({
open: true,
type: AddressTypeEnum.BILLING
}),
onChangeAddressInputOption: change,
addressInputOption: data.billingAddressInputOption,
selectedCustomerAddressId: data.customerBillingAddress?.id,
formAddress: data.billingAddress,
formAddressCountryDisplayName: data.billingCountryDisplayName,
onChangeFormAddress: event =>
handlers.changeFormAddress(event, "billingAddress"),
onChangeFormAddressCountry: handlers.selectBillingCountry
};
};

View file

@ -140,7 +140,7 @@ const OrderDraftPage: React.FC<OrderDraftPageProps> = props => {
</div> </div>
<div> <div>
<OrderCustomer <OrderCustomer
canEditAddresses={true} canEditAddresses={!!order?.user}
canEditCustomer={true} canEditCustomer={true}
fetchUsers={fetchUsers} fetchUsers={fetchUsers}
hasMore={hasMore} hasMore={hasMore}

View file

@ -1,87 +0,0 @@
import { transformAddressToForm } from "@saleor/misc";
import OrderAddressEditDialog from "@saleor/orders/components/OrderAddressEditDialog";
import { OrderDetails } from "@saleor/orders/types/OrderDetails";
import {
OrderDraftUpdate,
OrderDraftUpdateVariables
} from "@saleor/orders/types/OrderDraftUpdate";
import {
OrderUpdate,
OrderUpdateVariables
} from "@saleor/orders/types/OrderUpdate";
import { PartialMutationProviderOutput } from "@saleor/types";
import { AddressInput } from "@saleor/types/globalTypes";
import React from "react";
enum FieldType {
shipping = "shippingAddress",
billing = "billingAddress"
}
interface Props {
action: string;
id: string;
isDraft: boolean;
data: OrderDetails;
onClose: () => void;
orderUpdate: PartialMutationProviderOutput<OrderUpdate, OrderUpdateVariables>;
orderDraftUpdate: PartialMutationProviderOutput<
OrderDraftUpdate,
OrderDraftUpdateVariables
>;
}
const OrderAddressFields = ({
action,
isDraft,
id,
onClose,
orderUpdate,
orderDraftUpdate,
data
}: Props) => {
const order = data?.order;
const handleConfirm = (type: FieldType) => (value: AddressInput) => {
const updateMutation = isDraft ? orderDraftUpdate : orderUpdate;
updateMutation.mutate({
id,
input: {
[type]: value
}
});
};
const addressFieldCommonProps = {
confirmButtonState: isDraft
? orderDraftUpdate.opts.status
: orderUpdate.opts.status,
countries: data?.shop?.countries,
errors: isDraft
? orderDraftUpdate.opts.data?.draftOrderUpdate.errors
: orderUpdate.opts.data?.orderUpdate.errors,
onClose
};
return (
<>
<OrderAddressEditDialog
{...addressFieldCommonProps}
address={transformAddressToForm(order?.shippingAddress)}
open={action === "edit-shipping-address"}
variant="shipping"
onConfirm={handleConfirm(FieldType.shipping)}
/>
<OrderAddressEditDialog
{...addressFieldCommonProps}
address={transformAddressToForm(order?.billingAddress)}
open={action === "edit-billing-address"}
variant="billing"
onConfirm={handleConfirm(FieldType.billing)}
/>
</>
);
};
export default OrderAddressFields;

View file

@ -26,7 +26,6 @@ import {
OrderUrlDialog, OrderUrlDialog,
OrderUrlQueryParams OrderUrlQueryParams
} from "../../urls"; } from "../../urls";
import OrderAddressFields from "./OrderAddressFields";
import { OrderDetailsMessages } from "./OrderDetailsMessages"; import { OrderDetailsMessages } from "./OrderDetailsMessages";
import { OrderDraftDetails } from "./OrderDraftDetails"; import { OrderDraftDetails } from "./OrderDraftDetails";
import { OrderNormalDetails } from "./OrderNormalDetails"; import { OrderNormalDetails } from "./OrderNormalDetails";
@ -180,6 +179,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
orderAddNote={orderAddNote} orderAddNote={orderAddNote}
orderInvoiceRequest={orderInvoiceRequest} orderInvoiceRequest={orderInvoiceRequest}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
orderUpdate={orderUpdate}
orderCancel={orderCancel} orderCancel={orderCancel}
orderPaymentMarkAsPaid={orderPaymentMarkAsPaid} orderPaymentMarkAsPaid={orderPaymentMarkAsPaid}
orderVoid={orderVoid} orderVoid={orderVoid}
@ -224,6 +224,7 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
orderLineDelete={orderLineDelete} orderLineDelete={orderLineDelete}
orderInvoiceRequest={orderInvoiceRequest} orderInvoiceRequest={orderInvoiceRequest}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
orderUpdate={orderUpdate}
orderCancel={orderCancel} orderCancel={orderCancel}
orderShippingMethodUpdate={orderShippingMethodUpdate} orderShippingMethodUpdate={orderShippingMethodUpdate}
orderLinesAdd={orderLinesAdd} orderLinesAdd={orderLinesAdd}
@ -242,15 +243,6 @@ export const OrderDetails: React.FC<OrderDetailsProps> = ({ id, params }) => {
closeModal={closeModal} closeModal={closeModal}
/> />
)} )}
<OrderAddressFields
isDraft={order?.status === OrderStatus.DRAFT}
orderUpdate={orderUpdate}
orderDraftUpdate={orderDraftUpdate}
data={data}
id={id}
onClose={closeModal}
action={params.action}
/>
</> </>
)} )}
</OrderOperations> </OrderOperations>

View file

@ -4,19 +4,22 @@ import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import { useCustomerAddressesQuery } from "@saleor/customers/queries"; import { useCustomerAddressesQuery } from "@saleor/customers/queries";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import { CustomerEditData } from "@saleor/orders/components/OrderCustomer"; import { CustomerEditData } from "@saleor/orders/components/OrderCustomer";
import OrderCustomerAddressesEditDialog, { import { OrderCustomerAddressesEditDialogOutput } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/types";
OrderCustomerAddressesEditDialogOutput
} from "@saleor/orders/components/OrderCustomerAddressesEditDialog";
import { import {
CustomerChangeActionEnum, CustomerChangeActionEnum,
OrderCustomerChangeData OrderCustomerChangeData
} from "@saleor/orders/components/OrderCustomerChangeDialog/form"; } from "@saleor/orders/components/OrderCustomerChangeDialog/form";
import OrderCustomerChangeDialog from "@saleor/orders/components/OrderCustomerChangeDialog/OrderCustomerChangeDialog"; import OrderCustomerChangeDialog from "@saleor/orders/components/OrderCustomerChangeDialog/OrderCustomerChangeDialog";
import { OrderDetails } from "@saleor/orders/types/OrderDetails"; import { OrderDetails } from "@saleor/orders/types/OrderDetails";
import {
OrderDraftUpdate,
OrderDraftUpdateVariables
} from "@saleor/orders/types/OrderDraftUpdate";
import { getVariantSearchAddress } from "@saleor/orders/utils/data"; import { getVariantSearchAddress } from "@saleor/orders/utils/data";
import { OrderDiscountProvider } from "@saleor/products/components/OrderDiscountProviders/OrderDiscountProvider"; import { OrderDiscountProvider } from "@saleor/products/components/OrderDiscountProviders/OrderDiscountProvider";
import { OrderLineDiscountProvider } from "@saleor/products/components/OrderDiscountProviders/OrderLineDiscountProvider"; import { OrderLineDiscountProvider } from "@saleor/products/components/OrderDiscountProviders/OrderLineDiscountProvider";
import useCustomerSearch from "@saleor/searches/useCustomerSearch"; import useCustomerSearch from "@saleor/searches/useCustomerSearch";
import { PartialMutationProviderOutput } from "@saleor/types";
import { mapEdgesToItems } from "@saleor/utils/maps"; import { mapEdgesToItems } from "@saleor/utils/maps";
import React from "react"; import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
@ -27,6 +30,7 @@ import {
getStringOrPlaceholder getStringOrPlaceholder
} from "../../../../misc"; } from "../../../../misc";
import { productUrl } from "../../../../products/urls"; import { productUrl } from "../../../../products/urls";
import OrderAddressFields from "../../../components/OrderAddressFields/OrderAddressFields";
import OrderDraftCancelDialog from "../../../components/OrderDraftCancelDialog/OrderDraftCancelDialog"; import OrderDraftCancelDialog from "../../../components/OrderDraftCancelDialog/OrderDraftCancelDialog";
import OrderDraftPage from "../../../components/OrderDraftPage"; import OrderDraftPage from "../../../components/OrderDraftPage";
import OrderProductAddDialog from "../../../components/OrderProductAddDialog"; import OrderProductAddDialog from "../../../components/OrderProductAddDialog";
@ -45,13 +49,23 @@ interface OrderDraftDetailsProps {
orderLineDelete: any; orderLineDelete: any;
orderShippingMethodUpdate: any; orderShippingMethodUpdate: any;
orderLinesAdd: any; orderLinesAdd: any;
orderDraftUpdate: any; orderDraftUpdate: PartialMutationProviderOutput<
OrderDraftUpdate,
OrderDraftUpdateVariables
>;
orderDraftCancel: any; orderDraftCancel: any;
orderDraftFinalize: any; orderDraftFinalize: any;
openModal: (action: OrderUrlDialog, newParams?: OrderUrlQueryParams) => void; openModal: (action: OrderUrlDialog, newParams?: OrderUrlQueryParams) => void;
closeModal: any; closeModal: any;
} }
export const isAnyAddressEditModalOpen = (uri: string | undefined): boolean =>
[
"edit-customer-addresses",
"edit-shipping-address",
"edit-billing-address"
].includes(uri);
export const OrderDraftDetails: React.FC<OrderDraftDetailsProps> = ({ export const OrderDraftDetails: React.FC<OrderDraftDetailsProps> = ({
id, id,
params, params,
@ -99,7 +113,7 @@ export const OrderDraftDetails: React.FC<OrderDraftDetailsProps> = ({
variables: { variables: {
id: order?.user?.id id: order?.user?.id
}, },
skip: params.action !== "edit-customer-addresses" skip: !order?.user?.id || !isAnyAddressEditModalOpen(params.action)
}); });
const intl = useIntl(); const intl = useIntl();
@ -140,21 +154,13 @@ export const OrderDraftDetails: React.FC<OrderDraftDetailsProps> = ({
} }
}; };
const handleCustomerChangeAdresses = async ( const handleCustomerChangeAddresses = async (
data: OrderCustomerAddressesEditDialogOutput data: Partial<OrderCustomerAddressesEditDialogOutput>
) => { ): Promise<any> =>
const result = await orderDraftUpdate.mutate({ orderDraftUpdate.mutate({
id, id,
input: { input: data
shippingAddress: data.shippingAddress,
billingAddress: data.billingAddress
}
}); });
if (!result?.data?.draftOrderUpdate?.errors?.length) {
closeModal();
}
return result;
};
return ( return (
<> <>
@ -266,17 +272,18 @@ export const OrderDraftDetails: React.FC<OrderDraftDetailsProps> = ({
onClose={closeModal} onClose={closeModal}
onConfirm={handleCustomerChangeAction} onConfirm={handleCustomerChangeAction}
/> />
<OrderCustomerAddressesEditDialog <OrderAddressFields
open={params.action === "edit-customer-addresses"} action={params?.action}
loading={customerAddressesLoading} orderShippingAddress={order?.shippingAddress}
confirmButtonState={orderDraftUpdate.opts.status} orderBillingAddress={order?.billingAddress}
errors={orderDraftUpdate.opts.data?.draftOrderUpdate?.errors || []} customerAddressesLoading={customerAddressesLoading}
isDraft
countries={data?.shop?.countries} countries={data?.shop?.countries}
customerAddresses={customerAddresses?.user?.addresses} customer={customerAddresses?.user}
defaultShippingAddress={customerAddresses?.user?.defaultShippingAddress}
defaultBillingAddress={customerAddresses?.user?.defaultBillingAddress}
onClose={closeModal} onClose={closeModal}
onConfirm={handleCustomerChangeAdresses} onConfirm={handleCustomerChangeAddresses}
confirmButtonState={orderDraftUpdate.opts.status}
errors={orderDraftUpdate.opts.data?.draftOrderUpdate.errors}
/> />
</> </>
); );

View file

@ -1,13 +1,19 @@
import { useUser } from "@saleor/auth"; import { useUser } from "@saleor/auth";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import { useCustomerAddressesQuery } from "@saleor/customers/queries";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog"; import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
import { OrderCustomerAddressesEditDialogOutput } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/types";
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog"; import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog"; import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
import { import {
OrderFulfillmentApprove, OrderFulfillmentApprove,
OrderFulfillmentApproveVariables OrderFulfillmentApproveVariables
} from "@saleor/orders/types/OrderFulfillmentApprove"; } from "@saleor/orders/types/OrderFulfillmentApprove";
import {
OrderUpdate,
OrderUpdateVariables
} from "@saleor/orders/types/OrderUpdate";
import { PartialMutationProviderOutput } from "@saleor/types"; import { PartialMutationProviderOutput } from "@saleor/types";
import { mapEdgesToItems } from "@saleor/utils/maps"; import { mapEdgesToItems } from "@saleor/utils/maps";
import { useWarehouseList } from "@saleor/warehouses/queries"; import { useWarehouseList } from "@saleor/warehouses/queries";
@ -22,6 +28,7 @@ import {
} from "../../../../misc"; } from "../../../../misc";
import { productUrl } from "../../../../products/urls"; import { productUrl } from "../../../../products/urls";
import { FulfillmentStatus } from "../../../../types/globalTypes"; import { FulfillmentStatus } from "../../../../types/globalTypes";
import OrderAddressFields from "../../../components/OrderAddressFields/OrderAddressFields";
import OrderCancelDialog from "../../../components/OrderCancelDialog"; import OrderCancelDialog from "../../../components/OrderCancelDialog";
import OrderDetailsPage from "../../../components/OrderDetailsPage"; import OrderDetailsPage from "../../../components/OrderDetailsPage";
import OrderFulfillmentCancelDialog from "../../../components/OrderFulfillmentCancelDialog"; import OrderFulfillmentCancelDialog from "../../../components/OrderFulfillmentCancelDialog";
@ -37,6 +44,7 @@ import {
orderUrl, orderUrl,
OrderUrlQueryParams OrderUrlQueryParams
} from "../../../urls"; } from "../../../urls";
import { isAnyAddressEditModalOpen } from "../OrderDraftDetails";
interface OrderNormalDetailsProps { interface OrderNormalDetailsProps {
id: string; id: string;
@ -45,6 +53,7 @@ interface OrderNormalDetailsProps {
orderAddNote: any; orderAddNote: any;
orderInvoiceRequest: any; orderInvoiceRequest: any;
handleSubmit: any; handleSubmit: any;
orderUpdate: PartialMutationProviderOutput<OrderUpdate, OrderUpdateVariables>;
orderCancel: any; orderCancel: any;
orderPaymentMarkAsPaid: any; orderPaymentMarkAsPaid: any;
orderVoid: any; orderVoid: any;
@ -69,6 +78,7 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
orderAddNote, orderAddNote,
orderInvoiceRequest, orderInvoiceRequest,
handleSubmit, handleSubmit,
orderUpdate,
orderCancel, orderCancel,
orderPaymentMarkAsPaid, orderPaymentMarkAsPaid,
orderVoid, orderVoid,
@ -93,6 +103,24 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
first: 30 first: 30
} }
}); });
const {
data: customerAddresses,
loading: customerAddressesLoading
} = useCustomerAddressesQuery({
variables: {
id: order?.user?.id
},
skip: !order?.user?.id || !isAnyAddressEditModalOpen(params.action)
});
const handleCustomerChangeAddresses = async (
data: Partial<OrderCustomerAddressesEditDialogOutput>
): Promise<any> =>
orderUpdate.mutate({
id,
input: data
});
const intl = useIntl(); const intl = useIntl();
const [transactionReference, setTransactionReference] = React.useState(""); const [transactionReference, setTransactionReference] = React.useState("");
@ -307,6 +335,19 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
onClose={closeModal} onClose={closeModal}
onSend={() => orderInvoiceSend.mutate({ id: params.id })} onSend={() => orderInvoiceSend.mutate({ id: params.id })}
/> />
<OrderAddressFields
action={params?.action}
orderShippingAddress={order?.shippingAddress}
orderBillingAddress={order?.billingAddress}
customerAddressesLoading={customerAddressesLoading}
isDraft={false}
countries={data?.shop?.countries}
customer={customerAddresses?.user}
onClose={closeModal}
onConfirm={handleCustomerChangeAddresses}
confirmButtonState={orderUpdate.opts.status}
errors={orderUpdate.opts.data?.orderUpdate.errors}
/>
</> </>
); );
}; };

View file

@ -1,14 +1,20 @@
import { useUser } from "@saleor/auth"; import { useUser } from "@saleor/auth";
import { WindowTitle } from "@saleor/components/WindowTitle"; import { WindowTitle } from "@saleor/components/WindowTitle";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import { useCustomerAddressesQuery } from "@saleor/customers/queries";
import useNavigator from "@saleor/hooks/useNavigator"; import useNavigator from "@saleor/hooks/useNavigator";
import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog"; import OrderCannotCancelOrderDialog from "@saleor/orders/components/OrderCannotCancelOrderDialog";
import { OrderCustomerAddressesEditDialogOutput } from "@saleor/orders/components/OrderCustomerAddressesEditDialog/types";
import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog"; import OrderFulfillmentApproveDialog from "@saleor/orders/components/OrderFulfillmentApproveDialog";
import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog"; import OrderInvoiceEmailSendDialog from "@saleor/orders/components/OrderInvoiceEmailSendDialog";
import { import {
OrderFulfillmentApprove, OrderFulfillmentApprove,
OrderFulfillmentApproveVariables OrderFulfillmentApproveVariables
} from "@saleor/orders/types/OrderFulfillmentApprove"; } from "@saleor/orders/types/OrderFulfillmentApprove";
import {
OrderUpdate,
OrderUpdateVariables
} from "@saleor/orders/types/OrderUpdate";
import { OrderDiscountProvider } from "@saleor/products/components/OrderDiscountProviders/OrderDiscountProvider"; import { OrderDiscountProvider } from "@saleor/products/components/OrderDiscountProviders/OrderDiscountProvider";
import { OrderLineDiscountProvider } from "@saleor/products/components/OrderDiscountProviders/OrderLineDiscountProvider"; import { OrderLineDiscountProvider } from "@saleor/products/components/OrderDiscountProviders/OrderLineDiscountProvider";
import { PartialMutationProviderOutput } from "@saleor/types"; import { PartialMutationProviderOutput } from "@saleor/types";
@ -25,6 +31,7 @@ import {
} from "../../../../misc"; } from "../../../../misc";
import { productUrl } from "../../../../products/urls"; import { productUrl } from "../../../../products/urls";
import { FulfillmentStatus } from "../../../../types/globalTypes"; import { FulfillmentStatus } from "../../../../types/globalTypes";
import OrderAddressFields from "../../../components/OrderAddressFields/OrderAddressFields";
import OrderCancelDialog from "../../../components/OrderCancelDialog"; import OrderCancelDialog from "../../../components/OrderCancelDialog";
import OrderDetailsPage from "../../../components/OrderDetailsPage"; import OrderDetailsPage from "../../../components/OrderDetailsPage";
import OrderFulfillmentCancelDialog from "../../../components/OrderFulfillmentCancelDialog"; import OrderFulfillmentCancelDialog from "../../../components/OrderFulfillmentCancelDialog";
@ -43,6 +50,7 @@ import {
orderUrl, orderUrl,
OrderUrlQueryParams OrderUrlQueryParams
} from "../../../urls"; } from "../../../urls";
import { isAnyAddressEditModalOpen } from "../OrderDraftDetails";
interface OrderUnconfirmedDetailsProps { interface OrderUnconfirmedDetailsProps {
id: string; id: string;
@ -53,6 +61,7 @@ interface OrderUnconfirmedDetailsProps {
orderLineDelete: any; orderLineDelete: any;
orderInvoiceRequest: any; orderInvoiceRequest: any;
handleSubmit: any; handleSubmit: any;
orderUpdate: PartialMutationProviderOutput<OrderUpdate, OrderUpdateVariables>;
orderCancel: any; orderCancel: any;
orderShippingMethodUpdate: any; orderShippingMethodUpdate: any;
orderLinesAdd: any; orderLinesAdd: any;
@ -81,6 +90,7 @@ export const OrderUnconfirmedDetails: React.FC<OrderUnconfirmedDetailsProps> = (
orderLineDelete, orderLineDelete,
orderInvoiceRequest, orderInvoiceRequest,
handleSubmit, handleSubmit,
orderUpdate,
orderCancel, orderCancel,
orderShippingMethodUpdate, orderShippingMethodUpdate,
orderLinesAdd, orderLinesAdd,
@ -114,6 +124,25 @@ export const OrderUnconfirmedDetails: React.FC<OrderUnconfirmedDetailsProps> = (
first: 30 first: 30
} }
}); });
const {
data: customerAddresses,
loading: customerAddressesLoading
} = useCustomerAddressesQuery({
variables: {
id: order?.user?.id
},
skip: !order?.user?.id || !isAnyAddressEditModalOpen(params.action)
});
const handleCustomerChangeAddresses = async (
data: Partial<OrderCustomerAddressesEditDialogOutput>
): Promise<any> =>
orderUpdate.mutate({
id,
input: data
});
const intl = useIntl(); const intl = useIntl();
const [transactionReference, setTransactionReference] = React.useState(""); const [transactionReference, setTransactionReference] = React.useState("");
@ -382,6 +411,19 @@ export const OrderUnconfirmedDetails: React.FC<OrderUnconfirmedDetailsProps> = (
onClose={closeModal} onClose={closeModal}
onSend={() => orderInvoiceSend.mutate({ id: params.id })} onSend={() => orderInvoiceSend.mutate({ id: params.id })}
/> />
<OrderAddressFields
action={params?.action}
customerAddressesLoading={customerAddressesLoading}
orderShippingAddress={order?.shippingAddress}
orderBillingAddress={order?.billingAddress}
isDraft={false}
countries={data?.shop?.countries}
customer={customerAddresses?.user}
onClose={closeModal}
onConfirm={handleCustomerChangeAddresses}
confirmButtonState={orderUpdate.opts.status}
errors={orderUpdate.opts.data?.orderUpdate.errors}
/>
</> </>
); );
}; };

View file

@ -16867,6 +16867,36 @@ exports[`Storyshots Navigation / Menu item errors 1`] = `
/> />
`; `;
exports[`Storyshots Orders / Changing address in order address change when customer is changed 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / Changing address in order billing address change 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / Changing address in order loading 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / Changing address in order no customer addresses 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / Changing address in order shipping address change 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / Draft order channel section default 1`] = ` exports[`Storyshots Orders / Draft order channel section default 1`] = `
<div <div
style="padding:24px" style="padding:24px"
@ -17065,18 +17095,6 @@ exports[`Storyshots Orders / Order details channel section loading 1`] = `
</div> </div>
`; `;
exports[`Storyshots Orders / OrderAddressEditDialog billing address 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / OrderAddressEditDialog shipping address 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / OrderBulkCancelDialog default 1`] = ` exports[`Storyshots Orders / OrderBulkCancelDialog default 1`] = `
<div <div
style="padding:24px" style="padding:24px"
@ -17703,24 +17721,6 @@ exports[`Storyshots Orders / OrderCustomer with different addresses 1`] = `
</div> </div>
`; `;
exports[`Storyshots Orders / OrderCustomerAddressesEditDialog default 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / OrderCustomerAddressesEditDialog loading 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / OrderCustomerAddressesEditDialog no customer addresses 1`] = `
<div
style="padding:24px"
/>
`;
exports[`Storyshots Orders / OrderCustomerChangeDialog default 1`] = ` exports[`Storyshots Orders / OrderCustomerChangeDialog default 1`] = `
<div <div
style="padding:24px" style="padding:24px"
@ -138052,22 +138052,6 @@ exports[`Storyshots Views / Orders / Order draft default 1`] = `
> >
Shipping Address Shipping Address
</div> </div>
<div
class="OrderCustomer-sectionHeaderToolbar-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeSmall-id MuiButton-sizeSmall-id"
data-test-id="edit-shipping-address"
tabindex="0"
type="button"
>
<span
class="MuiButton-label-id"
>
Edit
</span>
</button>
</div>
</div> </div>
<div <div
class="MuiTypography-root-id MuiTypography-body1-id" class="MuiTypography-root-id MuiTypography-body1-id"
@ -138089,22 +138073,6 @@ exports[`Storyshots Views / Orders / Order draft default 1`] = `
> >
Billing Address Billing Address
</div> </div>
<div
class="OrderCustomer-sectionHeaderToolbar-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeSmall-id MuiButton-sizeSmall-id"
data-test-id="edit-billing-address"
tabindex="0"
type="button"
>
<span
class="MuiButton-label-id"
>
Edit
</span>
</button>
</div>
</div> </div>
<div <div
class="MuiTypography-root-id MuiTypography-body1-id" class="MuiTypography-root-id MuiTypography-body1-id"
@ -138338,23 +138306,6 @@ exports[`Storyshots Views / Orders / Order draft loading 1`] = `
> >
Shipping Address Shipping Address
</div> </div>
<div
class="OrderCustomer-sectionHeaderToolbar-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeSmall-id MuiButton-sizeSmall-id MuiButton-disabled-id MuiButtonBase-disabled-id"
data-test-id="edit-shipping-address"
disabled=""
tabindex="-1"
type="button"
>
<span
class="MuiButton-label-id"
>
Edit
</span>
</button>
</div>
</div> </div>
<span <span
class="Skeleton-skeleton-id" class="Skeleton-skeleton-id"
@ -138377,23 +138328,6 @@ exports[`Storyshots Views / Orders / Order draft loading 1`] = `
> >
Billing Address Billing Address
</div> </div>
<div
class="OrderCustomer-sectionHeaderToolbar-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeSmall-id MuiButton-sizeSmall-id MuiButton-disabled-id MuiButtonBase-disabled-id"
data-test-id="edit-billing-address"
disabled=""
tabindex="-1"
type="button"
>
<span
class="MuiButton-label-id"
>
Edit
</span>
</button>
</div>
</div> </div>
<span <span
class="Skeleton-skeleton-id" class="Skeleton-skeleton-id"
@ -139045,22 +138979,6 @@ exports[`Storyshots Views / Orders / Order draft no user permissions 1`] = `
> >
Shipping Address Shipping Address
</div> </div>
<div
class="OrderCustomer-sectionHeaderToolbar-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeSmall-id MuiButton-sizeSmall-id"
data-test-id="edit-shipping-address"
tabindex="0"
type="button"
>
<span
class="MuiButton-label-id"
>
Edit
</span>
</button>
</div>
</div> </div>
<div <div
class="MuiTypography-root-id MuiTypography-body1-id" class="MuiTypography-root-id MuiTypography-body1-id"
@ -139082,22 +139000,6 @@ exports[`Storyshots Views / Orders / Order draft no user permissions 1`] = `
> >
Billing Address Billing Address
</div> </div>
<div
class="OrderCustomer-sectionHeaderToolbar-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeSmall-id MuiButton-sizeSmall-id"
data-test-id="edit-billing-address"
tabindex="0"
type="button"
>
<span
class="MuiButton-label-id"
>
Edit
</span>
</button>
</div>
</div> </div>
<div <div
class="MuiTypography-root-id MuiTypography-body1-id" class="MuiTypography-root-id MuiTypography-body1-id"
@ -139411,22 +139313,6 @@ exports[`Storyshots Views / Orders / Order draft without lines 1`] = `
> >
Shipping Address Shipping Address
</div> </div>
<div
class="OrderCustomer-sectionHeaderToolbar-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeSmall-id MuiButton-sizeSmall-id"
data-test-id="edit-shipping-address"
tabindex="0"
type="button"
>
<span
class="MuiButton-label-id"
>
Edit
</span>
</button>
</div>
</div> </div>
<div <div
class="MuiTypography-root-id MuiTypography-body1-id" class="MuiTypography-root-id MuiTypography-body1-id"
@ -139448,22 +139334,6 @@ exports[`Storyshots Views / Orders / Order draft without lines 1`] = `
> >
Billing Address Billing Address
</div> </div>
<div
class="OrderCustomer-sectionHeaderToolbar-id"
>
<button
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-outlined-id MuiButton-outlinedPrimary-id MuiButton-outlinedSizeSmall-id MuiButton-sizeSmall-id"
data-test-id="edit-billing-address"
tabindex="0"
type="button"
>
<span
class="MuiButton-label-id"
>
Edit
</span>
</button>
</div>
</div> </div>
<div <div
class="MuiTypography-root-id MuiTypography-body1-id" class="MuiTypography-root-id MuiTypography-body1-id"

View file

@ -107,7 +107,6 @@ function loadStories() {
require("./stories/products/ProductVariantPage"); require("./stories/products/ProductVariantPage");
// Orders // Orders
require("./stories/orders/OrderAddressEditDialog");
require("./stories/orders/OrderBulkCancelDialog"); require("./stories/orders/OrderBulkCancelDialog");
require("./stories/orders/OrderCancelDialog"); require("./stories/orders/OrderCancelDialog");
require("./stories/orders/OrderCustomer"); require("./stories/orders/OrderCustomer");

View file

@ -1,36 +0,0 @@
import { storiesOf } from "@storybook/react";
import React from "react";
import { transformAddressToForm } from "../../../misc";
import OrderAddressEditDialog from "../../../orders/components/OrderAddressEditDialog";
import { countries, order as orderFixture } from "../../../orders/fixtures";
import Decorator from "../../Decorator";
const order = orderFixture("");
storiesOf("Orders / OrderAddressEditDialog", module)
.addDecorator(Decorator)
.add("shipping address", () => (
<OrderAddressEditDialog
confirmButtonState="default"
address={transformAddressToForm(order.shippingAddress)}
countries={countries}
errors={[]}
onClose={() => undefined}
onConfirm={() => undefined}
open={true}
variant="shipping"
/>
))
.add("billing address", () => (
<OrderAddressEditDialog
confirmButtonState="default"
address={transformAddressToForm(order.billingAddress)}
countries={countries}
errors={[]}
onClose={() => undefined}
onConfirm={() => undefined}
open={true}
variant="billing"
/>
));

View file

@ -1,5 +1,5 @@
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui"; import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
import { MutationResult } from "react-apollo"; import { MutationFetchResult, MutationResult } from "react-apollo";
import { IFilter, IFilterElement } from "./components/Filter"; import { IFilter, IFilterElement } from "./components/Filter";
import { MultiAutocompleteChoiceType } from "./components/MultiAutocompleteSelectField"; import { MultiAutocompleteChoiceType } from "./components/MultiAutocompleteSelectField";
@ -131,7 +131,7 @@ export interface PartialMutationProviderOutput<
TVariables extends {} = {} TVariables extends {} = {}
> { > {
opts: MutationResult<TData> & MutationResultAdditionalProps; opts: MutationResult<TData> & MutationResultAdditionalProps;
mutate: (variables: TVariables) => void; mutate: (variables: TVariables) => Promise<MutationFetchResult<TData>>;
} }
export interface Node { export interface Node {