Make variant creator view instead of dialog

This commit is contained in:
dominik-zeglen 2020-04-01 18:28:47 +02:00
parent 50ff050155
commit e41a79fe78
41 changed files with 1352 additions and 1104 deletions

View file

@ -1,147 +0,0 @@
import { makeStyles } from "@material-ui/core/styles";
import React from "react";
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate";
import { isSelected } from "@saleor/utils/lists";
import { ProductVariantCreateFormData } from "./form";
import ProductVariantCreatePrices from "./ProductVariantCreatePrices";
import ProductVariantCreateSummary from "./ProductVariantCreateSummary";
import ProductVariantCreateTabs from "./ProductVariantCreateTabs";
import ProductVariantCreateValues from "./ProductVariantCreateValues";
import { ProductVariantCreateReducerAction } from "./reducer";
import { ProductVariantCreateStep } from "./types";
const useStyles = makeStyles(
theme => ({
root: {
maxHeight: 400,
overflowX: "hidden",
overflowY: "scroll",
paddingLeft: theme.spacing(3),
paddingRight: theme.spacing(2),
position: "relative",
right: theme.spacing(3),
width: `calc(100% + ${theme.spacing(3)}px)`
}
}),
{ name: "ProductVariantCreateContent" }
);
export interface ProductVariantCreateContentProps {
attributes: ProductDetails_product_productType_variantAttributes[];
currencySymbol: string;
data: ProductVariantCreateFormData;
dispatchFormDataAction: React.Dispatch<ProductVariantCreateReducerAction>;
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[];
step: ProductVariantCreateStep;
onStepClick: (step: ProductVariantCreateStep) => void;
}
const ProductVariantCreateContent: React.FC<ProductVariantCreateContentProps> = props => {
const {
attributes,
currencySymbol,
data,
dispatchFormDataAction,
errors,
step,
onStepClick
} = props;
const classes = useStyles(props);
const selectedAttributes = attributes.filter(attribute =>
isSelected(
attribute.id,
data.attributes.map(dataAttribute => dataAttribute.id),
(a, b) => a === b
)
);
return (
<div>
<ProductVariantCreateTabs step={step} onStepClick={onStepClick} />
<div className={classes.root}>
{step === ProductVariantCreateStep.values && (
<ProductVariantCreateValues
attributes={selectedAttributes}
data={data}
onValueClick={(attributeId, valueId) =>
dispatchFormDataAction({
attributeId,
type: "selectValue",
valueId
})
}
/>
)}
{step === ProductVariantCreateStep.prices && (
<ProductVariantCreatePrices
attributes={selectedAttributes}
currencySymbol={currencySymbol}
data={data}
onApplyPriceOrStockChange={(all, type) =>
dispatchFormDataAction({
all,
type: type === "price" ? "applyPriceToAll" : "applyStockToAll"
})
}
onApplyToAllChange={(value, type) =>
dispatchFormDataAction({
type:
type === "price"
? "changeApplyPriceToAllValue"
: "changeApplyStockToAllValue",
value
})
}
onAttributeSelect={(attributeId, type) =>
dispatchFormDataAction({
attributeId,
type:
type === "price"
? "changeApplyPriceToAttributeId"
: "changeApplyStockToAttributeId"
})
}
onAttributeValueChange={(valueId, value, type) =>
dispatchFormDataAction({
type:
type === "price"
? "changeAttributeValuePrice"
: "changeAttributeValueStock",
value,
valueId
})
}
/>
)}
{step === ProductVariantCreateStep.summary && (
<ProductVariantCreateSummary
attributes={selectedAttributes}
currencySymbol={currencySymbol}
data={data}
errors={errors}
onVariantDataChange={(variantIndex, field, value) =>
dispatchFormDataAction({
field,
type: "changeVariantData",
value,
variantIndex
})
}
onVariantDelete={variantIndex =>
dispatchFormDataAction({
type: "deleteVariant",
variantIndex
})
}
/>
)}
</div>
</div>
);
};
ProductVariantCreateContent.displayName = "ProductVariantCreateContent";
export default ProductVariantCreateContent;

View file

@ -1,315 +0,0 @@
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Radio from "@material-ui/core/Radio";
import RadioGroup from "@material-ui/core/RadioGroup";
import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import FormSpacer from "@saleor/components/FormSpacer";
import Grid from "@saleor/components/Grid";
import Hr from "@saleor/components/Hr";
import SingleSelectField from "@saleor/components/SingleSelectField";
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
import { ProductVariantCreateFormData } from "./form";
const useStyles = makeStyles(
theme => ({
hr: {
marginBottom: theme.spacing(),
marginTop: theme.spacing(0.5)
},
hrAttribute: {
marginTop: theme.spacing(2)
},
label: {
alignSelf: "center"
},
shortInput: {
width: "50%"
}
}),
{ name: "ProductVariantCreatePrices" }
);
export type PriceOrStock = "price" | "stock";
export interface ProductVariantCreatePricesProps {
attributes: ProductDetails_product_productType_variantAttributes[];
currencySymbol: string;
data: ProductVariantCreateFormData;
onApplyPriceOrStockChange: (applyToAll: boolean, type: PriceOrStock) => void;
onApplyToAllChange: (value: string, type: PriceOrStock) => void;
onAttributeSelect: (id: string, type: PriceOrStock) => void;
onAttributeValueChange: (
id: string,
value: string,
type: PriceOrStock
) => void;
}
const ProductVariantCreatePrices: React.FC<
ProductVariantCreatePricesProps
> = props => {
const {
attributes,
currencySymbol,
data,
onApplyPriceOrStockChange,
onApplyToAllChange,
onAttributeSelect,
onAttributeValueChange
} = props;
const classes = useStyles(props);
const intl = useIntl();
const attributeChoices = attributes.map(attribute => ({
label: attribute.name,
value: attribute.id
}));
const priceAttributeValues = data.price.all
? null
: data.price.attribute
? 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
? null
: data.stock.attribute
? 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 (
<>
<Typography color="textSecondary" variant="h5">
<FormattedMessage
defaultMessage="Price"
description="variant price, header"
/>
</Typography>
<Hr className={classes.hr} />
<RadioGroup value={data.price.all ? "applyToAll" : "applyToAttribute"}>
<FormControlLabel
value="applyToAll"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply single price to all SKUs"
})}
onChange={() => onApplyPriceOrStockChange(true, "price")}
/>
<FormSpacer />
<TextField
className={classes.shortInput}
inputProps={{
min: 0,
type: "number"
}}
InputProps={{
endAdornment: currencySymbol
}}
label={intl.formatMessage({
defaultMessage: "Price",
id: "productVariantCreatePricesPriceInputLabel"
})}
value={data.price.value}
onChange={event => onApplyToAllChange(event.target.value, "price")}
/>
<FormSpacer />
<FormControlLabel
value="applyToAttribute"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply unique prices by attribute to each SKU"
})}
onChange={() => onApplyPriceOrStockChange(false, "price")}
/>
</RadioGroup>
{!data.price.all && (
<>
<FormSpacer />
<Grid variant="uniform">
<div className={classes.label}>
<Typography>
<FormattedMessage
defaultMessage="Choose attribute"
description="variant attribute"
/>
</Typography>
</div>
<div>
<SingleSelectField
choices={attributeChoices}
label={intl.formatMessage({
defaultMessage: "Attribute",
description: "variant attribute"
})}
value={data.price.attribute}
onChange={event =>
onAttributeSelect(event.target.value, "price")
}
/>
</div>
</Grid>
<Hr className={classes.hrAttribute} />
{priceAttributeValues &&
priceAttributeValues.map(attributeValue => (
<React.Fragment key={attributeValue.id}>
<FormSpacer />
<Grid variant="uniform">
<div className={classes.label}>
<Typography>{attributeValue.name}</Typography>
</div>
<div>
<TextField
label={intl.formatMessage({
defaultMessage: "Price",
description: "variant price",
id: "productVariantCreatePricesSetPricePlaceholder"
})}
inputProps={{
min: 0,
type: "number"
}}
InputProps={{
endAdornment: currencySymbol
}}
fullWidth
value={
data.price.values.find(
value => value.slug === attributeValue.slug
).value
}
onChange={event =>
onAttributeValueChange(
attributeValue.slug,
event.target.value,
"price"
)
}
/>
</div>
</Grid>
</React.Fragment>
))}
</>
)}
<FormSpacer />
<Typography color="textSecondary" variant="h5">
<FormattedMessage
defaultMessage="Stock"
description="variant stock, header"
/>
</Typography>
<Hr className={classes.hr} />
<RadioGroup value={data.stock.all ? "applyToAll" : "applyToAttribute"}>
<FormControlLabel
value="applyToAll"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply single stock to all SKUs"
})}
onChange={() => onApplyPriceOrStockChange(true, "stock")}
/>
<FormSpacer />
<TextField
className={classes.shortInput}
inputProps={{
min: 0,
type: "number"
}}
label={intl.formatMessage({
defaultMessage: "Stock",
id: "productVariantCreatePricesStockInputLabel"
})}
value={data.stock.value}
onChange={event => onApplyToAllChange(event.target.value, "stock")}
/>
<FormSpacer />
<FormControlLabel
value="applyToAttribute"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply unique stock by attribute to each SKU"
})}
onChange={() => onApplyPriceOrStockChange(false, "stock")}
/>
</RadioGroup>
{!data.stock.all && (
<>
<FormSpacer />
<Grid variant="uniform">
<div className={classes.label}>
<Typography>
<FormattedMessage
defaultMessage="Choose attribute"
description="variant attribute"
/>
</Typography>
</div>
<div>
<SingleSelectField
choices={attributeChoices}
label={intl.formatMessage({
defaultMessage: "Attribute",
description: "variant attribute"
})}
value={data.stock.attribute}
onChange={event =>
onAttributeSelect(event.target.value, "stock")
}
/>
</div>
</Grid>
<Hr className={classes.hrAttribute} />
{stockAttributeValues &&
stockAttributeValues.map(attributeValue => (
<React.Fragment key={attributeValue.id}>
<FormSpacer />
<Grid variant="uniform">
<div className={classes.label}>
<Typography>{attributeValue.name}</Typography>
</div>
<div>
<TextField
label={intl.formatMessage({
defaultMessage: "Stock",
description: "variant stock",
id: "productVariantCreatePricesSetStockPlaceholder"
})}
fullWidth
value={
data.stock.values.find(
value => value.slug === attributeValue.slug
).value
}
onChange={event =>
onAttributeValueChange(
attributeValue.slug,
event.target.value,
"stock"
)
}
/>
</div>
</Grid>
</React.Fragment>
))}
</>
)}
</>
);
};
ProductVariantCreatePrices.displayName = "ProductVariantCreatePrices";
export default ProductVariantCreatePrices;

