Merge pull request #207 from mirumee/fix/sales

Fix discounts
This commit is contained in:
Marcin Gębala 2019-10-16 09:11:06 +02:00 committed by GitHub
commit 47dc872eeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 2259 additions and 1237 deletions

View file

@ -36,3 +36,4 @@ All notable, unreleased changes to this project will be documented in this file.
- Add git hooks - #209 by @dominik-zeglen - Add git hooks - #209 by @dominik-zeglen
- Do not send customer invitation email - #211 by @dominik-zeglen - Do not send customer invitation email - #211 by @dominik-zeglen
- Send address update mutation only once - #210 by @dominik-zeglen - Send address update mutation only once - #210 by @dominik-zeglen
- Update sale details design - #207 by @dominik-zeglen

View file

@ -1,6 +1,6 @@
msgid "" msgid ""
msgstr "" msgstr ""
"POT-Creation-Date: 2019-10-09T15:30:47.333Z\n" "POT-Creation-Date: 2019-10-15T15:56:00.137Z\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -99,6 +99,14 @@ msgctxt "staff member status"
msgid "Active" msgid "Active"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/DiscountDates/DiscountDates.json
#. [src.discounts.components.DiscountDates.1662220323] - time during discount is active, header
#. defaultMessage is:
#. Active Dates
msgctxt "time during discount is active, header"
msgid "Active Dates"
msgstr ""
#: build/locale/src/discounts/components/VoucherDates/VoucherDates.json #: build/locale/src/discounts/components/VoucherDates/VoucherDates.json
#. [src.discounts.components.VoucherDates.1662220323] - time during voucher is active, header #. [src.discounts.components.VoucherDates.1662220323] - time during voucher is active, header
#. defaultMessage is: #. defaultMessage is:
@ -3235,12 +3243,12 @@ msgctxt "description"
msgid "Discount Code" msgid "Discount Code"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/VoucherValue/VoucherValue.json #: build/locale/src/discounts/components/SaleType/SaleType.json
#. [src.discounts.components.VoucherValue.1971417066] #. [src.discounts.components.SaleType.3216816841] - percentage or fixed, header
#. defaultMessage is: #. defaultMessage is:
#. Discount Specific Information #. Discount Type
msgctxt "description" msgctxt "percentage or fixed, header"
msgid "Discount Specific Information" msgid "Discount Type"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/VoucherTypes/VoucherTypes.json #: build/locale/src/discounts/components/VoucherTypes/VoucherTypes.json
@ -3251,10 +3259,14 @@ msgctxt "header"
msgid "Discount Type" msgid "Discount Type"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/SalePricing/SalePricing.json #: build/locale/src/discounts/components/SaleValue/SaleValue.json
#. [src.discounts.components.SalePricing.1205967018] #. [src.discounts.components.SaleValue.1205967018] - sale discount
#. defaultMessage is: #. defaultMessage is:
#. Discount Value #. Discount Value
msgctxt "sale discount"
msgid "Discount Value"
msgstr ""
#: build/locale/src/discounts/components/VoucherValue/VoucherValue.json #: build/locale/src/discounts/components/VoucherValue/VoucherValue.json
#. [src.discounts.components.VoucherValue.1205967018] #. [src.discounts.components.VoucherValue.1205967018]
#. defaultMessage is: #. defaultMessage is:
@ -3659,6 +3671,14 @@ msgctxt "description"
msgid "First Name" msgid "First Name"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/SaleType/SaleType.json
#. [src.discounts.components.SaleType.46415128] - discount type
#. defaultMessage is:
#. Fixed Amount
msgctxt "discount type"
msgid "Fixed Amount"
msgstr ""
#: build/locale/src/discounts/components/VoucherTypes/VoucherTypes.json #: build/locale/src/discounts/components/VoucherTypes/VoucherTypes.json
#. [src.discounts.components.VoucherTypes.46415128] - voucher discount type #. [src.discounts.components.VoucherTypes.46415128] - voucher discount type
#. defaultMessage is: #. defaultMessage is:
@ -5607,6 +5627,14 @@ msgctxt "order history message"
msgid "Payment was voided" msgid "Payment was voided"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/SaleType/SaleType.json
#. [src.discounts.components.SaleType.3688224049] - discount type
#. defaultMessage is:
#. Percentage
msgctxt "discount type"
msgid "Percentage"
msgstr ""
#: build/locale/src/discounts/components/VoucherTypes/VoucherTypes.json #: build/locale/src/discounts/components/VoucherTypes/VoucherTypes.json
#. [src.discounts.components.VoucherTypes.3688224049] - voucher discount type #. [src.discounts.components.VoucherTypes.3688224049] - voucher discount type
#. defaultMessage is: #. defaultMessage is:
@ -5875,14 +5903,6 @@ msgctxt "variant creation step"
msgid "Prices and SKU" msgid "Prices and SKU"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/SalePricing/SalePricing.json
#. [src.discounts.components.SalePricing.1099355007] - sale pricing, header
#. defaultMessage is:
#. Pricing
msgctxt "sale pricing, header"
msgid "Pricing"
msgstr ""
#: build/locale/src/products/components/ProductPricing/ProductPricing.json #: build/locale/src/products/components/ProductPricing/ProductPricing.json
#. [src.products.components.ProductPricing.1099355007] - product pricing #. [src.products.components.ProductPricing.1099355007] - product pricing
#. defaultMessage is: #. defaultMessage is:
@ -7091,6 +7111,10 @@ msgctxt "button"
msgid "Set as default shipping address" msgid "Set as default shipping address"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/DiscountDates/DiscountDates.json
#. [src.discounts.components.DiscountDates.1596226028] - voucher end date, switch button
#. defaultMessage is:
#. Set end date
#: build/locale/src/discounts/components/VoucherDates/VoucherDates.json #: build/locale/src/discounts/components/VoucherDates/VoucherDates.json
#. [src.discounts.components.VoucherDates.1596226028] - voucher end date, switch button #. [src.discounts.components.VoucherDates.1596226028] - voucher end date, switch button
#. defaultMessage is: #. defaultMessage is:
@ -7879,14 +7903,6 @@ msgctxt "description"
msgid "This will be shown to customers at checkout" msgid "This will be shown to customers at checkout"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/SalePricing/SalePricing.json
#. [src.discounts.components.SalePricing.2503204759] - time during which sale is active
#. defaultMessage is:
#. Time Frame
msgctxt "time during which sale is active"
msgid "Time Frame"
msgstr ""
#: build/locale/src/pages/components/PageInfo/PageInfo.json #: build/locale/src/pages/components/PageInfo/PageInfo.json
#. [src.pages.components.PageInfo.1124600214] - page title #. [src.pages.components.PageInfo.1124600214] - page title
#. defaultMessage is: #. defaultMessage is:
@ -8555,6 +8571,14 @@ msgctxt "sale value"
msgid "Value" msgid "Value"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/SaleValue/SaleValue.json
#. [src.discounts.components.SaleValue.1148029984] - sale value, header
#. defaultMessage is:
#. Value
msgctxt "sale value, header"
msgid "Value"
msgstr ""
#: build/locale/src/discounts/components/VoucherList/VoucherList.json #: build/locale/src/discounts/components/VoucherList/VoucherList.json
#. [src.discounts.components.VoucherList.1148029984] - voucher value #. [src.discounts.components.VoucherList.1148029984] - voucher value
#. defaultMessage is: #. defaultMessage is:
@ -8787,6 +8811,14 @@ msgctxt "description"
msgid "Voucher Name" msgid "Voucher Name"
msgstr "" msgstr ""
#: build/locale/src/discounts/components/VoucherValue/VoucherValue.json
#. [src.discounts.components.VoucherValue.1960678372]
#. defaultMessage is:
#. Voucher Specific Information
msgctxt "description"
msgid "Voucher Specific Information"
msgstr ""
#: build/locale/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.json #: build/locale/src/discounts/components/VoucherDetailsPage/VoucherDetailsPage.json
#. [src.discounts.components.VoucherDetailsPage.2071139683] #. [src.discounts.components.VoucherDetailsPage.2071139683]
#. defaultMessage is: #. defaultMessage is:

