saleor-dashboard/src/shipping/components/ShippingZoneRateDialog/ShippingZoneRateDialog.tsx
2020-03-24 13:54:25 +01:00

372 lines
14 KiB
TypeScript

import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ConfirmButton, {
ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import Form from "@saleor/components/Form";
import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr";
import Skeleton from "@saleor/components/Skeleton";
import { buttonMessages } from "@saleor/intl";
import { getFormErrors } from "@saleor/utils/errors";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import getShippingErrorMessage from "@saleor/utils/errors/shipping";
import { ShippingErrorFragment } from "@saleor/shipping/types/ShippingErrorFragment";
import { maybe } from "../../../misc";
import { ShippingMethodTypeEnum } from "../../../types/globalTypes";
import { ShippingZoneDetailsFragment_shippingMethods } from "../../types/ShippingZoneDetailsFragment";
import {
getShippingPriceRateErrorMessage,
getShippingWeightRateErrorMessage
} from "./errors";
export interface FormData {
name: string;
noLimits: boolean;
minValue: string;
maxValue: string;
isFree: boolean;
price: string;
}
export interface ShippingZoneRateDialogProps {
action: "create" | "edit";
confirmButtonState: ConfirmButtonTransitionState;
defaultCurrency: string;
disabled: boolean;
errors: ShippingErrorFragment[];
open: boolean;
rate: ShippingZoneDetailsFragment_shippingMethods;
variant: ShippingMethodTypeEnum;
onClose: () => void;
onSubmit: (data: FormData) => void;
}
const useStyles = makeStyles(
theme => ({
grid: {
display: "grid",
gridColumnGap: theme.spacing(2),
gridTemplateColumns: "1fr 1fr"
},
subheading: {
marginBottom: theme.spacing(2),
marginTop: theme.spacing(2)
}
}),
{
name: "ShippingZoneRateDialog"
}
);
const ShippingZoneRateDialog: React.FC<ShippingZoneRateDialogProps> = props => {
const {
action,
confirmButtonState,
defaultCurrency,
disabled,
errors: apiErrors,
onClose,
onSubmit,
open,
rate,
variant
} = props;
const classes = useStyles(props);
const intl = useIntl();
const errors = useModalDialogErrors(apiErrors, open);
const formFields = [
"name",
"minimumOrderPrice",
"minimumOrderWeight",
"maximumOrderPrice",
"maximumOrderWeight",
"price"
];
const formErrors = getFormErrors(formFields, errors);
const initialForm: FormData =
action === "create"
? {
isFree: false,
maxValue: "",
minValue: "",
name: "",
noLimits: false,
price: ""
}
: {
isFree: maybe(() => rate.price.amount === 0, false),
maxValue:
variant === ShippingMethodTypeEnum.PRICE
? maybe(() => rate.maximumOrderPrice.amount.toString(), "")
: maybe(() => rate.maximumOrderWeight.value.toString(), ""),
minValue:
variant === ShippingMethodTypeEnum.PRICE
? maybe(() => rate.minimumOrderPrice.amount.toString(), "")
: maybe(() => rate.minimumOrderWeight.value.toString(), ""),
name: maybe(() => rate.name, ""),
noLimits: false,
price: maybe(() => rate.price.amount.toString(), "")
};
if (action === "edit") {
initialForm.noLimits = !initialForm.maxValue && !initialForm.minValue;
}
const getErrorMessage =
variant === ShippingMethodTypeEnum.PRICE
? getShippingPriceRateErrorMessage
: getShippingWeightRateErrorMessage;
return (
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
<Form initial={initialForm} onSubmit={onSubmit}>
{({ change, data, hasChanged }) => (
<>
<DialogTitle>
{variant === ShippingMethodTypeEnum.PRICE
? action === "create"
? intl.formatMessage({
defaultMessage: "Add Price Rate",
description: "dialog header"
})
: intl.formatMessage({
defaultMessage: "Edit Price Rate",
description: "dialog header"
})
: action === "create"
? intl.formatMessage({
defaultMessage: "Add Weight Rate",
description:
"add weight based shipping method, dialog header"
})
: intl.formatMessage({
defaultMessage: "Edit Weight Rate",
description:
"edit weight based shipping method, dialog header"
})}
</DialogTitle>
<DialogContent>
<TextField
disabled={disabled}
error={!!formErrors.name}
fullWidth
helperText={
getShippingErrorMessage(formErrors.name, intl) ||
intl.formatMessage({
defaultMessage:
"This will be shown to customers at checkout"
})
}
label={intl.formatMessage({
defaultMessage: "Rate Name",
description: "shipping method name"
})}
name={"name" as keyof FormData}
value={data.name}
onChange={change}
/>
</DialogContent>
<Hr />
<DialogContent>
{!!variant ? (
<>
<Typography
className={classes.subheading}
variant="subtitle1"
>
{variant === ShippingMethodTypeEnum.PRICE
? intl.formatMessage({
defaultMessage: "Value range",
description: "order price range"
})
: intl.formatMessage({
defaultMessage: "Weight range",
description: "order weight range"
})}
</Typography>
<ControlledCheckbox
name={"noLimits" as keyof FormData}
label={
<>
<FormattedMessage
defaultMessage="There are no value limits"
description="shipping method has no value limits"
/>
<Typography variant="caption">
{variant === ShippingMethodTypeEnum.PRICE
? intl.formatMessage({
defaultMessage:
"This rate will apply to all orders of all prices"
})
: intl.formatMessage({
defaultMessage:
"This rate will apply to all orders of all weights"
})}
</Typography>
</>
}
checked={data.noLimits}
onChange={change}
disabled={disabled}
/>
{!data.noLimits && (
<>
<FormSpacer />
<div className={classes.grid}>
<TextField
disabled={disabled}
error={
!!formErrors.minimumOrderPrice ||
!!formErrors.minimumOrderWeight
}
fullWidth
helperText={
variant === ShippingMethodTypeEnum.PRICE
? getErrorMessage(
formErrors.minimumOrderPrice,
intl
)
: getErrorMessage(
formErrors.minimumOrderWeight,
intl
)
}
label={
variant === ShippingMethodTypeEnum.PRICE
? intl.formatMessage({
defaultMessage: "Minimal Order Value"
})
: intl.formatMessage({
defaultMessage: "Minimal Order Weight"
})
}
name={"minValue" as keyof FormData}
type="number"
value={data.minValue}
onChange={change}
/>
<TextField
disabled={disabled}
error={
!!formErrors.maximumOrderPrice ||
!!formErrors.maximumOrderWeight
}
fullWidth
helperText={
variant === ShippingMethodTypeEnum.PRICE
? getErrorMessage(
formErrors.maximumOrderPrice,
intl
)
: getErrorMessage(
formErrors.maximumOrderWeight,
intl
)
}
label={
variant === ShippingMethodTypeEnum.PRICE
? intl.formatMessage({
defaultMessage: "Maximal Order Value"
})
: intl.formatMessage({
defaultMessage: "Maximal Order Weight"
})
}
name={"maxValue" as keyof FormData}
type="number"
value={data.maxValue}
onChange={change}
/>
</div>
</>
)}
</>
) : (
<Skeleton />
)}
</DialogContent>
<Hr />
<DialogContent>
<Typography className={classes.subheading} variant="subtitle1">
<FormattedMessage
defaultMessage="Rate"
description="shipping method"
/>
</Typography>
<ControlledCheckbox
name={"isFree" as keyof FormData}
label={intl.formatMessage({
defaultMessage: "This is free shipping",
description: "shipping method, switch button"
})}
checked={data.isFree}
onChange={change}
disabled={disabled}
/>
{!data.isFree && (
<>
<FormSpacer />
<div className={classes.grid}>
<TextField
disabled={disabled}
error={!!formErrors.price}
fullWidth
helperText={getErrorMessage(formErrors.price, intl)}
label={intl.formatMessage({
defaultMessage: "Rate Price",
description: "shipping method price"
})}
name={"price" as keyof FormData}
type="number"
value={data.price}
onChange={change}
InputProps={{
endAdornment: defaultCurrency
}}
/>
</div>
</>
)}
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
disabled={disabled || !hasChanged}
transitionState={confirmButtonState}
color="primary"
variant="contained"
type="submit"
>
{action === "create"
? intl.formatMessage({
defaultMessage: "Create rate",
description: "button"
})
: intl.formatMessage({
defaultMessage: "Update rate",
description: "button"
})}
</ConfirmButton>
</DialogActions>
</>
)}
</Form>
</Dialog>
);
};
ShippingZoneRateDialog.displayName = "ShippingZoneRateDialog";
export default ShippingZoneRateDialog;