View file

@ -1,81 +0,0 @@
import makeStyles from "@material-ui/core/styles/makeStyles";
import Typography from "@material-ui/core/Typography";
import React from "react";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import Debounce from "@saleor/components/Debounce";
import Hr from "@saleor/components/Hr";
import Skeleton from "@saleor/components/Skeleton";
import { maybe } from "@saleor/misc";
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
import { isSelected } from "@saleor/utils/lists";
import { ProductVariantCreateFormData } from "./form";
export interface ProductVariantCreateValuesProps {
attributes: ProductDetails_product_productType_variantAttributes[];
data: ProductVariantCreateFormData;
onValueClick: (attributeId: string, valueId: string) => void;
}
const useStyles = makeStyles(
theme => ({
hr: {
marginBottom: theme.spacing(),
marginTop: theme.spacing(0.5)
},
valueContainer: {
display: "grid",
gridColumnGap: theme.spacing(3),
gridTemplateColumns: "repeat(3, 1fr)",
marginBottom: theme.spacing(3)
}
}),
{ name: "ProductVariantCreateValues" }
);
const ProductVariantCreateValues: React.FC<
ProductVariantCreateValuesProps
> = props => {
const { attributes, data, onValueClick } = props;
const classes = useStyles(props);
return (
<>
{attributes.map(attribute => (
<React.Fragment key={attribute.id}>
<Typography color="textSecondary" variant="h5">
{maybe<React.ReactNode>(() => attribute.name, <Skeleton />)}
</Typography>
<Hr className={classes.hr} />
<div className={classes.valueContainer}>
{attribute.values.map(value => (
<Debounce
debounceFn={() => onValueClick(attribute.id, value.slug)}
time={100}
key={value.slug}
>
{change => (
<ControlledCheckbox
checked={isSelected(
value.slug,
data.attributes.find(
dataAttribute => attribute.id === dataAttribute.id
).values,
(a, b) => a === b
)}
name={`value:${value.slug}`}
label={value.name}
onChange={change}
/>
)}
</Debounce>
))}
</div>
</React.Fragment>
))}
</>
);
};
ProductVariantCreateValues.displayName = "ProductVariantCreateValues";
export default ProductVariantCreateValues;

View file

@ -1,5 +0,0 @@
export enum ProductVariantCreateStep {
values,
prices,
summary
}

View file