View file

@ -5,28 +5,39 @@ import FormLabel from "@material-ui/core/FormLabel";
import MenuItem from "@material-ui/core/MenuItem"; import MenuItem from "@material-ui/core/MenuItem";
import Radio from "@material-ui/core/Radio"; import Radio from "@material-ui/core/Radio";
import RadioGroup from "@material-ui/core/RadioGroup"; import RadioGroup from "@material-ui/core/RadioGroup";
import { createStyles, withStyles, WithStyles } from "@material-ui/core/styles"; import { Theme } from "@material-ui/core/styles";
import { makeStyles } from "@material-ui/styles";
import classNames from "classnames"; import classNames from "classnames";
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
const styles = createStyles({ const useStyles = makeStyles(
formControl: { (theme: Theme) => ({
padding: 0, formLabel: {
width: "100%" marginBottom: theme.spacing.unit
}, },
formLabel: { radioLabel: {
marginLeft: "-5px", marginBottom: -theme.spacing.unit * 1.5
paddingBottom: "10px" },
}, root: {
radioLabel: { "& $radioLabel": {
"& > span": { "&:last-of-type": {
padding: "6px" marginBottom: 0
}
},
padding: 0,
width: "100%"
},
rootNoLabel: {
marginTop: -theme.spacing.unit * 1.5
} }
}),
{
name: "RadioGroupField"
} }
}); );
interface RadioGroupFieldChoice { export interface RadioGroupFieldChoice {
value: string; value: string;
label: React.ReactNode; label: React.ReactNode;
} }
@ -39,16 +50,13 @@ interface RadioGroupFieldProps {
hint?: string; hint?: string;
label?: string; label?: string;
name?: string; name?: string;
value?: string; value: string;
onChange: (event: React.ChangeEvent<any>) => void; onChange: (event: React.ChangeEvent<any>) => void;
} }
export const RadioGroupField = withStyles(styles, { export const RadioGroupField: React.FC<RadioGroupFieldProps> = props => {
name: "RadioGroupField" const {
})(
({
className, className,
classes,
disabled, disabled,
error, error,
label, label,
@ -57,42 +65,45 @@ export const RadioGroupField = withStyles(styles, {
onChange, onChange,
name, name,
hint hint
}: RadioGroupFieldProps & WithStyles<typeof styles>) => { } = props;
return ( const classes = useStyles(props);
<FormControl
className={classNames(classes.formControl, className)} return (
error={error} <FormControl
disabled={disabled} className={classNames(classes.root, className, {
[classes.rootNoLabel]: !label
})}
error={error}
disabled={disabled}
>
{label ? (
<FormLabel className={classes.formLabel}>{label}</FormLabel>
) : null}
<RadioGroup
aria-label={name}
name={name}
value={value}
onChange={onChange}
> >
{label ? ( {choices.length > 0 ? (
<FormLabel className={classes.formLabel}>{label}</FormLabel> choices.map(choice => (
) : null} <FormControlLabel
<RadioGroup value={choice.value}
aria-label={name} className={classes.radioLabel}
name={name} control={<Radio color="primary" />}
value={value} label={choice.label}
onChange={onChange} key={choice.value}
> />
{choices.length > 0 ? ( ))
choices.map(choice => ( ) : (
<FormControlLabel <MenuItem disabled={true}>
value={choice.value} <FormattedMessage defaultMessage="No results found" />
className={classes.radioLabel} </MenuItem>
control={<Radio color="primary" />} )}
label={choice.label} </RadioGroup>
key={choice.value} {hint && <FormHelperText>{hint}</FormHelperText>}
/> </FormControl>
)) );
) : ( };
<MenuItem disabled={true}>
<FormattedMessage defaultMessage="No results found" />
</MenuItem>
)}
</RadioGroup>
{hint && <FormHelperText>{hint}</FormHelperText>}
</FormControl>
);
}
);
RadioGroupField.displayName = "RadioGroupField"; RadioGroupField.displayName = "RadioGroupField";
export default RadioGroupField; export default RadioGroupField;

View file

@ -0,0 +1,119 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { ControlledCheckbox } from "@saleor/components/ControlledCheckbox";
import Grid from "@saleor/components/Grid";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types";
interface DiscountDatesProps {
data: {
endDate: string;
endTime: string;
hasEndDate: boolean;
startDate: string;
startTime: string;
};
defaultCurrency: string;
disabled: boolean;
errors: FormErrors<"endDate" | "startDate">;
onChange: (event: React.ChangeEvent<any>) => void;
}
const DiscountDates = ({
data,
disabled,
errors,
onChange
}: DiscountDatesProps) => {
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Active Dates",
description: "time during discount is active, header"
})}
/>
<CardContent>
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
name={"startDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startDate)}
value={data.startDate}
type="date"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
name={"startTime" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startHour)}
value={data.startTime}
type="time"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
</Grid>
<ControlledCheckbox
checked={data.hasEndDate}
label={intl.formatMessage({
defaultMessage: "Set end date",
description: "voucher end date, switch button"
})}
name={"hasEndDate" as keyof FormData}
onChange={onChange}
/>
{data.hasEndDate && (
<Grid variant="uniform">
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
name={"endDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endDate)}
value={data.endDate}
type="date"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
name={"endTime" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endHour)}
value={data.endTime}
type="time"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
</Grid>
)}
</CardContent>
</Card>
);
};
export default DiscountDates;

