Add error handling
This commit is contained in:
parent
6190c4ff45
commit
7bb26784e6
26 changed files with 1701 additions and 946 deletions
|
@ -60,6 +60,7 @@ export interface ProductUpdatePageProps extends ListActions {
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
fetchCategories: (query: string) => void;
|
fetchCategories: (query: string) => void;
|
||||||
fetchCollections: (query: string) => void;
|
fetchCollections: (query: string) => void;
|
||||||
|
onVariantsAdd: () => void;
|
||||||
onVariantShow: (id: string) => () => void;
|
onVariantShow: (id: string) => () => void;
|
||||||
onImageDelete: (id: string) => () => void;
|
onImageDelete: (id: string) => () => void;
|
||||||
onBack?();
|
onBack?();
|
||||||
|
@ -100,6 +101,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
onSeoClick,
|
onSeoClick,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onVariantAdd,
|
onVariantAdd,
|
||||||
|
onVariantsAdd,
|
||||||
onVariantShow,
|
onVariantShow,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
|
@ -236,6 +238,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
|
||||||
fallbackPrice={product ? product.basePrice : undefined}
|
fallbackPrice={product ? product.basePrice : undefined}
|
||||||
onRowClick={onVariantShow}
|
onRowClick={onVariantShow}
|
||||||
onVariantAdd={onVariantAdd}
|
onVariantAdd={onVariantAdd}
|
||||||
|
onVariantsAdd={onVariantsAdd}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
isChecked={isChecked}
|
isChecked={isChecked}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { storiesOf } from "@storybook/react";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { attributes } from "@saleor/attributes/fixtures";
|
import { attributes } from "@saleor/attributes/fixtures";
|
||||||
|
import { ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors } from "@saleor/products/types/ProductVariantBulkCreate";
|
||||||
|
import { ProductErrorCode } from "@saleor/types/globalTypes";
|
||||||
import Decorator from "../../../storybook/Decorator";
|
import Decorator from "../../../storybook/Decorator";
|
||||||
import { createVariants } from "./createVariants";
|
import { createVariants } from "./createVariants";
|
||||||
import { AllOrAttribute } from "./form";
|
import { AllOrAttribute } from "./form";
|
||||||
|
@ -19,7 +21,7 @@ const price: AllOrAttribute = {
|
||||||
attribute: selectedAttributes[1].id,
|
attribute: selectedAttributes[1].id,
|
||||||
value: "2.79",
|
value: "2.79",
|
||||||
values: selectedAttributes[1].values.map((attribute, attributeIndex) => ({
|
values: selectedAttributes[1].values.map((attribute, attributeIndex) => ({
|
||||||
id: attribute.id,
|
slug: attribute.slug,
|
||||||
value: (attributeIndex + 4).toFixed(2)
|
value: (attributeIndex + 4).toFixed(2)
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
@ -29,7 +31,7 @@ const stock: AllOrAttribute = {
|
||||||
attribute: selectedAttributes[1].id,
|
attribute: selectedAttributes[1].id,
|
||||||
value: "8",
|
value: "8",
|
||||||
values: selectedAttributes[1].values.map((attribute, attributeIndex) => ({
|
values: selectedAttributes[1].values.map((attribute, attributeIndex) => ({
|
||||||
id: attribute.id,
|
slug: attribute.slug,
|
||||||
value: (selectedAttributes.length * 10 - attributeIndex).toString()
|
value: (selectedAttributes.length * 10 - attributeIndex).toString()
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
@ -37,10 +39,20 @@ const stock: AllOrAttribute = {
|
||||||
const dataAttributes = selectedAttributes.map(attribute => ({
|
const dataAttributes = selectedAttributes.map(attribute => ({
|
||||||
id: attribute.id,
|
id: attribute.id,
|
||||||
values: attribute.values
|
values: attribute.values
|
||||||
.map(value => value.id)
|
.map(value => value.slug)
|
||||||
.filter((_, valueIndex) => valueIndex % 2 !== 1)
|
.filter((_, valueIndex) => valueIndex % 2 !== 1)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const errors: ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors[] = [
|
||||||
|
{
|
||||||
|
__typename: "BulkProductError",
|
||||||
|
code: ProductErrorCode.UNIQUE,
|
||||||
|
field: "sku",
|
||||||
|
index: 3,
|
||||||
|
message: "Duplicated SKU."
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const props: ProductVariantCreateContentProps = {
|
const props: ProductVariantCreateContentProps = {
|
||||||
attributes,
|
attributes,
|
||||||
currencySymbol: "USD",
|
currencySymbol: "USD",
|
||||||
|
@ -56,6 +68,7 @@ const props: ProductVariantCreateContentProps = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
dispatchFormDataAction: () => undefined,
|
dispatchFormDataAction: () => undefined,
|
||||||
|
errors: [],
|
||||||
step: "attributes"
|
step: "attributes"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,9 +91,26 @@ storiesOf("Views / Products / Create multiple variants", module)
|
||||||
))
|
))
|
||||||
.add("prices and SKU", () => (
|
.add("prices and SKU", () => (
|
||||||
<ProductVariantCreateContent {...props} step="prices" />
|
<ProductVariantCreateContent {...props} step="prices" />
|
||||||
|
));
|
||||||
|
|
||||||
|
storiesOf("Views / Products / Create multiple variants / summary", module)
|
||||||
|
.addDecorator(storyFn => (
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
margin: "auto",
|
||||||
|
overflow: "visible",
|
||||||
|
width: 800
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent>{storyFn()}</CardContent>
|
||||||
|
</Card>
|
||||||
))
|
))
|
||||||
.add("summary", () => (
|
.addDecorator(Decorator)
|
||||||
|
.add("default", () => (
|
||||||
<ProductVariantCreateContent {...props} step="summary" />
|
<ProductVariantCreateContent {...props} step="summary" />
|
||||||
|
))
|
||||||
|
.add("errors", () => (
|
||||||
|
<ProductVariantCreateContent {...props} step="summary" errors={errors} />
|
||||||
));
|
));
|
||||||
|
|
||||||
storiesOf("Views / Products / Create multiple variants", module)
|
storiesOf("Views / Products / Create multiple variants", module)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Theme } from "@material-ui/core/styles";
|
|
||||||
import Table from "@material-ui/core/Table";
|
import Table from "@material-ui/core/Table";
|
||||||
import TableBody from "@material-ui/core/TableBody";
|
import TableBody from "@material-ui/core/TableBody";
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
import TableCell from "@material-ui/core/TableCell";
|
||||||
|
@ -18,14 +17,14 @@ export interface ProductVariantCreateAttributesProps {
|
||||||
onAttributeClick: (id: string) => void;
|
onAttributeClick: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles({
|
||||||
checkboxCell: {
|
checkboxCell: {
|
||||||
paddingLeft: 0
|
paddingLeft: 0
|
||||||
},
|
},
|
||||||
wideCell: {
|
wideCell: {
|
||||||
width: "100%"
|
width: "100%"
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
|
||||||
const ProductVariantCreateAttributes: React.FC<
|
const ProductVariantCreateAttributes: React.FC<
|
||||||
ProductVariantCreateAttributesProps
|
ProductVariantCreateAttributesProps
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React from "react";
|
||||||
|
|
||||||
import { makeStyles } from "@material-ui/styles";
|
import { makeStyles } from "@material-ui/styles";
|
||||||
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
|
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
|
||||||
|
import { ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors } from "@saleor/products/types/ProductVariantBulkCreate";
|
||||||
import { isSelected } from "@saleor/utils/lists";
|
import { isSelected } from "@saleor/utils/lists";
|
||||||
import { ProductVariantCreateFormData } from "./form";
|
import { ProductVariantCreateFormData } from "./form";
|
||||||
import ProductVariantCreateAttributes from "./ProductVariantCreateAttributes";
|
import ProductVariantCreateAttributes from "./ProductVariantCreateAttributes";
|
||||||
|
@ -24,6 +25,7 @@ export interface ProductVariantCreateContentProps {
|
||||||
currencySymbol: string;
|
currencySymbol: string;
|
||||||
data: ProductVariantCreateFormData;
|
data: ProductVariantCreateFormData;
|
||||||
dispatchFormDataAction: React.Dispatch<ProductVariantCreateReducerAction>;
|
dispatchFormDataAction: React.Dispatch<ProductVariantCreateReducerAction>;
|
||||||
|
errors: ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors[];
|
||||||
step: ProductVariantCreateStep;
|
step: ProductVariantCreateStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ const ProductVariantCreateContent: React.FC<
|
||||||
currencySymbol,
|
currencySymbol,
|
||||||
data,
|
data,
|
||||||
dispatchFormDataAction,
|
dispatchFormDataAction,
|
||||||
|
errors,
|
||||||
step
|
step
|
||||||
} = props;
|
} = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
@ -121,6 +124,7 @@ const ProductVariantCreateContent: React.FC<
|
||||||
attributes={selectedAttributes}
|
attributes={selectedAttributes}
|
||||||
currencySymbol={currencySymbol}
|
currencySymbol={currencySymbol}
|
||||||
data={data}
|
data={data}
|
||||||
|
errors={errors}
|
||||||
onVariantDataChange={(variantIndex, field, value) =>
|
onVariantDataChange={(variantIndex, field, value) =>
|
||||||
dispatchFormDataAction({
|
dispatchFormDataAction({
|
||||||
field,
|
field,
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { Theme } from "@material-ui/core/styles";
|
||||||
import { makeStyles } from "@material-ui/styles";
|
import { makeStyles } from "@material-ui/styles";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
|
import { ProductVariantBulkCreateInput } from "../../../types/globalTypes";
|
||||||
import { initialForm, ProductVariantCreateFormData } from "./form";
|
import { initialForm, ProductVariantCreateFormData } from "./form";
|
||||||
import ProductVariantCreateContent, {
|
import ProductVariantCreateContent, {
|
||||||
ProductVariantCreateContentProps
|
ProductVariantCreateContentProps
|
||||||
|
@ -73,17 +75,17 @@ function canHitNext(
|
||||||
export interface ProductVariantCreateDialogProps
|
export interface ProductVariantCreateDialogProps
|
||||||
extends Omit<
|
extends Omit<
|
||||||
ProductVariantCreateContentProps,
|
ProductVariantCreateContentProps,
|
||||||
"dispatchFormDataAction" | "step"
|
"data" | "dispatchFormDataAction" | "step"
|
||||||
> {
|
> {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => undefined;
|
onClose: () => void;
|
||||||
onSubmit: (data: ProductVariantCreateFormData) => void;
|
onSubmit: (data: ProductVariantBulkCreateInput[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProductVariantCreateDialog: React.FC<
|
const ProductVariantCreateDialog: React.FC<
|
||||||
ProductVariantCreateDialogProps
|
ProductVariantCreateDialogProps
|
||||||
> = props => {
|
> = props => {
|
||||||
const { open, onClose, ...contentProps } = props;
|
const { open, onClose, onSubmit, ...contentProps } = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
const [step, setStep] = React.useState<ProductVariantCreateStep>(
|
const [step, setStep] = React.useState<ProductVariantCreateStep>(
|
||||||
"attributes"
|
"attributes"
|
||||||
|
@ -167,6 +169,7 @@ const ProductVariantCreateDialog: React.FC<
|
||||||
color="primary"
|
color="primary"
|
||||||
disabled={!canHitNext(step, data)}
|
disabled={!canHitNext(step, data)}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
onClick={() => onSubmit(data.variants)}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Create"
|
defaultMessage="Create"
|
||||||
|
|
|
@ -63,12 +63,24 @@ const ProductVariantCreatePrices: React.FC<
|
||||||
const priceAttributeValues = data.price.all
|
const priceAttributeValues = data.price.all
|
||||||
? null
|
? null
|
||||||
: data.price.attribute
|
: data.price.attribute
|
||||||
? attributes.find(attribute => attribute.id === data.price.attribute).values
|
? attributes
|
||||||
|
.find(attribute => attribute.id === data.price.attribute)
|
||||||
|
.values.filter(value =>
|
||||||
|
data.attributes
|
||||||
|
.find(attribute => attribute.id === data.price.attribute)
|
||||||
|
.values.includes(value.slug)
|
||||||
|
)
|
||||||
: [];
|
: [];
|
||||||
const stockAttributeValues = data.stock.all
|
const stockAttributeValues = data.stock.all
|
||||||
? null
|
? null
|
||||||
: data.stock.attribute
|
: data.stock.attribute
|
||||||
? attributes.find(attribute => attribute.id === data.stock.attribute).values
|
? attributes
|
||||||
|
.find(attribute => attribute.id === data.stock.attribute)
|
||||||
|
.values.filter(value =>
|
||||||
|
data.attributes
|
||||||
|
.find(attribute => attribute.id === data.stock.attribute)
|
||||||
|
.values.includes(value.slug)
|
||||||
|
)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -158,7 +170,7 @@ const ProductVariantCreatePrices: React.FC<
|
||||||
value={data.price.values[attributeValueIndex].value}
|
value={data.price.values[attributeValueIndex].value}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
onAttributeValueChange(
|
onAttributeValueChange(
|
||||||
attributeValue.id,
|
attributeValue.slug,
|
||||||
event.target.value,
|
event.target.value,
|
||||||
"price"
|
"price"
|
||||||
)
|
)
|
||||||
|
@ -256,7 +268,7 @@ const ProductVariantCreatePrices: React.FC<
|
||||||
value={data.stock.values[attributeValueIndex].value}
|
value={data.stock.values[attributeValueIndex].value}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
onAttributeValueChange(
|
onAttributeValueChange(
|
||||||
attributeValue.id,
|
attributeValue.slug,
|
||||||
event.target.value,
|
event.target.value,
|
||||||
"stock"
|
"stock"
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,11 +4,6 @@ import green from "@material-ui/core/colors/green";
|
||||||
import purple from "@material-ui/core/colors/purple";
|
import purple from "@material-ui/core/colors/purple";
|
||||||
import yellow from "@material-ui/core/colors/yellow";
|
import yellow from "@material-ui/core/colors/yellow";
|
||||||
import { Theme } from "@material-ui/core/styles";
|
import { Theme } from "@material-ui/core/styles";
|
||||||
import Table from "@material-ui/core/Table";
|
|
||||||
import TableBody from "@material-ui/core/TableBody";
|
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
|
||||||
import TableHead from "@material-ui/core/TableHead";
|
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { makeStyles } from "@material-ui/styles";
|
import { makeStyles } from "@material-ui/styles";
|
||||||
|
@ -17,7 +12,9 @@ import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import Hr from "@saleor/components/Hr";
|
import Hr from "@saleor/components/Hr";
|
||||||
import { ProductVariantCreateInput } from "@saleor/types/globalTypes";
|
import { maybe } from "@saleor/misc";
|
||||||
|
import { ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors } from "@saleor/products/types/ProductVariantBulkCreate";
|
||||||
|
import { ProductVariantBulkCreateInput } from "@saleor/types/globalTypes";
|
||||||
import { ProductDetails_product_productType_variantAttributes } from "../../types/ProductDetails";
|
import { ProductDetails_product_productType_variantAttributes } from "../../types/ProductDetails";
|
||||||
import { ProductVariantCreateFormData } from "./form";
|
import { ProductVariantCreateFormData } from "./form";
|
||||||
import { VariantField } from "./reducer";
|
import { VariantField } from "./reducer";
|
||||||
|
@ -26,6 +23,7 @@ export interface ProductVariantCreateSummaryProps {
|
||||||
attributes: ProductDetails_product_productType_variantAttributes[];
|
attributes: ProductDetails_product_productType_variantAttributes[];
|
||||||
currencySymbol: string;
|
currencySymbol: string;
|
||||||
data: ProductVariantCreateFormData;
|
data: ProductVariantCreateFormData;
|
||||||
|
errors: ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors[];
|
||||||
onVariantDataChange: (
|
onVariantDataChange: (
|
||||||
variantIndex: number,
|
variantIndex: number,
|
||||||
field: VariantField,
|
field: VariantField,
|
||||||
|
@ -35,42 +33,58 @@ export interface ProductVariantCreateSummaryProps {
|
||||||
|
|
||||||
const colors = [blue, cyan, green, purple, yellow].map(color => color[800]);
|
const colors = [blue, cyan, green, purple, yellow].map(color => color[800]);
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
const useStyles = makeStyles(
|
||||||
attributeValue: {
|
(theme: Theme) => ({
|
||||||
display: "inline-block",
|
attributeValue: {
|
||||||
marginRight: theme.spacing.unit
|
display: "inline-block",
|
||||||
},
|
marginRight: theme.spacing.unit
|
||||||
col: {
|
|
||||||
paddingLeft: theme.spacing.unit,
|
|
||||||
paddingRight: theme.spacing.unit
|
|
||||||
},
|
|
||||||
colName: {
|
|
||||||
paddingLeft: "0 !important",
|
|
||||||
width: "auto"
|
|
||||||
},
|
|
||||||
colPrice: {
|
|
||||||
width: 200
|
|
||||||
},
|
|
||||||
colSku: {
|
|
||||||
width: 210
|
|
||||||
},
|
|
||||||
colStock: {
|
|
||||||
width: 120
|
|
||||||
},
|
|
||||||
hr: {
|
|
||||||
marginBottom: theme.spacing.unit,
|
|
||||||
marginTop: theme.spacing.unit / 2
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
"& input": {
|
|
||||||
padding: "16px 12px 17px"
|
|
||||||
},
|
},
|
||||||
marginTop: theme.spacing.unit / 2
|
col: {
|
||||||
|
...theme.typography.body2,
|
||||||
|
fontSize: 14,
|
||||||
|
paddingLeft: theme.spacing.unit,
|
||||||
|
paddingRight: theme.spacing.unit
|
||||||
|
},
|
||||||
|
colHeader: {
|
||||||
|
...theme.typography.body2,
|
||||||
|
fontSize: 14
|
||||||
|
},
|
||||||
|
colName: {
|
||||||
|
"&&": {
|
||||||
|
paddingLeft: "0 !important"
|
||||||
|
},
|
||||||
|
"&:not($colHeader)": {
|
||||||
|
paddingTop: theme.spacing.unit * 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colPrice: {},
|
||||||
|
colSku: {},
|
||||||
|
colStock: {},
|
||||||
|
errorRow: {},
|
||||||
|
hr: {
|
||||||
|
marginBottom: theme.spacing.unit,
|
||||||
|
marginTop: theme.spacing.unit / 2
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
"& input": {
|
||||||
|
padding: "16px 12px 17px"
|
||||||
|
},
|
||||||
|
marginTop: theme.spacing.unit / 2
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "1fr 200px 120px 210px",
|
||||||
|
padding: `${theme.spacing.unit}px 0`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: "ProductVariantCreateSummary"
|
||||||
}
|
}
|
||||||
}));
|
);
|
||||||
|
|
||||||
function getVariantName(
|
function getVariantName(
|
||||||
variant: ProductVariantCreateInput,
|
variant: ProductVariantBulkCreateInput,
|
||||||
attributes: ProductDetails_product_productType_variantAttributes[]
|
attributes: ProductDetails_product_productType_variantAttributes[]
|
||||||
): string[] {
|
): string[] {
|
||||||
return attributes.reduce(
|
return attributes.reduce(
|
||||||
|
@ -78,7 +92,7 @@ function getVariantName(
|
||||||
...acc,
|
...acc,
|
||||||
attribute.values.find(
|
attribute.values.find(
|
||||||
value =>
|
value =>
|
||||||
value.id ===
|
value.slug ===
|
||||||
variant.attributes.find(
|
variant.attributes.find(
|
||||||
variantAttribute => variantAttribute.id === attribute.id
|
variantAttribute => variantAttribute.id === attribute.id
|
||||||
).values[0]
|
).values[0]
|
||||||
|
@ -91,7 +105,13 @@ function getVariantName(
|
||||||
const ProductVariantCreateSummary: React.FC<
|
const ProductVariantCreateSummary: React.FC<
|
||||||
ProductVariantCreateSummaryProps
|
ProductVariantCreateSummaryProps
|
||||||
> = props => {
|
> = props => {
|
||||||
const { attributes, currencySymbol, data, onVariantDataChange } = props;
|
const {
|
||||||
|
attributes,
|
||||||
|
currencySymbol,
|
||||||
|
data,
|
||||||
|
errors,
|
||||||
|
onVariantDataChange
|
||||||
|
} = props;
|
||||||
const classes = useStyles(props);
|
const classes = useStyles(props);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -103,40 +123,69 @@ const ProductVariantCreateSummary: React.FC<
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Hr className={classes.hr} />
|
<Hr className={classes.hr} />
|
||||||
<Table>
|
<div>
|
||||||
<TableHead>
|
<div className={classes.row}>
|
||||||
<TableRow>
|
<div
|
||||||
<TableCell className={classNames(classes.col, classes.colName)}>
|
className={classNames(
|
||||||
<FormattedMessage
|
classes.col,
|
||||||
defaultMessage="Variant"
|
classes.colHeader,
|
||||||
description="variant name"
|
classes.colName
|
||||||
/>
|
)}
|
||||||
</TableCell>
|
>
|
||||||
<TableCell className={classNames(classes.col, classes.colStock)}>
|
<FormattedMessage
|
||||||
<FormattedMessage
|
defaultMessage="Variant"
|
||||||
defaultMessage="Inventory"
|
description="variant name"
|
||||||
description="variant stock amount"
|
/>
|
||||||
/>
|
</div>
|
||||||
</TableCell>
|
<div
|
||||||
<TableCell className={classNames(classes.col, classes.colPrice)}>
|
className={classNames(
|
||||||
<FormattedMessage
|
classes.col,
|
||||||
defaultMessage="Price"
|
classes.colHeader,
|
||||||
description="variant price"
|
classes.colPrice
|
||||||
/>
|
)}
|
||||||
</TableCell>
|
>
|
||||||
<TableCell className={classNames(classes.col, classes.colSku)}>
|
<FormattedMessage
|
||||||
<FormattedMessage defaultMessage="SKU" />
|
defaultMessage="Price"
|
||||||
</TableCell>
|
description="variant price"
|
||||||
</TableRow>
|
/>
|
||||||
</TableHead>
|
</div>
|
||||||
<TableBody>
|
<div
|
||||||
{data.variants.map((variant, variantIndex) => (
|
className={classNames(
|
||||||
<TableRow
|
classes.col,
|
||||||
|
classes.colHeader,
|
||||||
|
classes.colStock
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Inventory"
|
||||||
|
description="variant stock amount"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
classes.col,
|
||||||
|
classes.colHeader,
|
||||||
|
classes.colSku
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<FormattedMessage defaultMessage="SKU" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{data.variants.map((variant, variantIndex) => {
|
||||||
|
const variantErrors = errors.filter(
|
||||||
|
error => error.index === variantIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(classes.row, {
|
||||||
|
[classes.errorRow]: variantErrors.length > 0
|
||||||
|
})}
|
||||||
key={variant.attributes
|
key={variant.attributes
|
||||||
.map(attribute => attribute.values[0])
|
.map(attribute => attribute.values[0])
|
||||||
.join(":")}
|
.join(":")}
|
||||||
>
|
>
|
||||||
<TableCell className={classNames(classes.col, classes.colName)}>
|
<div className={classNames(classes.col, classes.colName)}>
|
||||||
{getVariantName(variant, attributes).map(
|
{getVariantName(variant, attributes).map(
|
||||||
(value, valueIndex) => (
|
(value, valueIndex) => (
|
||||||
<span
|
<span
|
||||||
|
@ -149,31 +198,24 @@ const ProductVariantCreateSummary: React.FC<
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</TableCell>
|
</div>
|
||||||
<TableCell className={classNames(classes.col, classes.colStock)}>
|
<div className={classNames(classes.col, classes.colPrice)}>
|
||||||
<TextField
|
|
||||||
className={classes.input}
|
|
||||||
inputProps={{
|
|
||||||
min: 0,
|
|
||||||
type: "number"
|
|
||||||
}}
|
|
||||||
fullWidth
|
|
||||||
value={variant.quantity}
|
|
||||||
onChange={event =>
|
|
||||||
onVariantDataChange(
|
|
||||||
variantIndex,
|
|
||||||
"stock",
|
|
||||||
event.target.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className={classNames(classes.col, classes.colPrice)}>
|
|
||||||
<TextField
|
<TextField
|
||||||
InputProps={{
|
InputProps={{
|
||||||
endAdornment: currencySymbol
|
endAdornment: currencySymbol
|
||||||
}}
|
}}
|
||||||
className={classes.input}
|
className={classes.input}
|
||||||
|
error={
|
||||||
|
!!variantErrors.find(
|
||||||
|
error => error.field === "priceOverride"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
helperText={maybe(
|
||||||
|
() =>
|
||||||
|
variantErrors.find(
|
||||||
|
error => error.field === "priceOverride"
|
||||||
|
).message
|
||||||
|
)}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
min: 0,
|
min: 0,
|
||||||
type: "number"
|
type: "number"
|
||||||
|
@ -188,21 +230,52 @@ const ProductVariantCreateSummary: React.FC<
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</div>
|
||||||
<TableCell className={classNames(classes.col, classes.colSku)}>
|
<div className={classNames(classes.col, classes.colStock)}>
|
||||||
<TextField
|
<TextField
|
||||||
className={classes.input}
|
className={classes.input}
|
||||||
|
error={
|
||||||
|
!!variantErrors.find(error => error.field === "quantity")
|
||||||
|
}
|
||||||
|
helperText={maybe(
|
||||||
|
() =>
|
||||||
|
variantErrors.find(error => error.field === "quantity")
|
||||||
|
.message
|
||||||
|
)}
|
||||||
|
inputProps={{
|
||||||
|
min: 0,
|
||||||
|
type: "number"
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
value={variant.quantity}
|
||||||
|
onChange={event =>
|
||||||
|
onVariantDataChange(
|
||||||
|
variantIndex,
|
||||||
|
"stock",
|
||||||
|
event.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={classNames(classes.col, classes.colSku)}>
|
||||||
|
<TextField
|
||||||
|
className={classes.input}
|
||||||
|
error={!!variantErrors.find(error => error.field === "sku")}
|
||||||
|
helperText={maybe(
|
||||||
|
() =>
|
||||||
|
variantErrors.find(error => error.field === "sku").message
|
||||||
|
)}
|
||||||
fullWidth
|
fullWidth
|
||||||
value={variant.sku}
|
value={variant.sku}
|
||||||
onChange={event =>
|
onChange={event =>
|
||||||
onVariantDataChange(variantIndex, "sku", event.target.value)
|
onVariantDataChange(variantIndex, "sku", event.target.value)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</div>
|
||||||
</TableRow>
|
</div>
|
||||||
))}
|
);
|
||||||
</TableBody>
|
})}
|
||||||
</Table>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,15 +48,15 @@ const ProductVariantCreateValues: React.FC<
|
||||||
{attribute.values.map(value => (
|
{attribute.values.map(value => (
|
||||||
<ControlledCheckbox
|
<ControlledCheckbox
|
||||||
checked={isSelected(
|
checked={isSelected(
|
||||||
value.id,
|
value.slug,
|
||||||
data.attributes.find(
|
data.attributes.find(
|
||||||
dataAttribute => attribute.id === dataAttribute.id
|
dataAttribute => attribute.id === dataAttribute.id
|
||||||
).values,
|
).values,
|
||||||
(a, b) => a === b
|
(a, b) => a === b
|
||||||
)}
|
)}
|
||||||
name={`value:${value.id}`}
|
name={`value:${value.slug}`}
|
||||||
label={value.name}
|
label={value.name}
|
||||||
onChange={() => onValueClick(attribute.id, value.id)}
|
onChange={() => onValueClick(attribute.id, value.slug)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -133,7 +133,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -159,7 +158,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -185,7 +183,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -211,7 +208,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -237,7 +233,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -263,7 +258,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -289,7 +283,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -315,7 +308,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -354,11 +346,11 @@ Object {
|
||||||
"value": "",
|
"value": "",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
Object {
|
Object {
|
||||||
"id": "val-1-1",
|
"slug": "val-1-1",
|
||||||
"value": "45.99",
|
"value": "45.99",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"id": "val-1-7",
|
"slug": "val-1-7",
|
||||||
"value": "51.99",
|
"value": "51.99",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -392,7 +384,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -418,7 +409,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -444,7 +434,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -470,7 +459,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "45.99",
|
"priceOverride": "45.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -496,7 +484,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "51.99",
|
"priceOverride": "51.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -522,7 +509,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "51.99",
|
"priceOverride": "51.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -548,7 +534,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "51.99",
|
"priceOverride": "51.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -574,7 +559,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "51.99",
|
"priceOverride": "51.99",
|
||||||
"product": "",
|
|
||||||
"quantity": NaN,
|
"quantity": NaN,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -642,7 +626,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 45,
|
"quantity": 45,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -668,7 +651,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 45,
|
"quantity": 45,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -694,7 +676,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 45,
|
"quantity": 45,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -720,7 +701,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 45,
|
"quantity": 45,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -746,7 +726,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 45,
|
"quantity": 45,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -772,7 +751,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 45,
|
"quantity": 45,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -798,7 +776,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 45,
|
"quantity": 45,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -824,7 +801,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 45,
|
"quantity": 45,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -869,11 +845,11 @@ Object {
|
||||||
"value": "",
|
"value": "",
|
||||||
"values": Array [
|
"values": Array [
|
||||||
Object {
|
Object {
|
||||||
"id": "val-1-1",
|
"slug": "val-1-1",
|
||||||
"value": "13",
|
"value": "13",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"id": "val-1-7",
|
"slug": "val-1-7",
|
||||||
"value": "19",
|
"value": "19",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -901,7 +877,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 13,
|
"quantity": 13,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -927,7 +902,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 13,
|
"quantity": 13,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -953,7 +927,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 13,
|
"quantity": 13,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -979,7 +952,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 13,
|
"quantity": 13,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -1005,7 +977,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 19,
|
"quantity": 19,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -1031,7 +1002,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 19,
|
"quantity": 19,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -1057,7 +1027,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 19,
|
"quantity": 19,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
@ -1083,7 +1052,6 @@ Object {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"priceOverride": "",
|
"priceOverride": "",
|
||||||
"product": "",
|
|
||||||
"quantity": 19,
|
"quantity": 19,
|
||||||
"sku": "",
|
"sku": "",
|
||||||
},
|
},
|
||||||
|
|
|
@ -62,7 +62,7 @@ describe("Creates variant matrix", () => {
|
||||||
all: false,
|
all: false,
|
||||||
attribute: attribute.id,
|
attribute: attribute.id,
|
||||||
values: attribute.values.map((attributeValue, attributeValueIndex) => ({
|
values: attribute.values.map((attributeValue, attributeValueIndex) => ({
|
||||||
id: attributeValue,
|
slug: attributeValue,
|
||||||
value: (price * (attributeValueIndex + 1)).toString()
|
value: (price * (attributeValueIndex + 1)).toString()
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
@ -120,7 +120,7 @@ describe("Creates variant matrix", () => {
|
||||||
all: false,
|
all: false,
|
||||||
attribute: attribute.id,
|
attribute: attribute.id,
|
||||||
values: attribute.values.map((attributeValue, attributeValueIndex) => ({
|
values: attribute.values.map((attributeValue, attributeValueIndex) => ({
|
||||||
id: attributeValue,
|
slug: attributeValue,
|
||||||
value: (stock * (attributeValueIndex + 1)).toString()
|
value: (stock * (attributeValueIndex + 1)).toString()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ describe("Creates variant matrix", () => {
|
||||||
all: false,
|
all: false,
|
||||||
attribute: attribute.id,
|
attribute: attribute.id,
|
||||||
values: attribute.values.map((attributeValue, attributeValueIndex) => ({
|
values: attribute.values.map((attributeValue, attributeValueIndex) => ({
|
||||||
id: attributeValue,
|
slug: attributeValue,
|
||||||
value: (price * (attributeValueIndex + 1)).toString()
|
value: (price * (attributeValueIndex + 1)).toString()
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
@ -175,7 +175,7 @@ describe("Creates variant matrix", () => {
|
||||||
all: false,
|
all: false,
|
||||||
attribute: attribute.id,
|
attribute: attribute.id,
|
||||||
values: attribute.values.map((attributeValue, attributeValueIndex) => ({
|
values: attribute.values.map((attributeValue, attributeValueIndex) => ({
|
||||||
id: attributeValue,
|
slug: attributeValue,
|
||||||
value: (stock * (attributeValueIndex + 1)).toString()
|
value: (stock * (attributeValueIndex + 1)).toString()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ProductVariantCreateInput } from "@saleor/types/globalTypes";
|
import { ProductVariantBulkCreateInput } from "@saleor/types/globalTypes";
|
||||||
import {
|
import {
|
||||||
AllOrAttribute,
|
AllOrAttribute,
|
||||||
Attribute,
|
Attribute,
|
||||||
|
@ -7,7 +7,7 @@ import {
|
||||||
|
|
||||||
interface CreateVariantAttributeValueInput {
|
interface CreateVariantAttributeValueInput {
|
||||||
attributeId: string;
|
attributeId: string;
|
||||||
attributeValueId: string;
|
attributeValueSlug: string;
|
||||||
}
|
}
|
||||||
type CreateVariantInput = CreateVariantAttributeValueInput[];
|
type CreateVariantInput = CreateVariantAttributeValueInput[];
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ function getAttributeValuePriceOrStock(
|
||||||
);
|
);
|
||||||
|
|
||||||
const attributeValue = priceOrStock.values.find(
|
const attributeValue = priceOrStock.values.find(
|
||||||
attributeValue => attribute.attributeValueId === attributeValue.id
|
attributeValue => attribute.attributeValueSlug === attributeValue.slug
|
||||||
);
|
);
|
||||||
|
|
||||||
return attributeValue.value;
|
return attributeValue.value;
|
||||||
|
@ -29,7 +29,7 @@ function getAttributeValuePriceOrStock(
|
||||||
function createVariant(
|
function createVariant(
|
||||||
data: ProductVariantCreateFormData,
|
data: ProductVariantCreateFormData,
|
||||||
attributes: CreateVariantInput
|
attributes: CreateVariantInput
|
||||||
): ProductVariantCreateInput {
|
): ProductVariantBulkCreateInput {
|
||||||
const priceOverride = data.price.all
|
const priceOverride = data.price.all
|
||||||
? data.price.value
|
? data.price.value
|
||||||
: getAttributeValuePriceOrStock(attributes, data.price);
|
: getAttributeValuePriceOrStock(attributes, data.price);
|
||||||
|
@ -43,10 +43,9 @@ function createVariant(
|
||||||
return {
|
return {
|
||||||
attributes: attributes.map(attribute => ({
|
attributes: attributes.map(attribute => ({
|
||||||
id: attribute.attributeId,
|
id: attribute.attributeId,
|
||||||
values: [attribute.attributeValueId]
|
values: [attribute.attributeValueSlug]
|
||||||
})),
|
})),
|
||||||
priceOverride,
|
priceOverride,
|
||||||
product: "",
|
|
||||||
quantity,
|
quantity,
|
||||||
sku: ""
|
sku: ""
|
||||||
};
|
};
|
||||||
|
@ -56,11 +55,11 @@ function addAttributeToVariant(
|
||||||
attribute: Attribute,
|
attribute: Attribute,
|
||||||
variant: CreateVariantInput
|
variant: CreateVariantInput
|
||||||
): CreateVariantInput[] {
|
): CreateVariantInput[] {
|
||||||
return attribute.values.map(attributeValueId => [
|
return attribute.values.map(attributeValueSlug => [
|
||||||
...variant,
|
...variant,
|
||||||
{
|
{
|
||||||
attributeId: attribute.id,
|
attributeId: attribute.id,
|
||||||
attributeValueId
|
attributeValueSlug
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -91,7 +90,7 @@ export function createVariantFlatMatrixDimension(
|
||||||
|
|
||||||
export function createVariants(
|
export function createVariants(
|
||||||
data: ProductVariantCreateFormData
|
data: ProductVariantCreateFormData
|
||||||
): ProductVariantCreateInput[] {
|
): ProductVariantBulkCreateInput[] {
|
||||||
if (
|
if (
|
||||||
(!data.price.all && !data.price.attribute) ||
|
(!data.price.all && !data.price.attribute) ||
|
||||||
(!data.stock.all && !data.stock.attribute)
|
(!data.stock.all && !data.stock.attribute)
|
||||||
|
|
|
@ -74,11 +74,11 @@ const price: AllOrAttribute = {
|
||||||
value: "",
|
value: "",
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
id: thirdStep.attributes[1].values[0],
|
slug: thirdStep.attributes[1].values[0],
|
||||||
value: "24.99"
|
value: "24.99"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: thirdStep.attributes[1].values[1],
|
slug: thirdStep.attributes[1].values[1],
|
||||||
value: "26.99"
|
value: "26.99"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -89,11 +89,11 @@ const stock: AllOrAttribute = {
|
||||||
value: "",
|
value: "",
|
||||||
values: [
|
values: [
|
||||||
{
|
{
|
||||||
id: thirdStep.attributes[2].values[0],
|
slug: thirdStep.attributes[2].values[0],
|
||||||
value: "50"
|
value: "50"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: thirdStep.attributes[2].values[1],
|
slug: thirdStep.attributes[2].values[1],
|
||||||
value: "35"
|
value: "35"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ProductVariantCreateInput } from "../../../types/globalTypes";
|
import { ProductVariantBulkCreateInput } from "../../../types/globalTypes";
|
||||||
|
|
||||||
export interface AttributeValue {
|
export interface AttributeValue {
|
||||||
id: string;
|
slug: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
export interface AllOrAttribute {
|
export interface AllOrAttribute {
|
||||||
|
@ -18,7 +18,7 @@ export interface ProductVariantCreateFormData {
|
||||||
attributes: Attribute[];
|
attributes: Attribute[];
|
||||||
price: AllOrAttribute;
|
price: AllOrAttribute;
|
||||||
stock: AllOrAttribute;
|
stock: AllOrAttribute;
|
||||||
variants: ProductVariantCreateInput[];
|
variants: ProductVariantBulkCreateInput[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialForm: ProductVariantCreateFormData = {
|
export const initialForm: ProductVariantCreateFormData = {
|
||||||
|
|
|
@ -108,20 +108,20 @@ function applyStockToAll(
|
||||||
|
|
||||||
function changeAttributeValuePrice(
|
function changeAttributeValuePrice(
|
||||||
state: ProductVariantCreateFormData,
|
state: ProductVariantCreateFormData,
|
||||||
attributeValueId: string,
|
attributeValueSlug: string,
|
||||||
price: string
|
price: string
|
||||||
): ProductVariantCreateFormData {
|
): ProductVariantCreateFormData {
|
||||||
const index = state.price.values.findIndex(
|
const index = state.price.values.findIndex(
|
||||||
value => value.id === attributeValueId
|
value => value.slug === attributeValueSlug
|
||||||
);
|
);
|
||||||
|
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
throw new Error(`Value with id ${attributeValueId} not found`);
|
throw new Error(`Value with id ${attributeValueSlug} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const values = updateAtIndex(
|
const values = updateAtIndex(
|
||||||
{
|
{
|
||||||
id: attributeValueId,
|
slug: attributeValueSlug,
|
||||||
value: price
|
value: price
|
||||||
},
|
},
|
||||||
state.price.values,
|
state.price.values,
|
||||||
|
@ -144,20 +144,20 @@ function changeAttributeValuePrice(
|
||||||
|
|
||||||
function changeAttributeValueStock(
|
function changeAttributeValueStock(
|
||||||
state: ProductVariantCreateFormData,
|
state: ProductVariantCreateFormData,
|
||||||
attributeValueId: string,
|
attributeValueSlug: string,
|
||||||
stock: string
|
stock: string
|
||||||
): ProductVariantCreateFormData {
|
): ProductVariantCreateFormData {
|
||||||
const index = state.stock.values.findIndex(
|
const index = state.stock.values.findIndex(
|
||||||
value => value.id === attributeValueId
|
value => value.slug === attributeValueSlug
|
||||||
);
|
);
|
||||||
|
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
throw new Error(`Value with id ${attributeValueId} not found`);
|
throw new Error(`Value with id ${attributeValueSlug} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const values = updateAtIndex(
|
const values = updateAtIndex(
|
||||||
{
|
{
|
||||||
id: attributeValueId,
|
slug: attributeValueSlug,
|
||||||
value: stock
|
value: stock
|
||||||
},
|
},
|
||||||
state.stock.values,
|
state.stock.values,
|
||||||
|
@ -185,8 +185,8 @@ function changeApplyPriceToAttributeId(
|
||||||
const attribute = state.attributes.find(
|
const attribute = state.attributes.find(
|
||||||
attribute => attribute.id === attributeId
|
attribute => attribute.id === attributeId
|
||||||
);
|
);
|
||||||
const values = attribute.values.map(id => ({
|
const values = attribute.values.map(slug => ({
|
||||||
id,
|
slug,
|
||||||
value: ""
|
value: ""
|
||||||
}));
|
}));
|
||||||
const data = {
|
const data = {
|
||||||
|
@ -211,8 +211,8 @@ function changeApplyStockToAttributeId(
|
||||||
const attribute = state.attributes.find(
|
const attribute = state.attributes.find(
|
||||||
attribute => attribute.id === attributeId
|
attribute => attribute.id === attributeId
|
||||||
);
|
);
|
||||||
const values = attribute.values.map(id => ({
|
const values = attribute.values.map(slug => ({
|
||||||
id,
|
slug,
|
||||||
value: ""
|
value: ""
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ interface ProductVariantsProps extends ListActions, WithStyles<typeof styles> {
|
||||||
fallbackPrice?: ProductVariant_costPrice;
|
fallbackPrice?: ProductVariant_costPrice;
|
||||||
onRowClick: (id: string) => () => void;
|
onRowClick: (id: string) => () => void;
|
||||||
onVariantAdd?();
|
onVariantAdd?();
|
||||||
|
onVariantsAdd?();
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberOfColumns = 5;
|
const numberOfColumns = 5;
|
||||||
|
@ -81,6 +82,7 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
fallbackPrice,
|
fallbackPrice,
|
||||||
onRowClick,
|
onRowClick,
|
||||||
onVariantAdd,
|
onVariantAdd,
|
||||||
|
onVariantsAdd,
|
||||||
isChecked,
|
isChecked,
|
||||||
selected,
|
selected,
|
||||||
toggle,
|
toggle,
|
||||||
|
@ -98,7 +100,7 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
description: "section header"
|
description: "section header"
|
||||||
})}
|
})}
|
||||||
toolbar={
|
toolbar={
|
||||||
<>
|
hasVariants ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={onVariantAdd}
|
onClick={onVariantAdd}
|
||||||
variant="text"
|
variant="text"
|
||||||
|
@ -110,7 +112,19 @@ export const ProductVariants = withStyles(styles, { name: "ProductVariants" })(
|
||||||
description="button"
|
description="button"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
) : (
|
||||||
|
<Button
|
||||||
|
onClick={onVariantsAdd}
|
||||||
|
variant="text"
|
||||||
|
color="primary"
|
||||||
|
data-tc="button-add-variants"
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Create variants"
|
||||||
|
description="button"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{!variants.length && (
|
{!variants.length && (
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
TypedProductImageCreateMutation,
|
TypedProductImageCreateMutation,
|
||||||
TypedProductImageDeleteMutation,
|
TypedProductImageDeleteMutation,
|
||||||
TypedProductUpdateMutation,
|
TypedProductUpdateMutation,
|
||||||
|
TypedProductVariantBulkCreateMutation,
|
||||||
TypedProductVariantBulkDeleteMutation,
|
TypedProductVariantBulkDeleteMutation,
|
||||||
TypedSimpleProductUpdateMutation
|
TypedSimpleProductUpdateMutation
|
||||||
} from "../mutations";
|
} from "../mutations";
|
||||||
|
@ -25,6 +26,10 @@ import {
|
||||||
ProductImageReorderVariables
|
ProductImageReorderVariables
|
||||||
} from "../types/ProductImageReorder";
|
} from "../types/ProductImageReorder";
|
||||||
import { ProductUpdate, ProductUpdateVariables } from "../types/ProductUpdate";
|
import { ProductUpdate, ProductUpdateVariables } from "../types/ProductUpdate";
|
||||||
|
import {
|
||||||
|
ProductVariantBulkCreate,
|
||||||
|
ProductVariantBulkCreateVariables
|
||||||
|
} from "../types/ProductVariantBulkCreate";
|
||||||
import {
|
import {
|
||||||
ProductVariantBulkDelete,
|
ProductVariantBulkDelete,
|
||||||
ProductVariantBulkDeleteVariables
|
ProductVariantBulkDeleteVariables
|
||||||
|
@ -38,6 +43,10 @@ import ProductImagesReorderProvider from "./ProductImagesReorder";
|
||||||
interface ProductUpdateOperationsProps {
|
interface ProductUpdateOperationsProps {
|
||||||
product: ProductDetails_product;
|
product: ProductDetails_product;
|
||||||
children: (props: {
|
children: (props: {
|
||||||
|
bulkProductVariantCreate: PartialMutationProviderOutput<
|
||||||
|
ProductVariantBulkCreate,
|
||||||
|
ProductVariantBulkCreateVariables
|
||||||
|
>;
|
||||||
bulkProductVariantDelete: PartialMutationProviderOutput<
|
bulkProductVariantDelete: PartialMutationProviderOutput<
|
||||||
ProductVariantBulkDelete,
|
ProductVariantBulkDelete,
|
||||||
ProductVariantBulkDeleteVariables
|
ProductVariantBulkDeleteVariables
|
||||||
|
@ -67,6 +76,7 @@ interface ProductUpdateOperationsProps {
|
||||||
SimpleProductUpdateVariables
|
SimpleProductUpdateVariables
|
||||||
>;
|
>;
|
||||||
}) => React.ReactNode;
|
}) => React.ReactNode;
|
||||||
|
onBulkProductVariantCreate?: (data: ProductVariantBulkCreate) => void;
|
||||||
onBulkProductVariantDelete?: (data: ProductVariantBulkDelete) => void;
|
onBulkProductVariantDelete?: (data: ProductVariantBulkDelete) => void;
|
||||||
onDelete?: (data: ProductDelete) => void;
|
onDelete?: (data: ProductDelete) => void;
|
||||||
onImageCreate?: (data: ProductImageCreate) => void;
|
onImageCreate?: (data: ProductImageCreate) => void;
|
||||||
|
@ -80,6 +90,7 @@ const ProductUpdateOperations: React.StatelessComponent<
|
||||||
> = ({
|
> = ({
|
||||||
product,
|
product,
|
||||||
children,
|
children,
|
||||||
|
onBulkProductVariantCreate,
|
||||||
onBulkProductVariantDelete,
|
onBulkProductVariantDelete,
|
||||||
onDelete,
|
onDelete,
|
||||||
onImageDelete,
|
onImageDelete,
|
||||||
|
@ -112,31 +123,40 @@ const ProductUpdateOperations: React.StatelessComponent<
|
||||||
<TypedProductVariantBulkDeleteMutation
|
<TypedProductVariantBulkDeleteMutation
|
||||||
onCompleted={onBulkProductVariantDelete}
|
onCompleted={onBulkProductVariantDelete}
|
||||||
>
|
>
|
||||||
{(...bulkProductVariantDelete) =>
|
{(...bulkProductVariantDelete) => (
|
||||||
children({
|
<TypedProductVariantBulkCreateMutation
|
||||||
bulkProductVariantDelete: getMutationProviderData(
|
onCompleted={onBulkProductVariantCreate}
|
||||||
...bulkProductVariantDelete
|
>
|
||||||
),
|
{(...bulkProductVariantCreate) =>
|
||||||
createProductImage: getMutationProviderData(
|
children({
|
||||||
...createProductImage
|
bulkProductVariantCreate: getMutationProviderData(
|
||||||
),
|
...bulkProductVariantCreate
|
||||||
deleteProduct: getMutationProviderData(
|
),
|
||||||
...deleteProduct
|
bulkProductVariantDelete: getMutationProviderData(
|
||||||
),
|
...bulkProductVariantDelete
|
||||||
deleteProductImage: getMutationProviderData(
|
),
|
||||||
...deleteProductImage
|
createProductImage: getMutationProviderData(
|
||||||
),
|
...createProductImage
|
||||||
reorderProductImages: getMutationProviderData(
|
),
|
||||||
...reorderProductImages
|
deleteProduct: getMutationProviderData(
|
||||||
),
|
...deleteProduct
|
||||||
updateProduct: getMutationProviderData(
|
),
|
||||||
...updateProduct
|
deleteProductImage: getMutationProviderData(
|
||||||
),
|
...deleteProductImage
|
||||||
updateSimpleProduct: getMutationProviderData(
|
),
|
||||||
...updateSimpleProduct
|
reorderProductImages: getMutationProviderData(
|
||||||
)
|
...reorderProductImages
|
||||||
})
|
),
|
||||||
}
|
updateProduct: getMutationProviderData(
|
||||||
|
...updateProduct
|
||||||
|
),
|
||||||
|
updateSimpleProduct: getMutationProviderData(
|
||||||
|
...updateSimpleProduct
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TypedProductVariantBulkCreateMutation>
|
||||||
|
)}
|
||||||
</TypedProductVariantBulkDeleteMutation>
|
</TypedProductVariantBulkDeleteMutation>
|
||||||
)}
|
)}
|
||||||
</TypedSimpleProductUpdateMutation>
|
</TypedSimpleProductUpdateMutation>
|
||||||
|
|
|
@ -45,6 +45,10 @@ import {
|
||||||
productBulkPublish,
|
productBulkPublish,
|
||||||
productBulkPublishVariables
|
productBulkPublishVariables
|
||||||
} from "./types/productBulkPublish";
|
} from "./types/productBulkPublish";
|
||||||
|
import {
|
||||||
|
ProductVariantBulkCreate,
|
||||||
|
ProductVariantBulkCreateVariables
|
||||||
|
} from "./types/ProductVariantBulkCreate";
|
||||||
import {
|
import {
|
||||||
ProductVariantBulkDelete,
|
ProductVariantBulkDelete,
|
||||||
ProductVariantBulkDeleteVariables
|
ProductVariantBulkDeleteVariables
|
||||||
|
@ -440,6 +444,30 @@ export const TypedProductBulkPublishMutation = TypedMutation<
|
||||||
productBulkPublishVariables
|
productBulkPublishVariables
|
||||||
>(productBulkPublishMutation);
|
>(productBulkPublishMutation);
|
||||||
|
|
||||||
|
export const ProductVariantBulkCreateMutation = gql`
|
||||||
|
mutation ProductVariantBulkCreate(
|
||||||
|
$id: ID!
|
||||||
|
$inputs: [ProductVariantBulkCreateInput]!
|
||||||
|
) {
|
||||||
|
productVariantBulkCreate(product: $id, variants: $inputs) {
|
||||||
|
bulkProductErrors {
|
||||||
|
field
|
||||||
|
message
|
||||||
|
code
|
||||||
|
index
|
||||||
|
}
|
||||||
|
errors {
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const TypedProductVariantBulkCreateMutation = TypedMutation<
|
||||||
|
ProductVariantBulkCreate,
|
||||||
|
ProductVariantBulkCreateVariables
|
||||||
|
>(ProductVariantBulkCreateMutation);
|
||||||
|
|
||||||
export const ProductVariantBulkDeleteMutation = gql`
|
export const ProductVariantBulkDeleteMutation = gql`
|
||||||
mutation ProductVariantBulkDelete($ids: [ID!]!) {
|
mutation ProductVariantBulkDelete($ids: [ID!]!) {
|
||||||
productVariantBulkDelete(ids: $ids) {
|
productVariantBulkDelete(ids: $ids) {
|
||||||
|
|
|
@ -267,6 +267,7 @@ const productDetailsQuery = gql`
|
||||||
values {
|
values {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
slug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,7 @@ export interface ProductDetails_product_productType_variantAttributes_values {
|
||||||
__typename: "AttributeValue";
|
__typename: "AttributeValue";
|
||||||
id: string;
|
id: string;
|
||||||
name: string | null;
|
name: string | null;
|
||||||
|
slug: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProductDetails_product_productType_variantAttributes {
|
export interface ProductDetails_product_productType_variantAttributes {
|
||||||
|
|
38
src/products/types/ProductVariantBulkCreate.ts
Normal file
38
src/products/types/ProductVariantBulkCreate.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
|
import { ProductVariantBulkCreateInput, ProductErrorCode } from "./../../types/globalTypes";
|
||||||
|
|
||||||
|
// ====================================================
|
||||||
|
// GraphQL mutation operation: ProductVariantBulkCreate
|
||||||
|
// ====================================================
|
||||||
|
|
||||||
|
export interface ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors {
|
||||||
|
__typename: "BulkProductError";
|
||||||
|
field: string | null;
|
||||||
|
message: string | null;
|
||||||
|
code: ProductErrorCode | null;
|
||||||
|
index: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProductVariantBulkCreate_productVariantBulkCreate_errors {
|
||||||
|
__typename: "Error";
|
||||||
|
field: string | null;
|
||||||
|
message: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProductVariantBulkCreate_productVariantBulkCreate {
|
||||||
|
__typename: "ProductVariantBulkCreate";
|
||||||
|
bulkProductErrors: ProductVariantBulkCreate_productVariantBulkCreate_bulkProductErrors[] | null;
|
||||||
|
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProductVariantBulkCreate {
|
||||||
|
productVariantBulkCreate: ProductVariantBulkCreate_productVariantBulkCreate | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProductVariantBulkCreateVariables {
|
||||||
|
id: string;
|
||||||
|
inputs: (ProductVariantBulkCreateInput | null)[];
|
||||||
|
}
|
|
@ -53,7 +53,7 @@ export const productListUrl = (params?: ProductListUrlQueryParams): string =>
|
||||||
export const productPath = (id: string) => urlJoin(productSection + id);
|
export const productPath = (id: string) => urlJoin(productSection + id);
|
||||||
export type ProductUrlDialog = "remove";
|
export type ProductUrlDialog = "remove";
|
||||||
export type ProductUrlQueryParams = BulkAction &
|
export type ProductUrlQueryParams = BulkAction &
|
||||||
Dialog<"remove" | "remove-variants">;
|
Dialog<"create-variants" | "remove" | "remove-variants">;
|
||||||
export const productUrl = (id: string, params?: ProductUrlQueryParams) =>
|
export const productUrl = (id: string, params?: ProductUrlQueryParams) =>
|
||||||
productPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
productPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,10 @@ import { WindowTitle } from "@saleor/components/WindowTitle";
|
||||||
import useBulkActions from "@saleor/hooks/useBulkActions";
|
import useBulkActions from "@saleor/hooks/useBulkActions";
|
||||||
import useNavigator from "@saleor/hooks/useNavigator";
|
import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useNotifier from "@saleor/hooks/useNotifier";
|
import useNotifier from "@saleor/hooks/useNotifier";
|
||||||
|
import useShop from "@saleor/hooks/useShop";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
|
import ProductVariantCreateDialog from "@saleor/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog";
|
||||||
|
import { ProductVariantBulkCreate } from "@saleor/products/types/ProductVariantBulkCreate";
|
||||||
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
|
import { DEFAULT_INITIAL_SEARCH_DATA } from "../../../config";
|
||||||
import SearchCategories from "../../../containers/SearchCategories";
|
import SearchCategories from "../../../containers/SearchCategories";
|
||||||
import SearchCollections from "../../../containers/SearchCollections";
|
import SearchCollections from "../../../containers/SearchCollections";
|
||||||
|
@ -54,6 +57,7 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
params.ids
|
params.ids
|
||||||
);
|
);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const shop = useShop();
|
||||||
|
|
||||||
const openModal = (action: ProductUrlDialog) =>
|
const openModal = (action: ProductUrlDialog) =>
|
||||||
navigate(
|
navigate(
|
||||||
|
@ -115,6 +119,15 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
const handleVariantAdd = () =>
|
const handleVariantAdd = () =>
|
||||||
navigate(productVariantAddUrl(id));
|
navigate(productVariantAddUrl(id));
|
||||||
|
|
||||||
|
const handleBulkProductVariantCreate = (
|
||||||
|
data: ProductVariantBulkCreate
|
||||||
|
) => {
|
||||||
|
if (data.productVariantBulkCreate.errors.length === 0) {
|
||||||
|
navigate(productUrl(id), true);
|
||||||
|
refetch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleBulkProductVariantDelete = (
|
const handleBulkProductVariantDelete = (
|
||||||
data: ProductVariantBulkDelete
|
data: ProductVariantBulkDelete
|
||||||
) => {
|
) => {
|
||||||
|
@ -125,10 +138,19 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleVariantCreatorOpen = () =>
|
||||||
|
navigate(
|
||||||
|
productUrl(id, {
|
||||||
|
...params,
|
||||||
|
action: "create-variants"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const product = data ? data.product : undefined;
|
const product = data ? data.product : undefined;
|
||||||
return (
|
return (
|
||||||
<ProductUpdateOperations
|
<ProductUpdateOperations
|
||||||
product={product}
|
product={product}
|
||||||
|
onBulkProductVariantCreate={handleBulkProductVariantCreate}
|
||||||
onBulkProductVariantDelete={handleBulkProductVariantDelete}
|
onBulkProductVariantDelete={handleBulkProductVariantDelete}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
onImageCreate={handleImageCreate}
|
onImageCreate={handleImageCreate}
|
||||||
|
@ -136,6 +158,7 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
onUpdate={handleUpdate}
|
onUpdate={handleUpdate}
|
||||||
>
|
>
|
||||||
{({
|
{({
|
||||||
|
bulkProductVariantCreate,
|
||||||
bulkProductVariantDelete,
|
bulkProductVariantDelete,
|
||||||
createProductImage,
|
createProductImage,
|
||||||
deleteProduct,
|
deleteProduct,
|
||||||
|
@ -245,6 +268,7 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
onImageReorder={handleImageReorder}
|
onImageReorder={handleImageReorder}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onVariantAdd={handleVariantAdd}
|
onVariantAdd={handleVariantAdd}
|
||||||
|
onVariantsAdd={handleVariantCreatorOpen}
|
||||||
onVariantShow={variantId => () =>
|
onVariantShow={variantId => () =>
|
||||||
navigate(
|
navigate(
|
||||||
productVariantEditUrl(product.id, variantId)
|
productVariantEditUrl(product.id, variantId)
|
||||||
|
@ -328,6 +352,34 @@ export const ProductUpdate: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
/>
|
/>
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
</ActionDialog>
|
</ActionDialog>
|
||||||
|
<ProductVariantCreateDialog
|
||||||
|
errors={maybe(
|
||||||
|
() =>
|
||||||
|
bulkProductVariantCreate.opts.data
|
||||||
|
.productVariantBulkCreate.bulkProductErrors,
|
||||||
|
[]
|
||||||
|
)}
|
||||||
|
open={params.action === "create-variants"}
|
||||||
|
attributes={maybe(
|
||||||
|
() => data.product.productType.variantAttributes,
|
||||||
|
[]
|
||||||
|
)}
|
||||||
|
currencySymbol={maybe(() => shop.defaultCurrency)}
|
||||||
|
onClose={() =>
|
||||||
|
navigate(
|
||||||
|
productUrl(id, {
|
||||||
|
...params,
|
||||||
|
action: undefined
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onSubmit={inputs =>
|
||||||
|
bulkProductVariantCreate.mutate({
|
||||||
|
id,
|
||||||
|
inputs
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -58,18 +58,20 @@ export const ProductVariant: React.StatelessComponent<ProductUpdateProps> = ({
|
||||||
) =>
|
) =>
|
||||||
variantCreate({
|
variantCreate({
|
||||||
variables: {
|
variables: {
|
||||||
attributes: formData.attributes
|
input: {
|
||||||
.filter(attribute => attribute.value !== "")
|
attributes: formData.attributes
|
||||||
.map(attribute => ({
|
.filter(attribute => attribute.value !== "")
|
||||||
id: attribute.id,
|
.map(attribute => ({
|
||||||
values: [attribute.value]
|
id: attribute.id,
|
||||||
})),
|
values: [attribute.value]
|
||||||
costPrice: decimal(formData.costPrice),
|
})),
|
||||||
priceOverride: decimal(formData.priceOverride),
|
costPrice: decimal(formData.costPrice),
|
||||||
product: productId,
|
priceOverride: decimal(formData.priceOverride),
|
||||||
quantity: formData.quantity || null,
|
product: productId,
|
||||||
sku: formData.sku,
|
quantity: formData.quantity || null,
|
||||||
trackInventory: true
|
sku: formData.sku,
|
||||||
|
trackInventory: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const handleVariantClick = (id: string) =>
|
const handleVariantClick = (id: string) =>
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -30,6 +30,7 @@ const props: ProductUpdatePageProps = {
|
||||||
onSubmit: () => undefined,
|
onSubmit: () => undefined,
|
||||||
onVariantAdd: () => undefined,
|
onVariantAdd: () => undefined,
|
||||||
onVariantShow: () => undefined,
|
onVariantShow: () => undefined,
|
||||||
|
onVariantsAdd: () => undefined,
|
||||||
placeholderImage,
|
placeholderImage,
|
||||||
product,
|
product,
|
||||||
saveButtonBarState: "default",
|
saveButtonBarState: "default",
|
||||||
|
|
|
@ -193,6 +193,20 @@ export enum PermissionEnum {
|
||||||
MANAGE_WEBHOOKS = "MANAGE_WEBHOOKS",
|
MANAGE_WEBHOOKS = "MANAGE_WEBHOOKS",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ProductErrorCode {
|
||||||
|
ALREADY_EXISTS = "ALREADY_EXISTS",
|
||||||
|
ATTRIBUTE_ALREADY_ASSIGNED = "ATTRIBUTE_ALREADY_ASSIGNED",
|
||||||
|
ATTRIBUTE_CANNOT_BE_ASSIGNED = "ATTRIBUTE_CANNOT_BE_ASSIGNED",
|
||||||
|
ATTRIBUTE_VARIANTS_DISABLED = "ATTRIBUTE_VARIANTS_DISABLED",
|
||||||
|
GRAPHQL_ERROR = "GRAPHQL_ERROR",
|
||||||
|
INVALID = "INVALID",
|
||||||
|
NOT_FOUND = "NOT_FOUND",
|
||||||
|
NOT_PRODUCTS_IMAGE = "NOT_PRODUCTS_IMAGE",
|
||||||
|
REQUIRED = "REQUIRED",
|
||||||
|
UNIQUE = "UNIQUE",
|
||||||
|
VARIANT_NO_DIGITAL_CONTENT = "VARIANT_NO_DIGITAL_CONTENT",
|
||||||
|
}
|
||||||
|
|
||||||
export enum ProductOrderField {
|
export enum ProductOrderField {
|
||||||
DATE = "DATE",
|
DATE = "DATE",
|
||||||
MINIMAL_PRICE = "MINIMAL_PRICE",
|
MINIMAL_PRICE = "MINIMAL_PRICE",
|
||||||
|
@ -614,6 +628,16 @@ export interface ProductTypeInput {
|
||||||
taxCode?: string | null;
|
taxCode?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProductVariantBulkCreateInput {
|
||||||
|
attributes: (AttributeValueInput | null)[];
|
||||||
|
costPrice?: any | null;
|
||||||
|
priceOverride?: any | null;
|
||||||
|
sku: string;
|
||||||
|
quantity?: number | null;
|
||||||
|
trackInventory?: boolean | null;
|
||||||
|
weight?: any | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProductVariantCreateInput {
|
export interface ProductVariantCreateInput {
|
||||||
attributes: (AttributeValueInput | null)[];
|
attributes: (AttributeValueInput | null)[];
|
||||||
costPrice?: any | null;
|
costPrice?: any | null;
|
||||||
|
|
Loading…
Reference in a new issue