@ -1,18 +1,18 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import { storiesOf } from "@storybook/react";
import React from "react";
import { attributes } from "@saleor/attributes/fixtures";
import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate";
import { ProductErrorCode } from "@saleor/types/globalTypes";
import Container from "@saleor/components/Container";
import Decorator from "../../../storybook/Decorator";
import { createVariants } from "./createVariants";
import { AllOrAttribute } from "./form";
import ProductVariantCreateContent, {
ProductVariantCreateContentProps
} from "./ProductVariantCreateContent";
import ProductVariantCreateDialog from "./ProductVariantCreateDialog";
import ProductVariantCreatorContent, {
ProductVariantCreatorContentProps
} from "./ProductVariantCreatorContent";
import ProductVariantCreatorPage from "./ProductVariantCreatorPage";
import { ProductVariantCreatorStep } from "./types";
const selectedAttributes = [1, 4, 5].map(index => attributes[index]);
@ -52,7 +52,7 @@ const errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[] = [
}
];
const props: ProductVariantCreateContentProps = {
const props: ProductVariantCreatorContentProps = {
attributes,
currencySymbol: "USD",
data: {
@ -68,56 +68,43 @@ const props: ProductVariantCreateContentProps = {
},
dispatchFormDataAction: () => undefined,
errors: [],
onStepClick: () => undefined,
step: "values"
step: ProductVariantCreatorStep.values
};
storiesOf("Views / Products / Create multiple variants", module)
.addDecorator(storyFn => (
<Card
style={{
margin: "auto",
overflow: "visible",
width: 800
}}
>
<CardContent>{storyFn()}</CardContent>
</Card>
))
.addDecorator(storyFn => <Container>{storyFn()}</Container>)
.addDecorator(Decorator)
.add("choose values", () => <ProductVariantCreateContent {...props} />)
.add("choose values", () => <ProductVariantCreatorContent {...props} />)
.add("prices and SKU", () => (
<ProductVariantCreateContent {...props} step="prices" />
<ProductVariantCreatorContent
{...props}
step={ProductVariantCreatorStep.prices}
/>
));
storiesOf("Views / Products / Create multiple variants / summary", module)
.addDecorator(storyFn => (
<Card
style={{
margin: "auto",
overflow: "visible",
width: 800
}}
>
<CardContent>{storyFn()}</CardContent>
</Card>
))
.addDecorator(storyFn => <Container>{storyFn()}</Container>)
.addDecorator(Decorator)
.add("default", () => (
<ProductVariantCreateContent {...props} step="summary" />
<ProductVariantCreatorContent
{...props}
step={ProductVariantCreatorStep.summary}
/>
))
.add("errors", () => (
<ProductVariantCreateContent {...props} step="summary" errors={errors} />
<ProductVariantCreatorContent
{...props}
step={ProductVariantCreatorStep.summary}
errors={errors}
/>
));
storiesOf("Views / Products / Create multiple variants", module)
.addDecorator(Decorator)
.add("interactive", () => (
<ProductVariantCreateDialog
<ProductVariantCreatorPage
{...props}
defaultPrice="10.99"
open={true}
onClose={() => undefined}
onSubmit={() => undefined}
/>
));

View file

@ -0,0 +1,122 @@
import React from "react";
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate";
import { isSelected } from "@saleor/utils/lists";
import { ProductVariantCreateFormData } from "./form";
import ProductVariantCreatePrices from "./ProductVariantCreatorPrices";
import ProductVariantCreateSummary from "./ProductVariantCreatorSummary";
import ProductVariantCreateValues from "./ProductVariantCreatorValues";
import { ProductVariantCreateReducerAction } from "./reducer";
import { ProductVariantCreatorStep } from "./types";
export interface ProductVariantCreatorContentProps {
attributes: ProductDetails_product_productType_variantAttributes[];
currencySymbol: string;
data: ProductVariantCreateFormData;
dispatchFormDataAction: React.Dispatch<ProductVariantCreateReducerAction>;
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[];
step: ProductVariantCreatorStep;
}
const ProductVariantCreatorContent: React.FC<ProductVariantCreatorContentProps> = props => {
const {
attributes,
currencySymbol,
data,
dispatchFormDataAction,
errors,
step
} = props;
const selectedAttributes = attributes.filter(attribute =>
isSelected(
attribute.id,
data.attributes.map(dataAttribute => dataAttribute.id),
(a, b) => a === b
)
);
return (
<>
{step === ProductVariantCreatorStep.values && (
<ProductVariantCreateValues
attributes={selectedAttributes}
data={data}
onValueClick={(attributeId, valueId) =>
dispatchFormDataAction({
attributeId,
type: "selectValue",
valueId
})
}
/>
)}
{step === ProductVariantCreatorStep.prices && (
<ProductVariantCreatePrices
attributes={selectedAttributes}
currencySymbol={currencySymbol}
data={data}
onApplyPriceOrStockChange={(all, type) =>
dispatchFormDataAction({
all,
type: type === "price" ? "applyPriceToAll" : "applyStockToAll"
})
}
onApplyToAllChange={(value, type) =>
dispatchFormDataAction({
type:
type === "price"
? "changeApplyPriceToAllValue"
: "changeApplyStockToAllValue",
value
})
}
onAttributeSelect={(attributeId, type) =>
dispatchFormDataAction({
attributeId,
type:
type === "price"
? "changeApplyPriceToAttributeId"
: "changeApplyStockToAttributeId"
})
}
onAttributeValueChange={(valueId, value, type) =>
dispatchFormDataAction({
type:
type === "price"
? "changeAttributeValuePrice"
: "changeAttributeValueStock",
value,
valueId
})
}
/>
)}
{step === ProductVariantCreatorStep.summary && (
<ProductVariantCreateSummary
attributes={selectedAttributes}
currencySymbol={currencySymbol}
data={data}
errors={errors}
onVariantDataChange={(variantIndex, field, value) =>
dispatchFormDataAction({
field,
type: "changeVariantData",
value,
variantIndex
})
}
onVariantDelete={variantIndex =>
dispatchFormDataAction({
type: "deleteVariant",
variantIndex
})
}
/>
)}
</>
);
};
ProductVariantCreatorContent.displayName = "ProductVariantCreatorContent";
export default ProductVariantCreatorContent;

View file

@ -1,22 +1,19 @@
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
import { makeStyles } from "@material-ui/core/styles";
import React from "react";
import { FormattedMessage } from "react-intl";
import { FormattedMessage, useIntl, IntlShape } from "react-intl";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen";
import useWizard from "@saleor/hooks/useWizard";
import PageHeader from "@saleor/components/PageHeader";
import Container from "@saleor/components/Container";
import { ProductVariantBulkCreateInput } from "../../../types/globalTypes";
import { createInitialForm, ProductVariantCreateFormData } from "./form";
import ProductVariantCreateContent, {
ProductVariantCreateContentProps
} from "./ProductVariantCreateContent";
import ProductVariantCreatorContent, {
ProductVariantCreatorContentProps
} from "./ProductVariantCreatorContent";
import reduceProductVariantCreateFormData from "./reducer";
import { ProductVariantCreateStep } from "./types";
import { ProductVariantCreatorStep } from "./types";
import ProductVariantCreateTabs from "./ProductVariantCreatorTabs";
const useStyles = makeStyles(
theme => ({
@ -27,22 +24,19 @@ const useStyles = makeStyles(
overflowX: "visible",
overflowY: "hidden",
width: 800
},
spacer: {
flex: 1
}
}),
{ name: "ProductVariantCreateDialog" }
{ name: "ProductVariantCreatePage" }
);
function canHitNext(
step: ProductVariantCreateStep,
step: ProductVariantCreatorStep,
data: ProductVariantCreateFormData
): boolean {
switch (step) {
case ProductVariantCreateStep.values:
case ProductVariantCreatorStep.values:
return data.attributes.every(attribute => attribute.values.length > 0);
case ProductVariantCreateStep.prices:
case ProductVariantCreatorStep.prices:
if (data.price.all) {
if (data.price.value === "") {
return false;
@ -70,7 +64,7 @@ function canHitNext(
}
return true;
case ProductVariantCreateStep.summary:
case ProductVariantCreatorStep.summary:
return data.variants.every(variant => variant.sku !== "");
default:
@ -78,34 +72,45 @@ function canHitNext(
}
}
export interface ProductVariantCreateDialogProps
export interface ProductVariantCreatePageProps
extends Omit<
ProductVariantCreateContentProps,
ProductVariantCreatorContentProps,
"data" | "dispatchFormDataAction" | "step" | "onStepClick"
> {
defaultPrice: string;
open: boolean;
onClose: () => void;
onSubmit: (data: ProductVariantBulkCreateInput[]) => void;
}
const ProductVariantCreateDialog: React.FC<ProductVariantCreateDialogProps> = props => {
const {
attributes,
defaultPrice,
errors: apiErrors,
open,
onClose,
onSubmit,
...contentProps
} = props;
function getTitle(step: ProductVariantCreatorStep, intl: IntlShape): string {
switch (step) {
case ProductVariantCreatorStep.values:
return intl.formatMessage({
defaultMessage: "Choose Values",
description: "product attribute values, page title"
});
case ProductVariantCreatorStep.prices:
return intl.formatMessage({
defaultMessage: "Price and SKUs",
description: "page title"
});
case ProductVariantCreatorStep.summary:
return intl.formatMessage({
defaultMessage: "Summary",
description: "page title"
});
}
}
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = props => {
const { attributes, defaultPrice, errors, onSubmit, ...contentProps } = props;
const classes = useStyles(props);
const intl = useIntl();
const [step, { next, prev, set: setStep }] = useWizard<
ProductVariantCreateStep
>(ProductVariantCreateStep.values, [
ProductVariantCreateStep.values,
ProductVariantCreateStep.prices,
ProductVariantCreateStep.summary
ProductVariantCreatorStep
>(ProductVariantCreatorStep.values, [
ProductVariantCreatorStep.values,
ProductVariantCreatorStep.prices,
ProductVariantCreatorStep.summary
]);
const [data, dispatchFormDataAction] = React.useReducer(
@ -121,40 +126,11 @@ const ProductVariantCreateDialog: React.FC<ProductVariantCreateDialogProps> = pr
React.useEffect(reloadForm, [attributes.length]);
useModalDialogOpen(open, {
onClose: () => {
reloadForm();
setStep(ProductVariantCreateStep.values);
}
});
const errors = useModalDialogErrors(apiErrors, open);
return (
<Dialog open={open} maxWidth="md">
<DialogTitle>
<FormattedMessage
defaultMessage="Assign Attribute"
description="dialog header"
/>
</DialogTitle>
<DialogContent className={classes.content}>
<ProductVariantCreateContent
{...contentProps}
attributes={attributes}
data={data}
dispatchFormDataAction={dispatchFormDataAction}
errors={errors}
step={step}
onStepClick={step => setStep(step)}
/>
</DialogContent>
<DialogActions>
<Button className={classes.button} onClick={onClose}>
<FormattedMessage defaultMessage="Cancel" description="button" />
</Button>
<div className={classes.spacer} />
{step !== ProductVariantCreateStep.values && (
<Container>
<ProductVariantCreateTabs step={step} onStepClick={setStep} />
<PageHeader title={getTitle(step, intl)}>
{step !== ProductVariantCreatorStep.values && (
<Button className={classes.button} color="primary" onClick={prev}>
<FormattedMessage
defaultMessage="Previous"
@ -162,7 +138,7 @@ const ProductVariantCreateDialog: React.FC<ProductVariantCreateDialogProps> = pr
/>
</Button>
)}
{step !== ProductVariantCreateStep.summary ? (
{step !== ProductVariantCreatorStep.summary ? (
<Button
className={classes.button}
color="primary"
@ -186,10 +162,18 @@ const ProductVariantCreateDialog: React.FC<ProductVariantCreateDialogProps> = pr
/>
</Button>
)}
</DialogActions>
</Dialog>
</PageHeader>
<ProductVariantCreatorContent
{...contentProps}
attributes={attributes}
data={data}
dispatchFormDataAction={dispatchFormDataAction}
errors={errors}
step={step}
/>
</Container>
);
};
ProductVariantCreateDialog.displayName = "ProductVariantCreateDialog";
export default ProductVariantCreateDialog;
ProductVariantCreatePage.displayName = "ProductVariantCreatePage";
export default ProductVariantCreatePage;

View file

@ -0,0 +1,331 @@
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Radio from "@material-ui/core/Radio";
import RadioGroup from "@material-ui/core/RadioGroup";
import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import FormSpacer from "@saleor/components/FormSpacer";
import Grid from "@saleor/components/Grid";
import Hr from "@saleor/components/Hr";
import SingleSelectField from "@saleor/components/SingleSelectField";
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
import CardTitle from "@saleor/components/CardTitle";
import CardSpacer from "@saleor/components/CardSpacer";
import { ProductVariantCreateFormData } from "./form";
const useStyles = makeStyles(
theme => ({
hr: {
marginBottom: theme.spacing(),
marginTop: theme.spacing(0.5)
},
hrAttribute: {
marginTop: theme.spacing(2)
},
label: {
alignSelf: "center"
},
shortInput: {
width: "33%"
}
}),
{ name: "ProductVariantCreatorPrices" }
);
export type PriceOrStock = "price" | "stock";
export interface ProductVariantCreatorPricesProps {
attributes: ProductDetails_product_productType_variantAttributes[];
currencySymbol: string;
data: ProductVariantCreateFormData;
onApplyPriceOrStockChange: (applyToAll: boolean, type: PriceOrStock) => void;
onApplyToAllChange: (value: string, type: PriceOrStock) => void;
onAttributeSelect: (id: string, type: PriceOrStock) => void;
onAttributeValueChange: (
id: string,
value: string,
type: PriceOrStock
) => void;
}
const ProductVariantCreatorPrices: React.FC<ProductVariantCreatorPricesProps> = props => {
const {
attributes,
currencySymbol,
data,
onApplyPriceOrStockChange,
onApplyToAllChange,
onAttributeSelect,
onAttributeValueChange
} = props;
const classes = useStyles(props);
const intl = useIntl();
const attributeChoices = attributes.map(attribute => ({
label: attribute.name,
value: attribute.id
}));
const priceAttributeValues = data.price.all
? null
: data.price.attribute
? 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
? null
: data.stock.attribute
? 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 (
<>
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Price",
description: "variant price, header"
})}
/>
<CardContent>
<RadioGroup
value={data.price.all ? "applyToAll" : "applyToAttribute"}
>
<FormControlLabel
value="applyToAll"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply single price to all SKUs"
})}
onChange={() => onApplyPriceOrStockChange(true, "price")}
/>
<FormSpacer />
<TextField
className={classes.shortInput}
inputProps={{
min: 0,
type: "number"
}}
InputProps={{
endAdornment: currencySymbol
}}
label={intl.formatMessage({
defaultMessage: "Price",
id: "productVariantCreatePricesPriceInputLabel"
})}
value={data.price.value}
onChange={event =>
onApplyToAllChange(event.target.value, "price")
}
/>
<FormSpacer />
<FormControlLabel
value="applyToAttribute"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply unique prices by attribute to each SKU"
})}
onChange={() => onApplyPriceOrStockChange(false, "price")}
/>
</RadioGroup>
{!data.price.all && (
<>
<FormSpacer />
<Grid variant="uniform">
<div className={classes.label}>
<Typography>
<FormattedMessage
defaultMessage="Choose attribute"
description="variant attribute"
/>
</Typography>
</div>
<div>
<SingleSelectField
choices={attributeChoices}
label={intl.formatMessage({
defaultMessage: "Attribute",
description: "variant attribute"
})}
value={data.price.attribute}
onChange={event =>
onAttributeSelect(event.target.value, "price")
}
/>
</div>
</Grid>
{priceAttributeValues &&
priceAttributeValues.map(attributeValue => (
<React.Fragment key={attributeValue.id}>
<Hr className={classes.hrAttribute} />
<FormSpacer />
<Grid variant="uniform">
<div className={classes.label}>
<Typography>{attributeValue.name}</Typography>
</div>
<div>
<TextField
label={intl.formatMessage({
defaultMessage: "Price",
description: "variant price",
id: "productVariantCreatePricesSetPricePlaceholder"
})}
inputProps={{
min: 0,
type: "number"
}}
InputProps={{
endAdornment: currencySymbol
}}
fullWidth
value={
data.price.values.find(
value => value.slug === attributeValue.slug
).value
}
onChange={event =>
onAttributeValueChange(
attributeValue.slug,
event.target.value,
"price"
)
}
/>
</div>
</Grid>
</React.Fragment>
))}
</>
)}
</CardContent>
</Card>
<CardSpacer />
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Stock",
description: "variant stock, header"
})}
/>
<CardContent>
<RadioGroup
value={data.stock.all ? "applyToAll" : "applyToAttribute"}
>
<FormControlLabel
value="applyToAll"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply single stock to all SKUs"
})}
onChange={() => onApplyPriceOrStockChange(true, "stock")}
/>
<FormSpacer />
<TextField
className={classes.shortInput}
inputProps={{
min: 0,
type: "number"
}}
label={intl.formatMessage({
defaultMessage: "Stock",
id: "productVariantCreatePricesStockInputLabel"
})}
value={data.stock.value}
onChange={event =>
onApplyToAllChange(event.target.value, "stock")
}
/>
<FormSpacer />
<FormControlLabel
value="applyToAttribute"
control={<Radio color="primary" />}
label={intl.formatMessage({
defaultMessage: "Apply unique stock by attribute to each SKU"
})}
onChange={() => onApplyPriceOrStockChange(false, "stock")}
/>
</RadioGroup>
{!data.stock.all && (
<>
<FormSpacer />
<Grid variant="uniform">
<div className={classes.label}>
<Typography>
<FormattedMessage
defaultMessage="Choose attribute"
description="variant attribute"
/>
</Typography>
</div>
<div>
<SingleSelectField
choices={attributeChoices}
label={intl.formatMessage({
defaultMessage: "Attribute",
description: "variant attribute"
})}
value={data.stock.attribute}
onChange={event =>
onAttributeSelect(event.target.value, "stock")
}
/>
</div>
</Grid>
{stockAttributeValues &&
stockAttributeValues.map(attributeValue => (
<React.Fragment key={attributeValue.id}>
<Hr className={classes.hrAttribute} />
<FormSpacer />
<Grid variant="uniform">
<div className={classes.label}>
<Typography>{attributeValue.name}</Typography>
</div>
<div>
<TextField
label={intl.formatMessage({
defaultMessage: "Stock",
description: "variant stock",
id: "productVariantCreatePricesSetStockPlaceholder"
})}
fullWidth
value={
data.stock.values.find(
value => value.slug === attributeValue.slug
).value
}
onChange={event =>
onAttributeValueChange(
attributeValue.slug,
event.target.value,
"stock"
)
}
/>
</div>
</Grid>
</React.Fragment>
))}
</>
)}
</CardContent>
</Card>
</>
);
};
ProductVariantCreatorPrices.displayName = "ProductVariantCreatorPrices";
export default ProductVariantCreatorPrices;