View file

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

View file

@ -11,16 +11,20 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { UserError } from "../../../types"; import { UserError } from "../../../types";
import { SaleType } from "../../../types/globalTypes"; import { SaleType as SaleTypeEnum } from "../../../types/globalTypes";
import DiscountDates from "../DiscountDates";
import SaleInfo from "../SaleInfo"; import SaleInfo from "../SaleInfo";
import SalePricing from "../SalePricing"; import SaleType from "../SaleType";
export interface FormData { export interface FormData {
endDate: string;
endTime: string;
hasEndDate: boolean;
name: string; name: string;
startDate: string; startDate: string;
endDate: string; startTime: string;
type: SaleTypeEnum;
value: string; value: string;
type: SaleType;
} }
export interface SaleCreatePageProps { export interface SaleCreatePageProps {
@ -44,9 +48,12 @@ const SaleCreatePage: React.StatelessComponent<SaleCreatePageProps> = ({
const initialForm: FormData = { const initialForm: FormData = {
endDate: "", endDate: "",
endTime: "",
hasEndDate: false,
name: "", name: "",
startDate: "", startDate: "",
type: SaleType.FIXED, startTime: "",
type: SaleTypeEnum.FIXED,
value: "" value: ""
}; };
return ( return (
@ -71,10 +78,12 @@ const SaleCreatePage: React.StatelessComponent<SaleCreatePageProps> = ({
onChange={change} onChange={change}
/> />
<CardSpacer /> <CardSpacer />
<SalePricing <SaleType data={data} disabled={disabled} onChange={change} />
<CardSpacer />
<DiscountDates
data={data} data={data}
defaultCurrency={defaultCurrency}
disabled={disabled} disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors} errors={formErrors}
onChange={change} onChange={change}
/> />

View file

@ -11,23 +11,28 @@ import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar"; import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { Tab, TabContainer } from "@saleor/components/Tab"; import { Tab, TabContainer } from "@saleor/components/Tab";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { maybe } from "../../../misc"; import { maybe, splitDateTime } from "../../../misc";
import { ListProps, TabListActions, UserError } from "../../../types"; import { ListProps, TabListActions, UserError } from "../../../types";
import { SaleType } from "../../../types/globalTypes"; import { SaleType as SaleTypeEnum } from "../../../types/globalTypes";
import { SaleDetails_sale } from "../../types/SaleDetails"; import { SaleDetails_sale } from "../../types/SaleDetails";
import DiscountCategories from "../DiscountCategories"; import DiscountCategories from "../DiscountCategories";
import DiscountCollections from "../DiscountCollections"; import DiscountCollections from "../DiscountCollections";
import DiscountDates from "../DiscountDates";
import DiscountProducts from "../DiscountProducts"; import DiscountProducts from "../DiscountProducts";
import SaleInfo from "../SaleInfo"; import SaleInfo from "../SaleInfo";
import SalePricing from "../SalePricing";
import SaleSummary from "../SaleSummary"; import SaleSummary from "../SaleSummary";
import SaleType from "../SaleType";
import SaleValue from "../SaleValue";
export interface FormData { export interface FormData {
endDate: string;
endTime: string;
hasEndDate: boolean;
name: string; name: string;
startDate: string; startDate: string;
endDate: string; startTime: string;
type: SaleTypeEnum;
value: string; value: string;
type: SaleType;
} }
export enum SaleDetailsPageTab { export enum SaleDetailsPageTab {
@ -106,10 +111,13 @@ const SaleDetailsPage: React.StatelessComponent<SaleDetailsPageProps> = ({
const intl = useIntl(); const intl = useIntl();
const initialForm: FormData = { const initialForm: FormData = {
endDate: maybe(() => (sale.endDate ? sale.endDate : ""), ""), endDate: splitDateTime(maybe(() => sale.endDate, "")).date,
endTime: splitDateTime(maybe(() => sale.endDate, "")).time,
hasEndDate: maybe(() => !!sale.endDate),
name: maybe(() => sale.name, ""), name: maybe(() => sale.name, ""),
startDate: maybe(() => sale.startDate, ""), startDate: splitDateTime(maybe(() => sale.startDate, "")).date,
type: maybe(() => sale.type, SaleType.FIXED), startTime: splitDateTime(maybe(() => sale.startDate, "")).time,
type: maybe(() => sale.type, SaleTypeEnum.FIXED),
value: maybe(() => sale.value.toString(), "") value: maybe(() => sale.value.toString(), "")
}; };
return ( return (
@ -129,9 +137,11 @@ const SaleDetailsPage: React.StatelessComponent<SaleDetailsPageProps> = ({
onChange={change} onChange={change}
/> />
<CardSpacer /> <CardSpacer />
<SalePricing <SaleType data={data} disabled={disabled} onChange={change} />
<CardSpacer />
<SaleValue
currencySymbol={defaultCurrency}
data={data} data={data}
defaultCurrency={defaultCurrency}
disabled={disabled} disabled={disabled}
errors={formErrors} errors={formErrors}
onChange={change} onChange={change}
@ -243,6 +253,14 @@ const SaleDetailsPage: React.StatelessComponent<SaleDetailsPageProps> = ({
toolbar={productListToolbar} toolbar={productListToolbar}
/> />
)} )}
<CardSpacer />
<DiscountDates
data={data}
disabled={disabled}
defaultCurrency={defaultCurrency}
errors={formErrors}
onChange={change}
/>
</div> </div>
<div> <div>
<SaleSummary defaultCurrency={defaultCurrency} sale={sale} /> <SaleSummary defaultCurrency={defaultCurrency} sale={sale} />

View file

@ -1,138 +0,0 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import {
createStyles,
Theme,
WithStyles,
withStyles
} 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 CardTitle from "@saleor/components/CardTitle";
import Hr from "@saleor/components/Hr";
import TextFieldWithChoice from "@saleor/components/TextFieldWithChoice";
import { commonMessages } from "@saleor/intl";
import { FormErrors } from "../../../types";
import { SaleType } from "../../../types/globalTypes";
import { FormData } from "../SaleDetailsPage";
export interface SalePricingProps {
data: FormData;
defaultCurrency: string;
disabled: boolean;
errors: FormErrors<"startDate" | "endDate" | "value">;
onChange: (event: React.ChangeEvent<any>) => void;
}
const styles = (theme: Theme) =>
createStyles({
root: {
display: "grid",
gridColumnGap: theme.spacing.unit * 2 + "px",
gridTemplateColumns: "1fr 1fr"
},
subheading: {
gridColumnEnd: "span 2",
marginBottom: theme.spacing.unit * 2
}
});
const SalePricing = withStyles(styles, {
name: "SalePricing"
})(
({
classes,
data,
defaultCurrency,
disabled,
errors,
onChange
}: SalePricingProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Pricing",
description: "sale pricing, header"
})}
/>
<CardContent className={classes.root}>
<TextFieldWithChoice
disabled={disabled}
ChoiceProps={{
label: data.type === SaleType.FIXED ? defaultCurrency : "%",
name: "type",
values: [
{
label: defaultCurrency,
value: SaleType.FIXED
},
{
label: "%",
value: SaleType.PERCENTAGE
}
]
}}
error={!!errors.value}
helperText={errors.value}
name={"value" as keyof FormData}
onChange={onChange}
label={intl.formatMessage({
defaultMessage: "Discount Value"
})}
value={data.value}
type="number"
fullWidth
inputProps={{
min: 0
}}
/>
</CardContent>
<Hr />
<CardContent className={classes.root}>
<Typography className={classes.subheading} variant="subtitle1">
<FormattedMessage
defaultMessage="Time Frame"
description="time during which sale is active"
/>
</Typography>
<TextField
disabled={disabled}
error={!!errors.startDate}
helperText={errors.startDate}
name={"startDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.startDate)}
value={data.startDate}
type="date"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
<TextField
disabled={disabled}
error={!!errors.endDate}
helperText={errors.endDate}
name={"endDate" as keyof FormData}
onChange={onChange}
label={intl.formatMessage(commonMessages.endDate)}
value={data.endDate}
type="date"
InputLabelProps={{
shrink: true
}}
fullWidth
/>
</CardContent>
</Card>
);
}
);
SalePricing.displayName = "SalePricing";
export default SalePricing;

View file

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

View file

@ -0,0 +1,84 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import { Theme } from "@material-ui/core/styles";
import { makeStyles } from "@material-ui/styles";
import React from "react";
import { IntlShape, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import RadioGroupField, {
RadioGroupFieldChoice
} from "@saleor/components/RadioGroupField";
import { FormChange } from "@saleor/hooks/useForm";
import { SaleType as SaleTypeEnum } from "@saleor/types/globalTypes";
import { FormData } from "../SaleDetailsPage";
export interface SaleTypeProps {
data: FormData;
disabled: boolean;
onChange: FormChange;
}
const useStyles = makeStyles(
(theme: Theme) => ({
root: {
"&&": {
paddingBottom: theme.spacing.unit * 1.5
}
}
}),
{
name: "SaleType"
}
);
function createChoices(intl: IntlShape): RadioGroupFieldChoice[] {
return [
{
label: intl.formatMessage({
defaultMessage: "Percentage",
description: "discount type"
}),
value: SaleTypeEnum.PERCENTAGE
},
{
label: intl.formatMessage({
defaultMessage: "Fixed Amount",
description: "discount type"
}),
value: SaleTypeEnum.FIXED
}
];
}
const SaleType: React.FC<SaleTypeProps> = props => {
const { data, disabled, onChange } = props;
const classes = useStyles(props);
const intl = useIntl();
const choices = createChoices(intl);
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Discount Type",
description: "percentage or fixed, header"
})}
/>
<CardContent className={classes.root}>
<RadioGroupField
choices={choices}
disabled={disabled}
name={"type" as keyof FormData}
value={data.type}
onChange={onChange}
/>
</CardContent>
</Card>
);
};
SaleType.displayName = "SaleType";
export default SaleType;

View file

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

View file

@ -0,0 +1,61 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import React from "react";
import { useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import { FormChange } from "@saleor/hooks/useForm";
import { FormErrors } from "@saleor/types";
import { SaleType } from "@saleor/types/globalTypes";
import { FormData } from "../SaleDetailsPage";
export interface SaleValueProps {
currencySymbol: string;
data: FormData;
disabled: boolean;
errors: FormErrors<"value">;
onChange: FormChange;
}
const SaleValue: React.FC<SaleValueProps> = ({
currencySymbol,
data,
disabled,
errors,
onChange
}) => {
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Value",
description: "sale value, header"
})}
/>
<CardContent>
<TextField
disabled={disabled}
fullWidth
label={intl.formatMessage({
defaultMessage: "Discount Value",
description: "sale discount"
})}
error={!!errors.value}
name="value"
InputProps={{
endAdornment: data.type === SaleType.FIXED ? currencySymbol : "%"
}}
helperText={errors.value}
value={data.value}
onChange={onChange}
/>
</CardContent>
</Card>
);
};
SaleValue.displayName = "SaleValue";
export default SaleValue;

View file

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

View file

@ -23,8 +23,8 @@ import {
import { VoucherDetails_voucher } from "../../types/VoucherDetails"; import { VoucherDetails_voucher } from "../../types/VoucherDetails";
import DiscountCategories from "../DiscountCategories"; import DiscountCategories from "../DiscountCategories";
import DiscountCollections from "../DiscountCollections"; import DiscountCollections from "../DiscountCollections";
import DiscountDates from "../DiscountDates";
import DiscountProducts from "../DiscountProducts"; import DiscountProducts from "../DiscountProducts";
import VoucherDates from "../VoucherDates";
import VoucherInfo from "../VoucherInfo"; import VoucherInfo from "../VoucherInfo";
import VoucherLimits from "../VoucherLimits"; import VoucherLimits from "../VoucherLimits";
import VoucherRequirements from "../VoucherRequirements"; import VoucherRequirements from "../VoucherRequirements";
@ -349,7 +349,7 @@ const VoucherDetailsPage: React.StatelessComponent<VoucherDetailsPageProps> = ({
onChange={change} onChange={change}
/> />
<CardSpacer /> <CardSpacer />
<VoucherDates <DiscountDates
data={data} data={data}
disabled={disabled} disabled={disabled}
defaultCurrency={defaultCurrency} defaultCurrency={defaultCurrency}

View file

@ -70,7 +70,9 @@ const VoucherRequirements = ({
value={data.requirementsPicker} value={data.requirementsPicker}
onChange={onChange} onChange={onChange}
/> />
<FormSpacer /> {[RequirementsPicker.ORDER, RequirementsPicker.ITEM].includes(
data.requirementsPicker
) && <FormSpacer />}
{data.requirementsPicker === RequirementsPicker.ORDER ? ( {data.requirementsPicker === RequirementsPicker.ORDER ? (
<TextField <TextField
disabled={disabled} disabled={disabled}

View file

@ -1,6 +1,8 @@
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent"; import CardContent from "@material-ui/core/CardContent";
import { Theme } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/styles";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
@ -29,14 +31,21 @@ export enum VoucherType {
SPECIFIC_PRODUCT = "SPECIFIC_PRODUCT" SPECIFIC_PRODUCT = "SPECIFIC_PRODUCT"
} }
const VoucherValue = ({ const useStyles = makeStyles(
data, (theme: Theme) => ({
defaultCurrency, hr: {
disabled, margin: `${theme.spacing.unit * 2}px 0`
errors, }
variant, }),
onChange {
}: VoucherValueProps) => { name: "VoucherValue"
}
);
const VoucherValue: React.FC<VoucherValueProps> = props => {
const { data, defaultCurrency, disabled, errors, variant, onChange } = props;
const classes = useStyles(props);
const intl = useIntl(); const intl = useIntl();
const translatedVoucherTypes = translateVoucherTypes(intl); const translatedVoucherTypes = translateVoucherTypes(intl);
@ -81,22 +90,22 @@ const VoucherValue = ({
<FormSpacer /> <FormSpacer />
{variant === "update" && ( {variant === "update" && (
<> <>
<Hr className={classes.hr} />
<RadioGroupField <RadioGroupField
choices={voucherTypeChoices} choices={voucherTypeChoices}
disabled={disabled} disabled={disabled}
error={!!errors.type} error={!!errors.type}
hint={errors.type} hint={errors.type}
label={intl.formatMessage({ label={intl.formatMessage({
defaultMessage: "Discount Specific Information" defaultMessage: "Voucher Specific Information"
})} })}
name={"type" as keyof FormData} name={"type" as keyof FormData}
value={data.type} value={data.type}
onChange={onChange} onChange={onChange}
/> />
<FormSpacer />
</> </>
)} )}
<Hr /> <Hr className={classes.hr} />
<FormSpacer /> <FormSpacer />
<ControlledCheckbox <ControlledCheckbox
name={"applyOncePerOrder" as keyof FormData} name={"applyOncePerOrder" as keyof FormData}

View file

@ -22,7 +22,7 @@ import { DEFAULT_INITIAL_SEARCH_DATA, PAGINATE_BY } from "../../config";
import SearchCategories from "../../containers/SearchCategories"; import SearchCategories from "../../containers/SearchCategories";
import SearchCollections from "../../containers/SearchCollections"; import SearchCollections from "../../containers/SearchCollections";
import SearchProducts from "../../containers/SearchProducts"; import SearchProducts from "../../containers/SearchProducts";
import { decimal, getMutationState, maybe } from "../../misc"; import { decimal, getMutationState, joinDateTime, maybe } from "../../misc";
import { productUrl } from "../../products/urls"; import { productUrl } from "../../products/urls";
import { DiscountValueTypeEnum, SaleType } from "../../types/globalTypes"; import { DiscountValueTypeEnum, SaleType } from "../../types/globalTypes";
import SaleDetailsPage, { import SaleDetailsPage, {
@ -273,15 +273,17 @@ export const SaleDetails: React.StatelessComponent<SaleDetailsProps> = ({
variables: { variables: {
id, id,
input: { input: {
endDate: endDate: formData.hasEndDate
formData.endDate === "" ? joinDateTime(
? null formData.endDate,
: formData.endDate, formData.endTime
)
: null,
name: formData.name, name: formData.name,
startDate: startDate: joinDateTime(
formData.startDate === "" formData.startDate,
? null formData.startTime
: formData.startDate, ),
type: discountValueTypeEnum( type: discountValueTypeEnum(
formData.type formData.type
), ),

View file

@ -162,7 +162,7 @@ const ProductVariantCreatePrices: React.FC<
<Hr className={classes.hrAttribute} /> <Hr className={classes.hrAttribute} />
{priceAttributeValues && {priceAttributeValues &&
priceAttributeValues.map(attributeValue => ( priceAttributeValues.map(attributeValue => (
<> <React.Fragment key={attributeValue.id}>
<FormSpacer /> <FormSpacer />
<Grid variant="uniform"> <Grid variant="uniform">
<div className={classes.label}> <div className={classes.label}>
@ -198,7 +198,7 @@ const ProductVariantCreatePrices: React.FC<
/> />
</div> </div>
</Grid> </Grid>
</> </React.Fragment>
))} ))}
</> </>
)} )}
@ -272,7 +272,7 @@ const ProductVariantCreatePrices: React.FC<
<Hr className={classes.hrAttribute} /> <Hr className={classes.hrAttribute} />
{stockAttributeValues && {stockAttributeValues &&
stockAttributeValues.map(attributeValue => ( stockAttributeValues.map(attributeValue => (
<> <React.Fragment key={attributeValue.id}>
<FormSpacer /> <FormSpacer />
<Grid variant="uniform"> <Grid variant="uniform">
<div className={classes.label}> <div className={classes.label}>
@ -301,7 +301,7 @@ const ProductVariantCreatePrices: React.FC<
/> />
</div> </div>
</Grid> </Grid>
</> </React.Fragment>
))} ))}
</> </>
)} )}

View file

@ -200,6 +200,7 @@ const ProductVariantCreateSummary: React.FC<
style={{ style={{
color: colors[valueIndex % colors.length] color: colors[valueIndex % colors.length]
}} }}
key={value}
> >
{value} {value}
</span> </span>

File diff suppressed because it is too large Load diff

View file

@ -106,7 +106,7 @@ export default (colors: IThemeColors): Theme =>
MuiFormControlLabel: { MuiFormControlLabel: {
root: { root: {
display: "grid", display: "grid",
gridTemplateColumns: "50px 6fr" gridTemplateColumns: "48px 1fr"
} }
}, },
MuiFormLabel: { MuiFormLabel: {
@ -453,7 +453,8 @@ export default (colors: IThemeColors): Theme =>
ripple: { ripple: {
"&$rippleVisible": { "&$rippleVisible": {
backgroundColor: fade(colors.primary, 0.2) backgroundColor: fade(colors.primary, 0.2)
} },
borderRadius: "100%"
} }
} }
}, },