* wip design label * add usesLeft calculation * snapshots & messages * fix type errors * add error on input and disable save button when value is invalid * resetting input value to initial after checkbox state change * remove uses left on new vouchers & set initial value to 1
This commit is contained in:
parent
4f18947dda
commit
3de07a4f3b
9 changed files with 115 additions and 44 deletions
|
@ -3123,6 +3123,10 @@
|
|||
"context": "voucher usage limit, header",
|
||||
"string": "Usage Limit"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_VoucherLimits_dot_usesLeftCaption": {
|
||||
"context": "usage limit uses left caption",
|
||||
"string": "Uses left"
|
||||
},
|
||||
"src_dot_discounts_dot_components_dot_VoucherListPage_dot_1112241061": {
|
||||
"context": "tab name",
|
||||
"string": "All Vouchers"
|
||||
|
|
|
@ -45,7 +45,8 @@ export interface FormData extends MetadataFormData {
|
|||
startDate: string;
|
||||
startTime: string;
|
||||
type: VoucherTypeEnum;
|
||||
usageLimit: string;
|
||||
usageLimit: number;
|
||||
used: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
|
@ -95,7 +96,8 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
|
|||
startDate: "",
|
||||
startTime: "",
|
||||
type: VoucherTypeEnum.ENTIRE_ORDER,
|
||||
usageLimit: "0",
|
||||
usageLimit: 1,
|
||||
used: 0,
|
||||
value: 0,
|
||||
metadata: [],
|
||||
privateMetadata: []
|
||||
|
@ -103,7 +105,7 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
|
|||
|
||||
return (
|
||||
<Form initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ change, data, hasChanged, submit, triggerChange }) => {
|
||||
{({ change, data, hasChanged, submit, triggerChange, set }) => {
|
||||
const handleDiscountTypeChange = createDiscountTypeChangeHandler(
|
||||
change
|
||||
);
|
||||
|
@ -173,9 +175,12 @@ const VoucherCreatePage: React.FC<VoucherCreatePageProps> = ({
|
|||
<CardSpacer />
|
||||
<VoucherLimits
|
||||
data={data}
|
||||
initialUsageLimit={initialForm.usageLimit}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
setData={set}
|
||||
isNewVoucher
|
||||
/>
|
||||
<CardSpacer />
|
||||
<VoucherDates
|
||||
|
|
|
@ -75,7 +75,8 @@ export interface VoucherDetailsPageFormData extends MetadataFormData {
|
|||
startDate: string;
|
||||
startTime: string;
|
||||
type: VoucherTypeEnum;
|
||||
usageLimit: string;
|
||||
usageLimit: number;
|
||||
used: number;
|
||||
}
|
||||
|
||||
export interface VoucherDetailsPageProps
|
||||
|
@ -192,14 +193,15 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
|
|||
startDate: splitDateTime(voucher?.startDate ?? "").date,
|
||||
startTime: splitDateTime(voucher?.startDate ?? "").time,
|
||||
type: voucher?.type ?? VoucherTypeEnum.ENTIRE_ORDER,
|
||||
usageLimit: voucher?.usageLimit?.toString() ?? "0",
|
||||
usageLimit: voucher?.usageLimit ?? 1,
|
||||
used: voucher?.used ?? 0,
|
||||
metadata: voucher?.metadata.map(mapMetadataItemToInput),
|
||||
privateMetadata: voucher?.privateMetadata.map(mapMetadataItemToInput)
|
||||
};
|
||||
|
||||
return (
|
||||
<Form initial={initialForm} onSubmit={onSubmit}>
|
||||
{({ change, data, hasChanged, submit, triggerChange }) => {
|
||||
{({ change, data, hasChanged, submit, triggerChange, set }) => {
|
||||
const handleDiscountTypeChange = createDiscountTypeChangeHandler(
|
||||
change
|
||||
);
|
||||
|
@ -209,13 +211,14 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
|
|||
triggerChange
|
||||
);
|
||||
const formDisabled =
|
||||
data.discountType.toString() !== "SHIPPING" &&
|
||||
data.channelListings?.some(
|
||||
channel =>
|
||||
validatePrice(channel.discountValue) ||
|
||||
(data.requirementsPicker === RequirementsPicker.ORDER &&
|
||||
validatePrice(channel.minSpent))
|
||||
);
|
||||
(data.discountType.toString() !== "SHIPPING" &&
|
||||
data.channelListings?.some(
|
||||
channel =>
|
||||
validatePrice(channel.discountValue) ||
|
||||
(data.requirementsPicker === RequirementsPicker.ORDER &&
|
||||
validatePrice(channel.minSpent))
|
||||
)) ||
|
||||
data.usageLimit <= 0;
|
||||
const changeMetadata = makeMetadataChangeHandler(change);
|
||||
|
||||
return (
|
||||
|
@ -399,9 +402,12 @@ const VoucherDetailsPage: React.FC<VoucherDetailsPageProps> = ({
|
|||
<CardSpacer />
|
||||
<VoucherLimits
|
||||
data={data}
|
||||
initialUsageLimit={initialForm.usageLimit}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
setData={set}
|
||||
isNewVoucher={false}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<DiscountDates
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Card, CardContent, TextField } from "@material-ui/core";
|
||||
import { Card, CardContent, TextField, Typography } from "@material-ui/core";
|
||||
import CardTitle from "@saleor/components/CardTitle";
|
||||
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
|
||||
import { Grid } from "@saleor/components/Grid";
|
||||
import { DiscountErrorFragment } from "@saleor/fragments/types/DiscountErrorFragment";
|
||||
import { getFormErrors } from "@saleor/utils/errors";
|
||||
import getDiscountErrorMessage from "@saleor/utils/errors/discounts";
|
||||
|
@ -9,50 +10,89 @@ import { useIntl } from "react-intl";
|
|||
|
||||
import { VoucherDetailsPageFormData } from "../VoucherDetailsPage";
|
||||
import messages from "./messages";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
interface VoucherLimitsProps {
|
||||
data: VoucherDetailsPageFormData;
|
||||
disabled: boolean;
|
||||
errors: DiscountErrorFragment[];
|
||||
initialUsageLimit: number;
|
||||
onChange: (event: React.ChangeEvent<any>) => void;
|
||||
setData: (data: Partial<VoucherDetailsPageFormData>) => void;
|
||||
isNewVoucher: boolean;
|
||||
}
|
||||
|
||||
const VoucherLimits = ({
|
||||
data,
|
||||
disabled,
|
||||
errors,
|
||||
onChange
|
||||
initialUsageLimit,
|
||||
onChange,
|
||||
setData,
|
||||
isNewVoucher
|
||||
}: VoucherLimitsProps) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
|
||||
const formErrors = getFormErrors(["usageLimit"], errors);
|
||||
|
||||
const usesLeft = data.usageLimit - data.used;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle title={intl.formatMessage(messages.usageLimitsTitle)} />
|
||||
<CardContent>
|
||||
<CardContent className={classes.cardContent}>
|
||||
<ControlledCheckbox
|
||||
checked={data.hasUsageLimit}
|
||||
label={intl.formatMessage(messages.hasUsageLimit)}
|
||||
name={"hasUsageLimit" as keyof VoucherDetailsPageFormData}
|
||||
onChange={onChange}
|
||||
onChange={evt => {
|
||||
onChange(evt);
|
||||
setData({ usageLimit: initialUsageLimit });
|
||||
}}
|
||||
/>
|
||||
{data.hasUsageLimit && (
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!formErrors.usageLimit}
|
||||
helperText={getDiscountErrorMessage(formErrors.usageLimit, intl)}
|
||||
label={intl.formatMessage(messages.usageLimit)}
|
||||
name={"usageLimit" as keyof VoucherDetailsPageFormData}
|
||||
value={data.usageLimit}
|
||||
onChange={onChange}
|
||||
type="number"
|
||||
inputProps={{
|
||||
min: 0
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
)}
|
||||
{data.hasUsageLimit &&
|
||||
(isNewVoucher ? (
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!formErrors.usageLimit || data.usageLimit <= 0}
|
||||
helperText={getDiscountErrorMessage(formErrors.usageLimit, intl)}
|
||||
label={intl.formatMessage(messages.usageLimit)}
|
||||
name={"usageLimit" as keyof VoucherDetailsPageFormData}
|
||||
value={data.usageLimit}
|
||||
onChange={onChange}
|
||||
type="number"
|
||||
fullWidth
|
||||
inputProps={{
|
||||
min: 1
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Grid variant="uniform">
|
||||
<TextField
|
||||
disabled={disabled}
|
||||
error={!!formErrors.usageLimit || data.usageLimit <= 0}
|
||||
helperText={getDiscountErrorMessage(
|
||||
formErrors.usageLimit,
|
||||
intl
|
||||
)}
|
||||
label={intl.formatMessage(messages.usageLimit)}
|
||||
name={"usageLimit" as keyof VoucherDetailsPageFormData}
|
||||
value={data.usageLimit}
|
||||
onChange={onChange}
|
||||
type="number"
|
||||
inputProps={{
|
||||
min: 1
|
||||
}}
|
||||
/>
|
||||
<div className={classes.usesLeftLabelWrapper}>
|
||||
<Typography variant="caption">
|
||||
{intl.formatMessage(messages.usesLeftCaption)}
|
||||
</Typography>
|
||||
<Typography>{usesLeft >= 0 ? usesLeft : 0}</Typography>
|
||||
</div>
|
||||
</Grid>
|
||||
))}
|
||||
<ControlledCheckbox
|
||||
checked={data.applyOncePerCustomer}
|
||||
label={intl.formatMessage(messages.applyOncePerCustomer)}
|
||||
|
|
|
@ -13,6 +13,10 @@ export default defineMessages({
|
|||
defaultMessage: "Limit of Uses",
|
||||
description: "limit voucher"
|
||||
},
|
||||
usesLeftCaption: {
|
||||
defaultMessage: "Uses left",
|
||||
description: "usage limit uses left caption"
|
||||
},
|
||||
applyOncePerCustomer: {
|
||||
defaultMessage: "Limit to one use per customer",
|
||||
description: "limit voucher"
|
||||
|
|
16
src/discounts/components/VoucherLimits/styles.ts
Normal file
16
src/discounts/components/VoucherLimits/styles.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
() => ({
|
||||
cardContent: {
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
},
|
||||
usesLeftLabelWrapper: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flex: 1
|
||||
}
|
||||
}),
|
||||
{ name: "VoucherLimits" }
|
||||
);
|
|
@ -49,9 +49,7 @@ export function createHandler(
|
|||
formData.discountType === DiscountTypeEnum.SHIPPING
|
||||
? VoucherTypeEnum.SHIPPING
|
||||
: formData.type,
|
||||
usageLimit: formData.hasUsageLimit
|
||||
? parseInt(formData.usageLimit, 10)
|
||||
: null
|
||||
usageLimit: formData.hasUsageLimit ? formData.usageLimit : null
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -56,9 +56,7 @@ export function createUpdateHandler(
|
|||
formData.discountType === DiscountTypeEnum.SHIPPING
|
||||
? VoucherTypeEnum.SHIPPING
|
||||
: formData.type,
|
||||
usageLimit: formData.hasUsageLimit
|
||||
? parseInt(formData.usageLimit, 10)
|
||||
: null
|
||||
usageLimit: formData.hasUsageLimit ? formData.usageLimit : null
|
||||
}
|
||||
}).then(({ data }) => data?.voucherUpdate.errors ?? []),
|
||||
|
||||
|
|
|
@ -99037,7 +99037,7 @@ exports[`Storyshots Views / Discounts / Voucher create default 1`] = `
|
|||
class="CardTitle-hr-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiCardContent-root-id"
|
||||
class="MuiCardContent-root-id VoucherLimits-cardContent-id"
|
||||
>
|
||||
<label
|
||||
class="MuiFormControlLabel-root-id"
|
||||
|
@ -100597,7 +100597,7 @@ exports[`Storyshots Views / Discounts / Voucher create form errors 1`] = `
|
|||
class="CardTitle-hr-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiCardContent-root-id"
|
||||
class="MuiCardContent-root-id VoucherLimits-cardContent-id"
|
||||
>
|
||||
<label
|
||||
class="MuiFormControlLabel-root-id"
|
||||
|
@ -102790,7 +102790,7 @@ exports[`Storyshots Views / Discounts / Voucher details default 1`] = `
|
|||
class="CardTitle-hr-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiCardContent-root-id"
|
||||
class="MuiCardContent-root-id VoucherLimits-cardContent-id"
|
||||
>
|
||||
<label
|
||||
class="MuiFormControlLabel-root-id"
|
||||
|
@ -105165,7 +105165,7 @@ exports[`Storyshots Views / Discounts / Voucher details form errors 1`] = `
|
|||
class="CardTitle-hr-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiCardContent-root-id"
|
||||
class="MuiCardContent-root-id VoucherLimits-cardContent-id"
|
||||
>
|
||||
<label
|
||||
class="MuiFormControlLabel-root-id"
|
||||
|
@ -107103,7 +107103,7 @@ exports[`Storyshots Views / Discounts / Voucher details loading 1`] = `
|
|||
class="CardTitle-hr-id"
|
||||
/>
|
||||
<div
|
||||
class="MuiCardContent-root-id"
|
||||
class="MuiCardContent-root-id VoucherLimits-cardContent-id"
|
||||
>
|
||||
<label
|
||||
class="MuiFormControlLabel-root-id"
|
||||
|
|
Loading…
Reference in a new issue