View file

@ -3,25 +3,26 @@ import cyan from "@material-ui/core/colors/cyan";
import green from "@material-ui/core/colors/green";
import purple from "@material-ui/core/colors/purple";
import yellow from "@material-ui/core/colors/yellow";
import Card from "@material-ui/core/Card";
import IconButton from "@material-ui/core/IconButton";
import { makeStyles } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography";
import DeleteIcon from "@material-ui/icons/Delete";
import classNames from "classnames";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import Hr from "@saleor/components/Hr";
import { ProductVariantBulkCreate_productVariantBulkCreate_errors } from "@saleor/products/types/ProductVariantBulkCreate";
import { ProductVariantBulkCreateInput } from "@saleor/types/globalTypes";
import { getFormErrors } from "@saleor/utils/errors";
import { getBulkProductErrorMessage } from "@saleor/utils/errors/product";
import CardTitle from "@saleor/components/CardTitle";
import { commonMessages } from "@saleor/intl";
import { ProductDetails_product_productType_variantAttributes } from "../../types/ProductDetails";
import { ProductVariantCreateFormData } from "./form";
import { VariantField } from "./reducer";
export interface ProductVariantCreateSummaryProps {
export interface ProductVariantCreatorSummaryProps {
attributes: ProductDetails_product_productType_variantAttributes[];
currencySymbol: string;
data: ProductVariantCreateFormData;
@ -81,11 +82,11 @@ const useStyles = makeStyles(
borderBottom: `1px solid ${theme.palette.divider}`,
display: "grid",
gridTemplateColumns: "1fr 180px 120px 180px 64px",
padding: theme.spacing(1, 0)
padding: theme.spacing(1, 1, 1, 3)
}
}),
{
name: "ProductVariantCreateSummary"
name: "ProductVariantCreatorSummary"
}
);
@ -108,7 +109,7 @@ function getVariantName(
);
}
const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> = props => {
const ProductVariantCreatorSummary: React.FC<ProductVariantCreatorSummaryProps> = props => {
const {
attributes,
currencySymbol,
@ -121,14 +122,8 @@ const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> =
const intl = useIntl();
return (
<>
<Typography color="textSecondary" variant="h5">
<FormattedMessage
defaultMessage="You will create variants below"
description="header"
/>
</Typography>
<Hr className={classes.hr} />
<Card>
<CardTitle title={intl.formatMessage(commonMessages.summary)} />
<div>
<div className={classes.row}>
<div
@ -287,9 +282,9 @@ const ProductVariantCreateSummary: React.FC<ProductVariantCreateSummaryProps> =
);
})}
</div>
</>
</Card>
);
};
ProductVariantCreateSummary.displayName = "ProductVariantCreateSummary";
export default ProductVariantCreateSummary;
ProductVariantCreatorSummary.displayName = "ProductVariantCreatorSummary";
export default ProductVariantCreatorSummary;

View file

@ -4,11 +4,11 @@ import classNames from "classnames";
import React from "react";
import { IntlShape, useIntl } from "react-intl";
import { ProductVariantCreateStep } from "./types";
import { ProductVariantCreatorStep } from "./types";
interface Step {
label: string;
value: ProductVariantCreateStep;
value: ProductVariantCreatorStep;
}
function getSteps(intl: IntlShape): Step[] {
return [
@ -17,21 +17,21 @@ function getSteps(intl: IntlShape): Step[] {
defaultMessage: "Select Values",
description: "attribute values, variant creation step"
}),
value: ProductVariantCreateStep.values
value: ProductVariantCreatorStep.values
},
{
label: intl.formatMessage({
defaultMessage: "Prices and SKU",
description: "variant creation step"
}),
value: ProductVariantCreateStep.prices
value: ProductVariantCreatorStep.prices
},
{
label: intl.formatMessage({
defaultMessage: "Summary",
description: "variant creation step"
}),
value: ProductVariantCreateStep.summary
value: ProductVariantCreatorStep.summary
}
];
}
@ -62,16 +62,16 @@ const useStyles = makeStyles(
}
}),
{
name: "ProductVariantCreateTabs"
name: "ProductVariantCreatorTabs"
}
);
export interface ProductVariantCreateTabsProps {
step: ProductVariantCreateStep;
onStepClick: (step: ProductVariantCreateStep) => void;
export interface ProductVariantCreatorTabsProps {
step: ProductVariantCreatorStep;
onStepClick: (step: ProductVariantCreatorStep) => void;
}
const ProductVariantCreateTabs: React.FC<ProductVariantCreateTabsProps> = props => {
const ProductVariantCreatorTabs: React.FC<ProductVariantCreatorTabsProps> = props => {
const { step: currentStep, onStepClick } = props;
const classes = useStyles(props);
const intl = useIntl();
@ -102,5 +102,5 @@ const ProductVariantCreateTabs: React.FC<ProductVariantCreateTabsProps> = props
);
};
ProductVariantCreateTabs.displayName = "ProductVariantCreateTabs";
export default ProductVariantCreateTabs;
ProductVariantCreatorTabs.displayName = "ProductVariantCreatorTabs";
export default ProductVariantCreatorTabs;

View file

@ -0,0 +1,75 @@
import makeStyles from "@material-ui/core/styles/makeStyles";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import React from "react";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import Debounce from "@saleor/components/Debounce";
import Skeleton from "@saleor/components/Skeleton";
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
import { isSelected } from "@saleor/utils/lists";
import CardTitle from "@saleor/components/CardTitle";
import CardSpacer from "@saleor/components/CardSpacer";
import { ProductVariantCreateFormData } from "./form";
export interface ProductVariantCreatorValuesProps {
attributes: ProductDetails_product_productType_variantAttributes[];
data: ProductVariantCreateFormData;
onValueClick: (attributeId: string, valueId: string) => void;
}
const useStyles = makeStyles(
theme => ({
valueContainer: {
display: "grid",
gridColumnGap: theme.spacing(3),
gridTemplateColumns: "repeat(5, 1fr)"
}
}),
{ name: "ProductVariantCreatorValues" }
);
const ProductVariantCreatorValues: React.FC<ProductVariantCreatorValuesProps> = props => {
const { attributes, data, onValueClick } = props;
const classes = useStyles(props);
return (
<>
{attributes.map(attribute => (
<>
<Card key={attribute.id}>
<CardTitle title={attribute?.name || <Skeleton />} />
<CardContent className={classes.valueContainer}>
{attribute.values.map(value => (
<Debounce
debounceFn={() => onValueClick(attribute.id, value.slug)}
time={100}
key={value.slug}
>
{change => (
<ControlledCheckbox
checked={isSelected(
value.slug,
data.attributes.find(
dataAttribute => attribute.id === dataAttribute.id
).values,
(a, b) => a === b
)}
name={`value:${value.slug}`}
label={value.name}
onChange={change}
/>
)}
</Debounce>
))}
</CardContent>
</Card>
<CardSpacer />
</>
))}
</>
);
};
ProductVariantCreatorValues.displayName = "ProductVariantCreatorValues";
export default ProductVariantCreatorValues;

View file

@ -97,9 +97,10 @@ export function createVariants(
) {
return [];
}
const variants = createVariantFlatMatrixDimension([[]], data.attributes).map(
variant => createVariant(data, variant)
);
const variants = createVariantFlatMatrixDimension(
[[]],
data.attributes
).map(variant => createVariant(data, variant));
return variants;
}

View file

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

View file

@ -0,0 +1,5 @@
export enum ProductVariantCreatorStep {
values,
prices,
summary
}

View file

@ -7,7 +7,6 @@ import {
TypedProductImageCreateMutation,
TypedProductImageDeleteMutation,
TypedProductUpdateMutation,
TypedProductVariantBulkCreateMutation,
TypedProductVariantBulkDeleteMutation,
TypedSimpleProductUpdateMutation
} from "../mutations";
@ -26,10 +25,6 @@ import {
ProductImageReorderVariables
} from "../types/ProductImageReorder";
import { ProductUpdate, ProductUpdateVariables } from "../types/ProductUpdate";
import {
ProductVariantBulkCreate,
ProductVariantBulkCreateVariables
} from "../types/ProductVariantBulkCreate";
import {
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
@ -43,10 +38,6 @@ import ProductImagesReorderProvider from "./ProductImagesReorder";
interface ProductUpdateOperationsProps {
product: ProductDetails_product;
children: (props: {
bulkProductVariantCreate: PartialMutationProviderOutput<
ProductVariantBulkCreate,
ProductVariantBulkCreateVariables
>;
bulkProductVariantDelete: PartialMutationProviderOutput<
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
@ -76,7 +67,6 @@ interface ProductUpdateOperationsProps {
SimpleProductUpdateVariables
>;
}) => React.ReactNode;
onBulkProductVariantCreate?: (data: ProductVariantBulkCreate) => void;
onBulkProductVariantDelete?: (data: ProductVariantBulkDelete) => void;
onDelete?: (data: ProductDelete) => void;
onImageCreate?: (data: ProductImageCreate) => void;
@ -88,7 +78,6 @@ interface ProductUpdateOperationsProps {
const ProductUpdateOperations: React.FC<ProductUpdateOperationsProps> = ({
product,
children,
onBulkProductVariantCreate,
onBulkProductVariantDelete,
onDelete,
onImageDelete,
@ -121,40 +110,31 @@ const ProductUpdateOperations: React.FC<ProductUpdateOperationsProps> = ({
<TypedProductVariantBulkDeleteMutation
onCompleted={onBulkProductVariantDelete}
>
{(...bulkProductVariantDelete) => (
<TypedProductVariantBulkCreateMutation
onCompleted={onBulkProductVariantCreate}
>
{(...bulkProductVariantCreate) =>
children({
bulkProductVariantCreate: getMutationProviderData(
...bulkProductVariantCreate
),
bulkProductVariantDelete: getMutationProviderData(
...bulkProductVariantDelete
),
createProductImage: getMutationProviderData(
...createProductImage
),
deleteProduct: getMutationProviderData(
...deleteProduct
),
deleteProductImage: getMutationProviderData(
...deleteProductImage
),
reorderProductImages: getMutationProviderData(
...reorderProductImages
),
updateProduct: getMutationProviderData(
...updateProduct
),
updateSimpleProduct: getMutationProviderData(
...updateSimpleProduct
)
})
}
</TypedProductVariantBulkCreateMutation>
)}
{(...bulkProductVariantDelete) =>
children({
bulkProductVariantDelete: getMutationProviderData(
...bulkProductVariantDelete
),
createProductImage: getMutationProviderData(
...createProductImage
),
deleteProduct: getMutationProviderData(
...deleteProduct
),
deleteProductImage: getMutationProviderData(
...deleteProductImage
),
reorderProductImages: getMutationProviderData(
...reorderProductImages
),
updateProduct: getMutationProviderData(
...updateProduct
),
updateSimpleProduct: getMutationProviderData(
...updateSimpleProduct
)
})
}
</TypedProductVariantBulkDeleteMutation>
)}
</TypedSimpleProductUpdateMutation>

View file

@ -20,7 +20,8 @@ import {
productVariantEditPath,
ProductVariantEditUrlQueryParams,
ProductAddUrlQueryParams,
ProductVariantAddUrlQueryParams
ProductVariantAddUrlQueryParams,
productVariantCreatorPath
} from "./urls";
import ProductCreateComponent from "./views/ProductCreate";
import ProductImageComponent from "./views/ProductImage";
@ -28,6 +29,7 @@ import ProductListComponent from "./views/ProductList";
import ProductUpdateComponent from "./views/ProductUpdate";
import ProductVariantComponent from "./views/ProductVariant";
import ProductVariantCreateComponent from "./views/ProductVariantCreate";
import ProductVariantCreatorComponent from "./views/ProductVariantCreator";
const ProductList: React.FC<RouteComponentProps<any>> = ({ location }) => {
const qs = parseQs(location.search.substr(1));
@ -100,6 +102,12 @@ const ProductVariantCreate: React.FC<RouteComponentProps<any>> = ({
);
};
const ProductVariantCreator: React.FC<RouteComponentProps<{
id: string;
}>> = ({ match }) => (
<ProductVariantCreatorComponent id={decodeURIComponent(match.params.id)} />
);
const ProductCreate: React.FC<RouteComponentProps> = ({ location }) => {
const qs = parseQs(location.search.substr(1));
const params: ProductAddUrlQueryParams = qs;
@ -116,6 +124,10 @@ const Component = () => {
<Switch>
<Route exact path={productListPath} component={ProductList} />
<Route exact path={productAddPath} component={ProductCreate} />
<Route
path={productVariantCreatorPath(":id")}
component={ProductVariantCreator}
/>
<Route
exact
path={productVariantAddPath(":id")}

View file

@ -543,7 +543,7 @@ export const ProductVariantBulkCreateMutation = gql`
}
}
`;
export const TypedProductVariantBulkCreateMutation = TypedMutation<
export const useProductVariantBulkCreateMutation = makeMutation<
ProductVariantBulkCreate,
ProductVariantBulkCreateVariables
>(ProductVariantBulkCreateMutation);

View file

@ -27,6 +27,10 @@ import {
InitialProductFilterData,
InitialProductFilterDataVariables
} from "./types/InitialProductFilterData";
import {
CreateMultipleVariantsData,
CreateMultipleVariantsDataVariables
} from "./types/CreateMultipleVariantsData";
export const stockFragment = gql`
fragment StockFragment on Stock {
@ -74,12 +78,50 @@ export const productFragment = gql`
}
}
`;
const productVariantAttributesFragment = gql`
fragment ProductVariantAttributesFragment on Product {
id
attributes {
attribute {
id
slug
name
inputType
valueRequired
values {
id
name
slug
}
}
values {
id
name
slug
}
}
productType {
variantAttributes {
id
name
values {
id
name
slug
}
}
}
}
`;
export const productFragmentDetails = gql`
${fragmentProductImage}
${fragmentMoney}
${productVariantAttributesFragment}
${stockFragment}
fragment Product on Product {
id
...ProductVariantAttributesFragment
name
descriptionJson
seoTitle
@ -111,25 +153,6 @@ export const productFragmentDetails = gql`
isPublished
chargeTaxes
publicationDate
attributes {
attribute {
id
slug
name
inputType
valueRequired
values {
id
name
slug
}
}
values {
id
name
slug
}
}
pricing {
priceRange {
start {
@ -332,17 +355,6 @@ const productDetailsQuery = gql`
query ProductDetails($id: ID!) {
product(id: $id) {
...Product
productType {
variantAttributes {
id
name
values {
id
name
slug
}
}
}
}
}
`;
@ -464,3 +476,20 @@ export const AvailableInGridAttributesQuery = TypedQuery<
AvailableInGridAttributes,
AvailableInGridAttributesVariables
>(availableInGridAttributes);
const createMultipleVariantsData = gql`
${fragmentMoney}
${productVariantAttributesFragment}
query CreateMultipleVariantsData($id: ID!) {
product(id: $id) {
...ProductVariantAttributesFragment
basePrice {
...Money
}
}
}
`;
export const useCreateMultipleVariantsData = makeQuery<
CreateMultipleVariantsData,
CreateMultipleVariantsDataVariables
>(createMultipleVariantsData);

View file

@ -0,0 +1,80 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AttributeInputTypeEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL query operation: CreateMultipleVariantsData
// ====================================================
export interface CreateMultipleVariantsData_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface CreateMultipleVariantsData_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (CreateMultipleVariantsData_product_attributes_attribute_values | null)[] | null;
}
export interface CreateMultipleVariantsData_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface CreateMultipleVariantsData_product_attributes {
__typename: "SelectedAttribute";
attribute: CreateMultipleVariantsData_product_attributes_attribute;
values: (CreateMultipleVariantsData_product_attributes_values | null)[];
}
export interface CreateMultipleVariantsData_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface CreateMultipleVariantsData_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (CreateMultipleVariantsData_product_productType_variantAttributes_values | null)[] | null;
}
export interface CreateMultipleVariantsData_product_productType {
__typename: "ProductType";
variantAttributes: (CreateMultipleVariantsData_product_productType_variantAttributes | null)[] | null;
}
export interface CreateMultipleVariantsData_product_basePrice {
__typename: "Money";
amount: number;
currency: string;
}
export interface CreateMultipleVariantsData_product {
__typename: "Product";
id: string;
attributes: CreateMultipleVariantsData_product_attributes[];
productType: CreateMultipleVariantsData_product_productType;
basePrice: CreateMultipleVariantsData_product_basePrice | null;
}
export interface CreateMultipleVariantsData {
product: CreateMultipleVariantsData_product | null;
}
export interface CreateMultipleVariantsDataVariables {
id: string;
}

View file

@ -8,6 +8,58 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes";
// GraphQL fragment: Product
// ====================================================
export interface Product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface Product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (Product_attributes_attribute_values | null)[] | null;
}
export interface Product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface Product_attributes {
__typename: "SelectedAttribute";
attribute: Product_attributes_attribute;
values: (Product_attributes_values | null)[];
}
export interface Product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface Product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (Product_productType_variantAttributes_values | null)[] | null;
}
export interface Product_productType {
__typename: "ProductType";
variantAttributes: (Product_productType_variantAttributes | null)[] | null;
id: string;
name: string;
hasVariants: boolean;
}
export interface Product_category {
__typename: "Category";
id: string;
@ -50,36 +102,6 @@ export interface Product_purchaseCost {
stop: Product_purchaseCost_stop | null;
}
export interface Product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface Product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (Product_attributes_attribute_values | null)[] | null;
}
export interface Product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface Product_attributes {
__typename: "SelectedAttribute";
attribute: Product_attributes_attribute;
values: (Product_attributes_values | null)[];
}
export interface Product_pricing_priceRange_start_net {
__typename: "Money";
amount: number;
@ -151,16 +173,11 @@ export interface Product_variants {
trackInventory: boolean;
}
export interface Product_productType {
__typename: "ProductType";
id: string;
name: string;
hasVariants: boolean;
}
export interface Product {
__typename: "Product";
id: string;
attributes: Product_attributes[];
productType: Product_productType;
name: string;
descriptionJson: any;
seoTitle: string | null;
@ -174,9 +191,7 @@ export interface Product {
isPublished: boolean;
chargeTaxes: boolean;
publicationDate: any | null;
attributes: Product_attributes[];
pricing: Product_pricing | null;
images: (Product_images | null)[] | null;
variants: (Product_variants | null)[] | null;
productType: Product_productType;
}

View file

@ -14,6 +14,58 @@ export interface ProductCreate_productCreate_errors {
field: string | null;
}
export interface ProductCreate_productCreate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductCreate_productCreate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductCreate_productCreate_product_attributes_attribute_values | null)[] | null;
}
export interface ProductCreate_productCreate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductCreate_productCreate_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductCreate_productCreate_product_attributes_attribute;
values: (ProductCreate_productCreate_product_attributes_values | null)[];
}
export interface ProductCreate_productCreate_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductCreate_productCreate_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (ProductCreate_productCreate_product_productType_variantAttributes_values | null)[] | null;
}
export interface ProductCreate_productCreate_product_productType {
__typename: "ProductType";
variantAttributes: (ProductCreate_productCreate_product_productType_variantAttributes | null)[] | null;
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductCreate_productCreate_product_category {
__typename: "Category";
id: string;
@ -56,36 +108,6 @@ export interface ProductCreate_productCreate_product_purchaseCost {
stop: ProductCreate_productCreate_product_purchaseCost_stop | null;
}
export interface ProductCreate_productCreate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductCreate_productCreate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductCreate_productCreate_product_attributes_attribute_values | null)[] | null;
}
export interface ProductCreate_productCreate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductCreate_productCreate_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductCreate_productCreate_product_attributes_attribute;
values: (ProductCreate_productCreate_product_attributes_values | null)[];
}
export interface ProductCreate_productCreate_product_pricing_priceRange_start_net {
__typename: "Money";
amount: number;
@ -157,16 +179,11 @@ export interface ProductCreate_productCreate_product_variants {
trackInventory: boolean;
}
export interface ProductCreate_productCreate_product_productType {
__typename: "ProductType";
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductCreate_productCreate_product {
__typename: "Product";
id: string;
attributes: ProductCreate_productCreate_product_attributes[];
productType: ProductCreate_productCreate_product_productType;
name: string;
descriptionJson: any;
seoTitle: string | null;
@ -180,11 +197,9 @@ export interface ProductCreate_productCreate_product {
isPublished: boolean;
chargeTaxes: boolean;
publicationDate: any | null;
attributes: ProductCreate_productCreate_product_attributes[];
pricing: ProductCreate_productCreate_product_pricing | null;
images: (ProductCreate_productCreate_product_images | null)[] | null;
variants: (ProductCreate_productCreate_product_variants | null)[] | null;
productType: ProductCreate_productCreate_product_productType;
}
export interface ProductCreate_productCreate {

View file

@ -8,6 +8,58 @@ import { AttributeInputTypeEnum } from "./../../types/globalTypes";
// GraphQL query operation: ProductDetails
// ====================================================
export interface ProductDetails_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductDetails_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductDetails_product_attributes_attribute_values | null)[] | null;
}
export interface ProductDetails_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductDetails_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductDetails_product_attributes_attribute;
values: (ProductDetails_product_attributes_values | null)[];
}
export interface ProductDetails_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductDetails_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (ProductDetails_product_productType_variantAttributes_values | null)[] | null;
}
export interface ProductDetails_product_productType {
__typename: "ProductType";
variantAttributes: (ProductDetails_product_productType_variantAttributes | null)[] | null;
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductDetails_product_category {
__typename: "Category";
id: string;
@ -50,36 +102,6 @@ export interface ProductDetails_product_purchaseCost {
stop: ProductDetails_product_purchaseCost_stop | null;
}
export interface ProductDetails_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductDetails_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductDetails_product_attributes_attribute_values | null)[] | null;
}
export interface ProductDetails_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductDetails_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductDetails_product_attributes_attribute;
values: (ProductDetails_product_attributes_values | null)[];
}
export interface ProductDetails_product_pricing_priceRange_start_net {
__typename: "Money";
amount: number;
@ -151,31 +173,11 @@ export interface ProductDetails_product_variants {
trackInventory: boolean;
}
export interface ProductDetails_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductDetails_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (ProductDetails_product_productType_variantAttributes_values | null)[] | null;
}
export interface ProductDetails_product_productType {
__typename: "ProductType";
id: string;
name: string;
hasVariants: boolean;
variantAttributes: (ProductDetails_product_productType_variantAttributes | null)[] | null;
}
export interface ProductDetails_product {
__typename: "Product";
id: string;
attributes: ProductDetails_product_attributes[];
productType: ProductDetails_product_productType;
name: string;
descriptionJson: any;
seoTitle: string | null;
@ -189,11 +191,9 @@ export interface ProductDetails_product {
isPublished: boolean;
chargeTaxes: boolean;
publicationDate: any | null;
attributes: ProductDetails_product_attributes[];
pricing: ProductDetails_product_pricing | null;
images: (ProductDetails_product_images | null)[] | null;
variants: (ProductDetails_product_variants | null)[] | null;
productType: ProductDetails_product_productType;
}
export interface ProductDetails {

View file

@ -14,6 +14,58 @@ export interface ProductImageCreate_productImageCreate_errors {
field: string | null;
}
export interface ProductImageCreate_productImageCreate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageCreate_productImageCreate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductImageCreate_productImageCreate_product_attributes_attribute_values | null)[] | null;
}
export interface ProductImageCreate_productImageCreate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageCreate_productImageCreate_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductImageCreate_productImageCreate_product_attributes_attribute;
values: (ProductImageCreate_productImageCreate_product_attributes_values | null)[];
}
export interface ProductImageCreate_productImageCreate_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageCreate_productImageCreate_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (ProductImageCreate_productImageCreate_product_productType_variantAttributes_values | null)[] | null;
}
export interface ProductImageCreate_productImageCreate_product_productType {
__typename: "ProductType";
variantAttributes: (ProductImageCreate_productImageCreate_product_productType_variantAttributes | null)[] | null;
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductImageCreate_productImageCreate_product_category {
__typename: "Category";
id: string;
@ -56,36 +108,6 @@ export interface ProductImageCreate_productImageCreate_product_purchaseCost {
stop: ProductImageCreate_productImageCreate_product_purchaseCost_stop | null;
}
export interface ProductImageCreate_productImageCreate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageCreate_productImageCreate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductImageCreate_productImageCreate_product_attributes_attribute_values | null)[] | null;
}
export interface ProductImageCreate_productImageCreate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageCreate_productImageCreate_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductImageCreate_productImageCreate_product_attributes_attribute;
values: (ProductImageCreate_productImageCreate_product_attributes_values | null)[];
}
export interface ProductImageCreate_productImageCreate_product_pricing_priceRange_start_net {
__typename: "Money";
amount: number;
@ -157,16 +179,11 @@ export interface ProductImageCreate_productImageCreate_product_variants {
trackInventory: boolean;
}
export interface ProductImageCreate_productImageCreate_product_productType {
__typename: "ProductType";
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductImageCreate_productImageCreate_product {
__typename: "Product";
id: string;
attributes: ProductImageCreate_productImageCreate_product_attributes[];
productType: ProductImageCreate_productImageCreate_product_productType;
name: string;
descriptionJson: any;
seoTitle: string | null;
@ -180,11 +197,9 @@ export interface ProductImageCreate_productImageCreate_product {
isPublished: boolean;
chargeTaxes: boolean;
publicationDate: any | null;
attributes: ProductImageCreate_productImageCreate_product_attributes[];
pricing: ProductImageCreate_productImageCreate_product_pricing | null;
images: (ProductImageCreate_productImageCreate_product_images | null)[] | null;
variants: (ProductImageCreate_productImageCreate_product_variants | null)[] | null;
productType: ProductImageCreate_productImageCreate_product_productType;
}
export interface ProductImageCreate_productImageCreate {

View file

@ -14,6 +14,58 @@ export interface ProductImageUpdate_productImageUpdate_errors {
field: string | null;
}
export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductImageUpdate_productImageUpdate_product_attributes_attribute_values | null)[] | null;
}
export interface ProductImageUpdate_productImageUpdate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageUpdate_productImageUpdate_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductImageUpdate_productImageUpdate_product_attributes_attribute;
values: (ProductImageUpdate_productImageUpdate_product_attributes_values | null)[];
}
export interface ProductImageUpdate_productImageUpdate_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageUpdate_productImageUpdate_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (ProductImageUpdate_productImageUpdate_product_productType_variantAttributes_values | null)[] | null;
}
export interface ProductImageUpdate_productImageUpdate_product_productType {
__typename: "ProductType";
variantAttributes: (ProductImageUpdate_productImageUpdate_product_productType_variantAttributes | null)[] | null;
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductImageUpdate_productImageUpdate_product_category {
__typename: "Category";
id: string;
@ -56,36 +108,6 @@ export interface ProductImageUpdate_productImageUpdate_product_purchaseCost {
stop: ProductImageUpdate_productImageUpdate_product_purchaseCost_stop | null;
}
export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageUpdate_productImageUpdate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductImageUpdate_productImageUpdate_product_attributes_attribute_values | null)[] | null;
}
export interface ProductImageUpdate_productImageUpdate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductImageUpdate_productImageUpdate_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductImageUpdate_productImageUpdate_product_attributes_attribute;
values: (ProductImageUpdate_productImageUpdate_product_attributes_values | null)[];
}
export interface ProductImageUpdate_productImageUpdate_product_pricing_priceRange_start_net {
__typename: "Money";
amount: number;
@ -157,16 +179,11 @@ export interface ProductImageUpdate_productImageUpdate_product_variants {
trackInventory: boolean;
}
export interface ProductImageUpdate_productImageUpdate_product_productType {
__typename: "ProductType";
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductImageUpdate_productImageUpdate_product {
__typename: "Product";
id: string;
attributes: ProductImageUpdate_productImageUpdate_product_attributes[];
productType: ProductImageUpdate_productImageUpdate_product_productType;
name: string;
descriptionJson: any;
seoTitle: string | null;
@ -180,11 +197,9 @@ export interface ProductImageUpdate_productImageUpdate_product {
isPublished: boolean;
chargeTaxes: boolean;
publicationDate: any | null;
attributes: ProductImageUpdate_productImageUpdate_product_attributes[];
pricing: ProductImageUpdate_productImageUpdate_product_pricing | null;
images: (ProductImageUpdate_productImageUpdate_product_images | null)[] | null;
variants: (ProductImageUpdate_productImageUpdate_product_variants | null)[] | null;
productType: ProductImageUpdate_productImageUpdate_product_productType;
}
export interface ProductImageUpdate_productImageUpdate {

View file

@ -14,6 +14,58 @@ export interface ProductUpdate_productUpdate_errors {
field: string | null;
}
export interface ProductUpdate_productUpdate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductUpdate_productUpdate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null;
}
export interface ProductUpdate_productUpdate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductUpdate_productUpdate_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductUpdate_productUpdate_product_attributes_attribute;
values: (ProductUpdate_productUpdate_product_attributes_values | null)[];
}
export interface ProductUpdate_productUpdate_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductUpdate_productUpdate_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (ProductUpdate_productUpdate_product_productType_variantAttributes_values | null)[] | null;
}
export interface ProductUpdate_productUpdate_product_productType {
__typename: "ProductType";
variantAttributes: (ProductUpdate_productUpdate_product_productType_variantAttributes | null)[] | null;
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductUpdate_productUpdate_product_category {
__typename: "Category";
id: string;
@ -56,36 +108,6 @@ export interface ProductUpdate_productUpdate_product_purchaseCost {
stop: ProductUpdate_productUpdate_product_purchaseCost_stop | null;
}
export interface ProductUpdate_productUpdate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductUpdate_productUpdate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null;
}
export interface ProductUpdate_productUpdate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductUpdate_productUpdate_product_attributes {
__typename: "SelectedAttribute";
attribute: ProductUpdate_productUpdate_product_attributes_attribute;
values: (ProductUpdate_productUpdate_product_attributes_values | null)[];
}
export interface ProductUpdate_productUpdate_product_pricing_priceRange_start_net {
__typename: "Money";
amount: number;
@ -157,16 +179,11 @@ export interface ProductUpdate_productUpdate_product_variants {
trackInventory: boolean;
}
export interface ProductUpdate_productUpdate_product_productType {
__typename: "ProductType";
id: string;
name: string;
hasVariants: boolean;
}
export interface ProductUpdate_productUpdate_product {
__typename: "Product";
id: string;
attributes: ProductUpdate_productUpdate_product_attributes[];
productType: ProductUpdate_productUpdate_product_productType;
name: string;
descriptionJson: any;
seoTitle: string | null;
@ -180,11 +197,9 @@ export interface ProductUpdate_productUpdate_product {
isPublished: boolean;
chargeTaxes: boolean;
publicationDate: any | null;
attributes: ProductUpdate_productUpdate_product_attributes[];
pricing: ProductUpdate_productUpdate_product_pricing | null;
images: (ProductUpdate_productUpdate_product_images | null)[] | null;
variants: (ProductUpdate_productUpdate_product_variants | null)[] | null;
productType: ProductUpdate_productUpdate_product_productType;
}
export interface ProductUpdate_productUpdate {

View file

@ -0,0 +1,65 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { AttributeInputTypeEnum } from "./../../types/globalTypes";
// ====================================================
// GraphQL fragment: ProductVariantAttributesFragment
// ====================================================
export interface ProductVariantAttributesFragment_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductVariantAttributesFragment_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (ProductVariantAttributesFragment_attributes_attribute_values | null)[] | null;
}
export interface ProductVariantAttributesFragment_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductVariantAttributesFragment_attributes {
__typename: "SelectedAttribute";
attribute: ProductVariantAttributesFragment_attributes_attribute;
values: (ProductVariantAttributesFragment_attributes_values | null)[];
}
export interface ProductVariantAttributesFragment_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface ProductVariantAttributesFragment_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (ProductVariantAttributesFragment_productType_variantAttributes_values | null)[] | null;
}
export interface ProductVariantAttributesFragment_productType {
__typename: "ProductType";
variantAttributes: (ProductVariantAttributesFragment_productType_variantAttributes | null)[] | null;
}
export interface ProductVariantAttributesFragment {
__typename: "Product";
id: string;
attributes: ProductVariantAttributesFragment_attributes[];
productType: ProductVariantAttributesFragment_productType;
}

View file

@ -14,6 +14,58 @@ export interface SimpleProductUpdate_productUpdate_errors {
field: string | null;
}
export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface SimpleProductUpdate_productUpdate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (SimpleProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null;
}
export interface SimpleProductUpdate_productUpdate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface SimpleProductUpdate_productUpdate_product_attributes {
__typename: "SelectedAttribute";
attribute: SimpleProductUpdate_productUpdate_product_attributes_attribute;
values: (SimpleProductUpdate_productUpdate_product_attributes_values | null)[];
}
export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface SimpleProductUpdate_productUpdate_product_productType_variantAttributes {
__typename: "Attribute";
id: string;
name: string | null;
values: (SimpleProductUpdate_productUpdate_product_productType_variantAttributes_values | null)[] | null;
}
export interface SimpleProductUpdate_productUpdate_product_productType {
__typename: "ProductType";
variantAttributes: (SimpleProductUpdate_productUpdate_product_productType_variantAttributes | null)[] | null;
id: string;
name: string;
hasVariants: boolean;
}
export interface SimpleProductUpdate_productUpdate_product_category {
__typename: "Category";
id: string;
@ -56,36 +108,6 @@ export interface SimpleProductUpdate_productUpdate_product_purchaseCost {
stop: SimpleProductUpdate_productUpdate_product_purchaseCost_stop | null;
}
export interface SimpleProductUpdate_productUpdate_product_attributes_attribute_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface SimpleProductUpdate_productUpdate_product_attributes_attribute {
__typename: "Attribute";
id: string;
slug: string | null;
name: string | null;
inputType: AttributeInputTypeEnum | null;
valueRequired: boolean;
values: (SimpleProductUpdate_productUpdate_product_attributes_attribute_values | null)[] | null;
}
export interface SimpleProductUpdate_productUpdate_product_attributes_values {
__typename: "AttributeValue";
id: string;
name: string | null;
slug: string | null;
}
export interface SimpleProductUpdate_productUpdate_product_attributes {
__typename: "SelectedAttribute";
attribute: SimpleProductUpdate_productUpdate_product_attributes_attribute;
values: (SimpleProductUpdate_productUpdate_product_attributes_values | null)[];
}
export interface SimpleProductUpdate_productUpdate_product_pricing_priceRange_start_net {
__typename: "Money";
amount: number;
@ -157,16 +179,11 @@ export interface SimpleProductUpdate_productUpdate_product_variants {
trackInventory: boolean;
}
export interface SimpleProductUpdate_productUpdate_product_productType {
__typename: "ProductType";
id: string;
name: string;
hasVariants: boolean;
}
export interface SimpleProductUpdate_productUpdate_product {
__typename: "Product";
id: string;
attributes: SimpleProductUpdate_productUpdate_product_attributes[];
productType: SimpleProductUpdate_productUpdate_product_productType;
name: string;
descriptionJson: any;
seoTitle: string | null;
@ -180,11 +197,9 @@ export interface SimpleProductUpdate_productUpdate_product {
isPublished: boolean;
chargeTaxes: boolean;
publicationDate: any | null;
attributes: SimpleProductUpdate_productUpdate_product_attributes[];
pricing: SimpleProductUpdate_productUpdate_product_pricing | null;
images: (SimpleProductUpdate_productUpdate_product_images | null)[] | null;
variants: (SimpleProductUpdate_productUpdate_product_variants | null)[] | null;
productType: SimpleProductUpdate_productUpdate_product_productType;
}
export interface SimpleProductUpdate_productUpdate {

View file

@ -69,11 +69,7 @@ export const productListUrl = (params?: ProductListUrlQueryParams): string =>
productListPath + "?" + stringifyQs(params);
export const productPath = (id: string) => urlJoin(productSection + id);
export type ProductUrlDialog =
| "create-variants"
| "edit-stocks"
| "remove"
| "remove-variants";
export type ProductUrlDialog = "edit-stocks" | "remove" | "remove-variants";
export type ProductUrlQueryParams = BulkAction & Dialog<ProductUrlDialog>;
export const productUrl = (id: string, params?: ProductUrlQueryParams) =>
productPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
@ -96,6 +92,11 @@ export const productVariantEditUrl = (
"?" +
stringifyQs(params);
export const productVariantCreatorPath = (productId: string) =>
urlJoin(productSection, productId, "variant-creator");
export const productVariantCreatorUrl = (productId: string) =>
productVariantCreatorPath(encodeURIComponent(productId));
export const productVariantAddPath = (productId: string) =>
urlJoin(productSection, productId, "variant/add");
export type ProductVariantAddUrlDialog = "edit-stocks";

View file

@ -11,10 +11,7 @@ import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import useBulkActions from "@saleor/hooks/useBulkActions";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop";
import { commonMessages } from "@saleor/intl";
import ProductVariantCreateDialog from "@saleor/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog";
import { ProductVariantBulkCreate } from "@saleor/products/types/ProductVariantBulkCreate";
import useCategorySearch from "@saleor/searches/useCategorySearch";
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
@ -39,7 +36,8 @@ import {
ProductUrlQueryParams,
productVariantAddUrl,
productVariantEditUrl,
ProductUrlDialog
ProductUrlDialog,
productVariantCreatorUrl
} from "../../urls";
import {
createImageReorderHandler,
@ -59,7 +57,6 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
params.ids
);
const intl = useIntl();
const shop = useShop();
const {
loadMore: loadMoreCategories,
search: searchCategories,
@ -144,15 +141,6 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
});
const handleVariantAdd = () => navigate(productVariantAddUrl(id));
const handleBulkProductVariantCreate = (
data: ProductVariantBulkCreate
) => {
if (data.productVariantBulkCreate.errors.length === 0) {
closeModal();
refetch();
}
};
const handleBulkProductVariantDelete = (
data: ProductVariantBulkDelete
) => {
@ -166,7 +154,6 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
return (
<ProductUpdateOperations
product={product}
onBulkProductVariantCreate={handleBulkProductVariantCreate}
onBulkProductVariantDelete={handleBulkProductVariantDelete}
onDelete={handleDelete}
onImageCreate={handleImageCreate}
@ -174,7 +161,6 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
onUpdate={handleUpdate}
>
{({
bulkProductVariantCreate,
bulkProductVariantDelete,
createProductImage,
deleteProduct,
@ -258,7 +244,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
onImageReorder={handleImageReorder}
onSubmit={handleSubmit}
onVariantAdd={handleVariantAdd}
onVariantsAdd={() => openModal("create-variants")}
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
onVariantShow={variantId => () =>
navigate(productVariantEditUrl(product.id, variantId))}
onImageUpload={handleImageUpload}
@ -347,28 +333,6 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
/>
</DialogContentText>
</ActionDialog>
<ProductVariantCreateDialog
defaultPrice={maybe(() =>
data.product.basePrice.amount.toFixed(2)
)}
errors={
bulkProductVariantCreate.opts.data
?.productVariantBulkCreate.errors || []
}
open={params.action === "create-variants"}
attributes={maybe(
() => data.product.productType.variantAttributes,
[]
)}
currencySymbol={maybe(() => shop.defaultCurrency)}
onClose={closeModal}
onSubmit={inputs =>
bulkProductVariantCreate.mutate({
id,
inputs
})
}
/>
{!product?.productType?.hasVariants && (
<ProductWarehousesDialog
confirmButtonState={addOrRemoveStocksOpts.status}

View file

@ -0,0 +1,71 @@
import React from "react";
import { useIntl } from "react-intl";
import { WindowTitle } from "@saleor/components/WindowTitle";
import { useCreateMultipleVariantsData } from "@saleor/products/queries";
import { useProductVariantBulkCreateMutation } from "@saleor/products/mutations";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import { productUrl } from "@saleor/products/urls";
import useShop from "@saleor/hooks/useShop";
import ProductVariantCreatorPage from "../../components/ProductVariantCreatorPage";
interface ProductVariantCreatorProps {
id: string;
}
const ProductVariantCreator: React.FC<ProductVariantCreatorProps> = ({
id
}) => {
const navigate = useNavigator();
const notify = useNotifier();
const intl = useIntl();
const { data } = useCreateMultipleVariantsData({
displayLoader: true,
variables: { id }
});
const [
bulkProductVariantCreate,
bulkProductVariantCreateOpts
] = useProductVariantBulkCreateMutation({
onCompleted: data => {
if (data.productVariantBulkCreate.errors.length === 0) {
notify({
text: intl.formatMessage({
defaultMessage: "Successfully created variants",
description: "success message"
})
});
navigate(productUrl(id));
}
}
});
const shop = useShop();
return (
<>
<WindowTitle
title={intl.formatMessage({
defaultMessage: "Create Variants",
description: "window title"
})}
/>
<ProductVariantCreatorPage
defaultPrice={data?.product?.basePrice.amount.toString()}
errors={
bulkProductVariantCreateOpts.data?.productVariantBulkCreate.errors ||
[]
}
attributes={data?.product?.productType?.variantAttributes || []}
currencySymbol={shop?.defaultCurrency}
onSubmit={inputs =>
bulkProductVariantCreate({
variables: { id, inputs }
})
}
/>
</>
);
};
ProductVariantCreator.displayName = "ProductVariantCreator";
export default ProductVariantCreator;

View file

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