Add stock to variant matrix

This commit is contained in:
dominik-zeglen 2020-04-03 16:29:32 +02:00
parent 422ede89a8
commit e153b59bc3
18 changed files with 395 additions and 252 deletions

View file

@ -4643,7 +4643,7 @@ input ProductVariantBulkCreateInput {
costPrice: Decimal
priceOverride: Decimal
sku: String!
quantity: Int
stocks: [StockInput!]!
trackInventory: Boolean
weight: WeightScalar
}

View file

@ -1,19 +1,33 @@
import { useState } from "react";
export interface UseWizardOpts<T> {
export interface UseWizardActions<T> {
next: () => void;
prev: () => void;
set: (step: T) => void;
}
export type UseWizard<T> = [T, UseWizardOpts<T>];
function useWizard<T>(initial: T, steps: T[]): UseWizard<T> {
export interface UseWizardOpts<T> {
onTransition: (prevStep: T, nextStep: T) => void;
}
export type UseWizard<T> = [T, UseWizardActions<T>];
function useWizard<T>(
initial: T,
steps: T[],
opts?: UseWizardOpts<T>
): UseWizard<T> {
const [stepIndex, setStepIndex] = useState(steps.indexOf(initial));
function goToStep(nextStepIndex) {
if (typeof opts?.onTransition === "function") {
opts.onTransition(steps[stepIndex], steps[nextStepIndex]);
}
setStepIndex(nextStepIndex);
}
function next() {
if (stepIndex === steps.length - 1) {
console.error("This is the last step");
} else {
setStepIndex(stepIndex + 1);
goToStep(stepIndex + 1);
}
}
@ -21,7 +35,7 @@ function useWizard<T>(initial: T, steps: T[]): UseWizard<T> {
if (stepIndex === 0) {
console.error("This is the first step");
} else {
setStepIndex(stepIndex - 1);
goToStep(stepIndex - 1);
}
}
@ -30,7 +44,7 @@ function useWizard<T>(initial: T, steps: T[]): UseWizard<T> {
if (newStepIndex === -1) {
console.error("Step does not exist");
} else {
setStepIndex(newStepIndex);
goToStep(newStepIndex);
}
}

View file

@ -5,9 +5,10 @@ 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 { warehouseList } from "@saleor/warehouses/fixtures";
import Decorator from "../../../storybook/Decorator";
import { createVariants } from "./createVariants";
import { AllOrAttribute } from "./form";
import { AllOrAttribute, ProductVariantCreateFormData } from "./form";
import ProductVariantCreatorContent, {
ProductVariantCreatorContentProps
} from "./ProductVariantCreatorContent";
@ -15,24 +16,33 @@ import ProductVariantCreatorPage from "./ProductVariantCreatorPage";
import { ProductVariantCreatorStep } from "./types";
const selectedAttributes = [1, 4, 5].map(index => attributes[index]);
const selectedWarehouses = [0, 1, 3].map(index => warehouseList[index]);
const price: AllOrAttribute = {
const price: AllOrAttribute<string> = {
all: false,
attribute: selectedAttributes[1].id,
attribute: selectedAttributes[0].id,
value: "2.79",
values: selectedAttributes[1].values.map((attribute, attributeIndex) => ({
values: selectedAttributes[0].values.map((attribute, attributeIndex) => ({
slug: attribute.slug,
value: (attributeIndex + 4).toFixed(2)
}))
};
const stock: AllOrAttribute = {
const stock: AllOrAttribute<string[]> = {
all: false,
attribute: selectedAttributes[1].id,
value: "8",
values: selectedAttributes[1].values.map((attribute, attributeIndex) => ({
attribute: selectedAttributes[0].id,
value: selectedWarehouses.map((_, warehouseIndex) =>
((warehouseIndex + 2) * 3).toString()
),
values: selectedAttributes[0].values.map((attribute, attributeIndex) => ({
slug: attribute.slug,
value: (selectedAttributes.length * 10 - attributeIndex).toString()
value: selectedWarehouses.map((_, warehouseIndex) =>
(
selectedAttributes.length * 10 -
attributeIndex -
warehouseIndex * 3
).toString()
)
}))
};
@ -52,23 +62,30 @@ const errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[] = [
}
];
const data: ProductVariantCreateFormData = {
attributes: dataAttributes,
price,
stock,
variants: createVariants({
attributes: dataAttributes,
price,
stock,
variants: [],
warehouses: selectedWarehouses.map(warehouse => warehouse.id)
}),
warehouses: selectedWarehouses.map(warehouse => warehouse.id)
};
const props: ProductVariantCreatorContentProps = {
attributes,
currencySymbol: "USD",
data: {
attributes: dataAttributes,
price,
stock,
variants: createVariants({
attributes: dataAttributes,
price,
stock,
variants: []
})
...data,
variants: createVariants(data)
},
dispatchFormDataAction: () => undefined,
errors: [],
step: ProductVariantCreatorStep.values
step: ProductVariantCreatorStep.values,
warehouses: warehouseList
};
storiesOf("Views / Products / Create multiple variants", module)

View file

@ -3,6 +3,7 @@ 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 { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment";
import { ProductVariantCreateFormData } from "./form";
import ProductVariantCreatePrices from "./ProductVariantCreatorPrices";
import ProductVariantCreateSummary from "./ProductVariantCreatorSummary";
@ -17,6 +18,7 @@ export interface ProductVariantCreatorContentProps {
dispatchFormDataAction: React.Dispatch<ProductVariantCreateReducerAction>;
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[];
step: ProductVariantCreatorStep;
warehouses: WarehouseFragment[];
}
const ProductVariantCreatorContent: React.FC<ProductVariantCreatorContentProps> = props => {
@ -26,7 +28,8 @@ const ProductVariantCreatorContent: React.FC<ProductVariantCreatorContentProps>
data,
dispatchFormDataAction,
errors,
step
step,
warehouses
} = props;
const selectedAttributes = attributes.filter(attribute =>
isSelected(
@ -106,12 +109,23 @@ const ProductVariantCreatorContent: React.FC<ProductVariantCreatorContentProps>
variantIndex
})
}
onVariantStockDataChange={(variantIndex, warehouse, value) =>
dispatchFormDataAction({
stock: {
quantity: parseInt(value, 10),
warehouse
},
type: "changeVariantStockData",
variantIndex
})
}
onVariantDelete={variantIndex =>
dispatchFormDataAction({
type: "deleteVariant",
variantIndex
})
}
warehouses={warehouses}
/>
)}
</>

View file

@ -50,17 +50,8 @@ function canHitNext(
}
}
if (data.stock.all) {
if (data.stock.value === "") {
return false;
}
} else {
if (
data.stock.attribute === "" ||
data.stock.values.some(attributeValue => attributeValue.value === "")
) {
return false;
}
if (!data.stock.all || data.stock.attribute) {
return false;
}
return true;
@ -102,25 +93,42 @@ function getTitle(step: ProductVariantCreatorStep, intl: IntlShape): string {
}
const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = props => {
const { attributes, defaultPrice, errors, onSubmit, ...contentProps } = props;
const {
attributes,
defaultPrice,
errors,
onSubmit,
warehouses,
...contentProps
} = props;
const classes = useStyles(props);
const intl = useIntl();
const [step, { next: nextStep, prev: prevStep, set: setStep }] = useWizard<
ProductVariantCreatorStep
>(ProductVariantCreatorStep.values, [
ProductVariantCreatorStep.values,
ProductVariantCreatorStep.prices,
ProductVariantCreatorStep.summary
]);
const [wizardData, dispatchFormDataAction] = React.useReducer(
reduceProductVariantCreateFormData,
createInitialForm(attributes, defaultPrice)
createInitialForm(attributes, defaultPrice, warehouses)
);
const [step, { next: nextStep, prev: prevStep, set: setStep }] = useWizard<
ProductVariantCreatorStep
>(
ProductVariantCreatorStep.values,
[
ProductVariantCreatorStep.values,
ProductVariantCreatorStep.prices,
ProductVariantCreatorStep.summary
],
{
onTransition: (_, nextStep) => {
if (nextStep === ProductVariantCreatorStep.summary) {
dispatchFormDataAction({
type: "reload"
});
}
}
}
);
const reloadForm = () =>
dispatchFormDataAction({
data: createInitialForm(attributes, defaultPrice),
data: createInitialForm(attributes, defaultPrice, warehouses),
type: "reload"
});
@ -170,6 +178,7 @@ const ProductVariantCreatePage: React.FC<ProductVariantCreatePageProps> = props
dispatchFormDataAction={dispatchFormDataAction}
errors={errors}
step={step}
warehouses={warehouses}
/>
</Container>
);

View file

@ -5,7 +5,7 @@ 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 { makeStyles, Theme } from "@material-ui/core/styles";
import TextField from "@material-ui/core/TextField";
import DeleteIcon from "@material-ui/icons/Delete";
import classNames from "classnames";
@ -17,7 +17,8 @@ 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 { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment";
import Hr from "@saleor/components/Hr";
import { ProductDetails_product_productType_variantAttributes } from "../../types/ProductDetails";
import { ProductVariantCreateFormData } from "./form";
import { VariantField } from "./reducer";
@ -27,39 +28,62 @@ export interface ProductVariantCreatorSummaryProps {
currencySymbol: string;
data: ProductVariantCreateFormData;
errors: ProductVariantBulkCreate_productVariantBulkCreate_errors[];
warehouses: WarehouseFragment[];
onVariantDataChange: (
variantIndex: number,
field: VariantField,
value: string
) => void;
onVariantStockDataChange: (
variantIndex: number,
warehouseId: string,
value: string
) => void;
onVariantDelete: (variantIndex: number) => void;
}
type ClassKey =
| "attributeValue"
| "card"
| "col"
| "colHeader"
| "colName"
| "colPrice"
| "colSku"
| "colStock"
| "delete"
| "hr"
| "input"
| "summary";
const colors = [blue, cyan, green, purple, yellow].map(color => color[800]);
const useStyles = makeStyles(
const useStyles = makeStyles<
Theme,
ProductVariantCreatorSummaryProps,
ClassKey
>(
theme => ({
attributeValue: {
display: "inline-block",
marginRight: theme.spacing(1)
},
col: {
...theme.typography.body1,
fontSize: 14,
paddingLeft: theme.spacing(),
paddingRight: theme.spacing(1)
card: {
paddingBottom: theme.spacing()
},
colHeader: {
col: {
...theme.typography.body1,
fontSize: 14
},
colHeader: {
...theme.typography.body1,
fontSize: 14,
paddingTop: theme.spacing(3)
},
colName: {
"&&": {
paddingLeft: "0 !important"
},
"&:not($colHeader)": {
paddingTop: theme.spacing(2)
}
},
paddingLeft: theme.spacing(3)
},
colPrice: {},
colSku: {},
@ -67,22 +91,21 @@ const useStyles = makeStyles(
delete: {
marginTop: theme.spacing(0.5)
},
errorRow: {},
hr: {
marginBottom: theme.spacing(),
marginTop: theme.spacing(0.5)
gridColumn: props => `span ${4 + props.data.stock.value.length}`
},
input: {
"& input": {
padding: "16px 12px 17px"
},
marginTop: theme.spacing(0.5)
}
},
row: {
borderBottom: `1px solid ${theme.palette.divider}`,
summary: {
columnGap: theme.spacing(3),
display: "grid",
gridTemplateColumns: "1fr 180px 120px 180px 64px",
padding: theme.spacing(1, 1, 1, 3)
gridTemplateColumns: props =>
`minmax(240px, auto) 170px repeat(${props.data.stock.value.length}, 140px) 140px 64px`,
overflowX: "scroll",
rowGap: theme.spacing() + "px"
}
}),
{
@ -115,63 +138,66 @@ const ProductVariantCreatorSummary: React.FC<ProductVariantCreatorSummaryProps>
currencySymbol,
data,
errors,
warehouses,
onVariantDataChange,
onVariantDelete
onVariantDelete,
onVariantStockDataChange
} = props;
const classes = useStyles(props);
const intl = useIntl();
return (
<Card>
<CardTitle title={intl.formatMessage(commonMessages.summary)} />
<div>
<div className={classes.row}>
<div
className={classNames(
classes.col,
classes.colHeader,
classes.colName
)}
>
<FormattedMessage
defaultMessage="Variant"
description="variant name"
/>
</div>
<div
className={classNames(
classes.col,
classes.colHeader,
classes.colPrice
)}
>
<FormattedMessage
defaultMessage="Price"
description="variant price"
/>
</div>
<Card className={classes.card}>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Created Variants",
description: "variant creator summary card header"
})}
/>
<div className={classes.summary}>
<div
className={classNames(
classes.col,
classes.colHeader,
classes.colName
)}
>
<FormattedMessage
defaultMessage="Variant"
description="variant name"
/>
</div>
<div
className={classNames(
classes.col,
classes.colHeader,
classes.colPrice
)}
>
<FormattedMessage
defaultMessage="Price"
description="variant price"
/>
</div>
{data.warehouses.map(warehouseId => (
<div
className={classNames(
classes.col,
classes.colHeader,
classes.colStock
)}
key={warehouseId}
>
<FormattedMessage
defaultMessage="Inventory"
description="variant stock amount"
/>
</div>
<div
className={classNames(
classes.col,
classes.colHeader,
classes.colSku
)}
>
<FormattedMessage defaultMessage="SKU" />
{warehouses.find(warehouse => warehouse.id === warehouseId).name}
</div>
))}
<div
className={classNames(classes.col, classes.colHeader, classes.colSku)}
>
<FormattedMessage defaultMessage="SKU" />
</div>
<div className={classNames(classes.col, classes.colHeader)} />
<Hr className={classes.hr} />
{data.variants.map((variant, variantIndex) => {
const variantErrors = errors.filter(
error => error.index === variantIndex
@ -182,10 +208,7 @@ const ProductVariantCreatorSummary: React.FC<ProductVariantCreatorSummaryProps>
);
return (
<div
className={classNames(classes.row, {
[classes.errorRow]: variantErrors.length > 0
})}
<React.Fragment
key={variant.attributes
.map(attribute => attribute.values[0])
.join(":")}
@ -198,7 +221,7 @@ const ProductVariantCreatorSummary: React.FC<ProductVariantCreatorSummaryProps>
style={{
color: colors[valueIndex % colors.length]
}}
key={value}
key={`${value}:${valueIndex}`}
>
{value}
</span>
@ -231,29 +254,34 @@ const ProductVariantCreatorSummary: React.FC<ProductVariantCreatorSummaryProps>
}
/>
</div>
<div className={classNames(classes.col, classes.colStock)}>
<TextField
className={classes.input}
error={!!variantFormErrors.quantity}
helperText={getBulkProductErrorMessage(
variantFormErrors.quantity,
intl
)}
inputProps={{
min: 0,
type: "number"
}}
fullWidth
value={variant.quantity}
onChange={event =>
onVariantDataChange(
variantIndex,
"stock",
event.target.value
)
}
/>
</div>
{variant.stocks.map(stock => (
<div
className={classNames(classes.col, classes.colStock)}
key={stock.warehouse}
>
<TextField
className={classes.input}
error={!!variantFormErrors.quantity}
helperText={getBulkProductErrorMessage(
variantFormErrors.quantity,
intl
)}
inputProps={{
min: 0,
type: "number"
}}
fullWidth
value={stock.quantity}
onChange={event =>
onVariantStockDataChange(
variantIndex,
stock.warehouse,
event.target.value
)
}
/>
</div>
))}
<div className={classNames(classes.col, classes.colSku)}>
<TextField
className={classes.input}
@ -278,7 +306,10 @@ const ProductVariantCreatorSummary: React.FC<ProductVariantCreatorSummaryProps>
<DeleteIcon />
</IconButton>
</div>
</div>
{variantIndex !== data.variants.length - 1 && (
<Hr className={classes.hr} />
)}
</React.Fragment>
);
})}
</div>

View file

@ -11,10 +11,10 @@ interface CreateVariantAttributeValueInput {
}
type CreateVariantInput = CreateVariantAttributeValueInput[];
function getAttributeValuePriceOrStock(
function getAttributeValuePriceOrStock<T>(
attributes: CreateVariantInput,
priceOrStock: AllOrAttribute
): string {
priceOrStock: AllOrAttribute<T>
): T {
const attribute = attributes.find(
attribute => attribute.attributeId === priceOrStock.attribute
);
@ -33,12 +33,9 @@ function createVariant(
const priceOverride = data.price.all
? data.price.value
: getAttributeValuePriceOrStock(attributes, data.price);
const quantity = parseInt(
data.stock.all
? data.stock.value
: getAttributeValuePriceOrStock(attributes, data.stock),
10
);
const stocks = data.stock.all
? data.stock.value
: getAttributeValuePriceOrStock(attributes, data.stock);
return {
attributes: attributes.map(attribute => ({
@ -46,8 +43,11 @@ function createVariant(
values: [attribute.attributeValueSlug]
})),
priceOverride,
quantity,
sku: ""
sku: "",
stocks: stocks.map((quantity, stockIndex) => ({
quantity: parseInt(quantity, 10),
warehouse: data.warehouses[stockIndex]
}))
};
}

View file

@ -1,15 +1,16 @@
import { ProductDetails_product_productType_variantAttributes } from "@saleor/products/types/ProductDetails";
import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment";
import { ProductVariantBulkCreateInput } from "../../../types/globalTypes";
export interface AttributeValue {
export interface AttributeValue<T> {
slug: string;
value: string;
value: T;
}
export interface AllOrAttribute {
export interface AllOrAttribute<T> {
all: boolean;
attribute: string;
value: string;
values: AttributeValue[];
value: T;
values: Array<AttributeValue<T>>;
}
export interface Attribute {
id: string;
@ -17,14 +18,16 @@ export interface Attribute {
}
export interface ProductVariantCreateFormData {
attributes: Attribute[];
price: AllOrAttribute;
stock: AllOrAttribute;
price: AllOrAttribute<string>;
stock: AllOrAttribute<number[]>;
variants: ProductVariantBulkCreateInput[];
warehouses: string[];
}
export const createInitialForm = (
attributes: ProductDetails_product_productType_variantAttributes[],
price: string
price: string,
warehouses: WarehouseFragment[]
): ProductVariantCreateFormData => ({
attributes: attributes.map(attribute => ({
id: attribute.id,
@ -39,8 +42,9 @@ export const createInitialForm = (
stock: {
all: true,
attribute: undefined,
value: "",
value: warehouses.length === 1 ? [0] : [],
values: []
},
variants: []
variants: [],
warehouses: warehouses.length === 1 ? [warehouses[0].id] : []
});

View file

@ -3,8 +3,10 @@ import {
remove,
removeAtIndex,
toggle,
updateAtIndex
updateAtIndex,
update
} from "@saleor/utils/lists";
import { StockInput } from "@saleor/types/globalTypes";
import { createVariants } from "./createVariants";
import { ProductVariantCreateFormData } from "./form";
@ -20,20 +22,24 @@ export type ProductVariantCreateReducerActionType =
| "changeAttributeValuePrice"
| "changeAttributeValueStock"
| "changeVariantData"
| "changeVariantStockData"
| "deleteVariant"
| "reload"
| "selectValue";
export type VariantField = "stock" | "price" | "sku";
export type VariantField = "price" | "sku";
export interface ProductVariantCreateReducerAction {
all?: boolean;
attributeId?: string;
data?: ProductVariantCreateFormData;
field?: VariantField;
quantity?: number;
stock?: StockInput;
type: ProductVariantCreateReducerActionType;
value?: string;
valueId?: string;
variantIndex?: number;
warehouseIndex?: number;
}
function selectValue(
@ -70,7 +76,7 @@ function selectValue(
? toggle(
{
slug: valueSlug,
value: ""
value: []
},
prevState.stock.values,
(a, b) => a.slug === b.slug
@ -95,36 +101,26 @@ function applyPriceToAll(
state: ProductVariantCreateFormData,
value: boolean
): ProductVariantCreateFormData {
const data = {
return {
...state,
price: {
...state.price,
all: value
}
};
return {
...data,
variants: createVariants(data)
};
}
function applyStockToAll(
state: ProductVariantCreateFormData,
value: boolean
): ProductVariantCreateFormData {
const data = {
return {
...state,
stock: {
...state.stock,
all: value
}
};
return {
...data,
variants: createVariants(data)
};
}
function changeAttributeValuePrice(
@ -149,24 +145,20 @@ function changeAttributeValuePrice(
index
);
const data = {
return {
...state,
price: {
...state.price,
values
}
};
return {
...data,
variants: createVariants(data)
};
}
function changeAttributeValueStock(
state: ProductVariantCreateFormData,
attributeValueSlug: string,
stock: string
warehouseIndex: number,
quantity: number
): ProductVariantCreateFormData {
const index = state.stock.values.findIndex(
value => value.slug === attributeValueSlug
@ -179,24 +171,19 @@ function changeAttributeValueStock(
const values = updateAtIndex(
{
slug: attributeValueSlug,
value: stock
value: updateAtIndex(quantity, state.stock.value, warehouseIndex)
},
state.stock.values,
index
);
const data = {
return {
...state,
stock: {
...state.stock,
values
}
};
return {
...data,
variants: createVariants(data)
};
}
function changeApplyPriceToAttributeId(
@ -210,7 +197,8 @@ function changeApplyPriceToAttributeId(
slug,
value: ""
}));
const data = {
return {
...state,
price: {
...state.price,
@ -218,11 +206,6 @@ function changeApplyPriceToAttributeId(
values
}
};
return {
...data,
variants: createVariants(data)
};
}
function changeApplyStockToAttributeId(
@ -234,10 +217,10 @@ function changeApplyStockToAttributeId(
);
const values = attribute.values.map(slug => ({
slug,
value: ""
value: []
}));
const data = {
return {
...state,
stock: {
...state.stock,
@ -245,47 +228,33 @@ function changeApplyStockToAttributeId(
values
}
};
return {
...data,
variants: createVariants(data)
};
}
function changeApplyPriceToAllValue(
state: ProductVariantCreateFormData,
value: string
): ProductVariantCreateFormData {
const data = {
return {
...state,
price: {
...state.price,
value
}
};
return {
...data,
variants: createVariants(data)
};
}
function changeApplyStockToAllValue(
state: ProductVariantCreateFormData,
value: string
warehouseIndex: number,
quantity: number
): ProductVariantCreateFormData {
const data = {
return {
...state,
stock: {
...state.stock,
value
value: updateAtIndex(quantity, state.stock.value, warehouseIndex)
}
};
return {
...data,
variants: createVariants(data)
};
}
function changeVariantData(
@ -299,8 +268,6 @@ function changeVariantData(
variant.priceOverride = value;
} else if (field === "sku") {
variant.sku = value;
} else {
variant.quantity = parseInt(value, 10);
}
return {
@ -309,6 +276,24 @@ function changeVariantData(
};
}
function changeVariantStockData(
state: ProductVariantCreateFormData,
stock: StockInput,
variantIndex: number
): ProductVariantCreateFormData {
const variant = state.variants[variantIndex];
variant.stocks = update(
stock,
variant.stocks,
(a, b) => a.warehouse === b.warehouse
);
return {
...state,
variants: updateAtIndex(variant, state.variants, variantIndex)
};
}
function deleteVariant(
state: ProductVariantCreateFormData,
variantIndex: number
@ -319,6 +304,15 @@ function deleteVariant(
};
}
function createVariantMatrix(
state: ProductVariantCreateFormData
): ProductVariantCreateFormData {
return {
...state,
variants: createVariants(state)
};
}
function reduceProductVariantCreateFormData(
prevState: ProductVariantCreateFormData,
action: ProductVariantCreateReducerAction
@ -326,7 +320,6 @@ function reduceProductVariantCreateFormData(
switch (action.type) {
case "selectValue":
return selectValue(prevState, action.attributeId, action.valueId);
case "applyPriceToAll":
return applyPriceToAll(prevState, action.all);
case "applyStockToAll":
@ -334,7 +327,12 @@ function reduceProductVariantCreateFormData(
case "changeAttributeValuePrice":
return changeAttributeValuePrice(prevState, action.valueId, action.value);
case "changeAttributeValueStock":
return changeAttributeValueStock(prevState, action.valueId, action.value);
return changeAttributeValueStock(
prevState,
action.valueId,
action.quantity,
action.warehouseIndex
);
case "changeApplyPriceToAttributeId":
return changeApplyPriceToAttributeId(prevState, action.attributeId);
case "changeApplyStockToAttributeId":
@ -342,7 +340,11 @@ function reduceProductVariantCreateFormData(
case "changeApplyPriceToAllValue":
return changeApplyPriceToAllValue(prevState, action.value);
case "changeApplyStockToAllValue":
return changeApplyStockToAllValue(prevState, action.value);
return changeApplyStockToAllValue(
prevState,
action.quantity,
action.warehouseIndex
);
case "changeVariantData":
return changeVariantData(
prevState,
@ -350,10 +352,16 @@ function reduceProductVariantCreateFormData(
action.value,
action.variantIndex
);
case "changeVariantStockData":
return changeVariantStockData(
prevState,
action.stock,
action.variantIndex
);
case "deleteVariant":
return deleteVariant(prevState, action.variantIndex);
case "reload":
return action.data;
return action.data ? action.data : createVariantMatrix(prevState);
default:
return prevState;
}

View file

@ -1,6 +1,7 @@
import gql from "graphql-tag";
import makeQuery from "@saleor/hooks/makeQuery";
import { warehouseFragment } from "@saleor/warehouses/queries";
import { pageInfoFragment, TypedQuery } from "../queries";
import {
AvailableInGridAttributes,
@ -480,6 +481,7 @@ export const AvailableInGridAttributesQuery = TypedQuery<
const createMultipleVariantsData = gql`
${fragmentMoney}
${productVariantAttributesFragment}
${warehouseFragment}
query CreateMultipleVariantsData($id: ID!) {
product(id: $id) {
...ProductVariantAttributesFragment
@ -487,6 +489,13 @@ const createMultipleVariantsData = gql`
...Money
}
}
warehouses(first: 20) {
edges {
node {
...WarehouseFragment
}
}
}
}
`;
export const useCreateMultipleVariantsData = makeQuery<

View file

@ -71,8 +71,25 @@ export interface CreateMultipleVariantsData_product {
basePrice: CreateMultipleVariantsData_product_basePrice | null;
}
export interface CreateMultipleVariantsData_warehouses_edges_node {
__typename: "Warehouse";
id: string;
name: string;
}
export interface CreateMultipleVariantsData_warehouses_edges {
__typename: "WarehouseCountableEdge";
node: CreateMultipleVariantsData_warehouses_edges_node;
}
export interface CreateMultipleVariantsData_warehouses {
__typename: "WarehouseCountableConnection";
edges: CreateMultipleVariantsData_warehouses_edges[];
}
export interface CreateMultipleVariantsData {
product: CreateMultipleVariantsData_product | null;
warehouses: CreateMultipleVariantsData_warehouses | null;
}
export interface CreateMultipleVariantsDataVariables {

View file

@ -63,6 +63,7 @@ const ProductVariantCreator: React.FC<ProductVariantCreatorProps> = ({
variables: { id, inputs }
})
}
warehouses={data?.warehouses.edges.map(edge => edge.node)}
/>
</>
);

View file

@ -1223,7 +1223,7 @@ export interface ProductVariantBulkCreateInput {
costPrice?: any | null;
priceOverride?: any | null;
sku: string;
quantity?: number | null;
stocks: StockInput[];
trackInventory?: boolean | null;
weight?: any | null;
}

View file

@ -10,7 +10,6 @@ import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import EditIcon from "@material-ui/icons/Edit";
import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment";
import ResponsiveTable from "@saleor/components/ResponsiveTable";
import Skeleton from "@saleor/components/Skeleton";
import TablePagination from "@saleor/components/TablePagination";
@ -19,6 +18,7 @@ import { ListProps, SortPage } from "@saleor/types";
import { WarehouseListUrlSortField } from "@saleor/warehouses/urls";
import TableCellHeader from "@saleor/components/TableCellHeader";
import { getArrowDirection } from "@saleor/utils/sort";
import { WarehouseWithShippingFragment } from "@saleor/warehouses/types/WarehouseWithShippingFragment";
const useStyles = makeStyles(
theme => ({
@ -59,7 +59,7 @@ const useStyles = makeStyles(
interface WarehouseListProps
extends ListProps,
SortPage<WarehouseListUrlSortField> {
warehouses: WarehouseFragment[];
warehouses: WarehouseWithShippingFragment[];
onAdd: () => void;
onRemove: (id: string) => void;
}

View file

@ -3,7 +3,6 @@ import Card from "@material-ui/core/Card";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { WarehouseFragment } from "@saleor/warehouses/types/WarehouseFragment";
import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader";
import SearchBar from "@saleor/components/SearchBar";
@ -16,6 +15,7 @@ import {
} from "@saleor/types";
import { WarehouseListUrlSortField } from "@saleor/warehouses/urls";
import AppHeader from "@saleor/components/AppHeader";
import { WarehouseWithShippingFragment } from "@saleor/warehouses/types/WarehouseWithShippingFragment";
import WarehouseList from "../WarehouseList";
export interface WarehouseListPageProps
@ -23,7 +23,7 @@ export interface WarehouseListPageProps
SearchPageProps,
SortPage<WarehouseListUrlSortField>,
TabPageProps {
warehouses: WarehouseFragment[];
warehouses: WarehouseWithShippingFragment[];
onBack: () => void;
onRemove: (id: string) => void;
}

View file

@ -13,6 +13,12 @@ export const warehouseFragment = gql`
fragment WarehouseFragment on Warehouse {
id
name
}
`;
export const warehouseWithShippingFragment = gql`
${warehouseFragment}
fragment WarehouseWithShippingFragment on Warehouse {
...WarehouseFragment
shippingZones(first: 100) {
edges {
node {
@ -26,9 +32,9 @@ export const warehouseFragment = gql`
export const warehouseDetailsFragment = gql`
${fragmentAddress}
${warehouseFragment}
${warehouseWithShippingFragment}
fragment WarehouseDetailsFragment on Warehouse {
...WarehouseFragment
...WarehouseWithShippingFragment
address {
...AddressFragment
}
@ -36,7 +42,7 @@ export const warehouseDetailsFragment = gql`
`;
const warehouseList = gql`
${warehouseFragment}
${warehouseWithShippingFragment}
${pageInfoFragment}
query WarehouseList(
$first: Int
@ -56,7 +62,7 @@ const warehouseList = gql`
) {
edges {
node {
...WarehouseFragment
...WarehouseWithShippingFragment
}
}
pageInfo {

View file

@ -6,25 +6,8 @@
// GraphQL fragment: WarehouseFragment
// ====================================================
export interface WarehouseFragment_shippingZones_edges_node {
__typename: "ShippingZone";
id: string;
name: string;
}
export interface WarehouseFragment_shippingZones_edges {
__typename: "ShippingZoneCountableEdge";
node: WarehouseFragment_shippingZones_edges_node;
}
export interface WarehouseFragment_shippingZones {
__typename: "ShippingZoneCountableConnection";
edges: WarehouseFragment_shippingZones_edges[];
}
export interface WarehouseFragment {
__typename: "Warehouse";
id: string;
name: string;
shippingZones: WarehouseFragment_shippingZones;
}

View file

@ -0,0 +1,30 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL fragment: WarehouseWithShippingFragment
// ====================================================
export interface WarehouseWithShippingFragment_shippingZones_edges_node {
__typename: "ShippingZone";
id: string;
name: string;
}
export interface WarehouseWithShippingFragment_shippingZones_edges {
__typename: "ShippingZoneCountableEdge";
node: WarehouseWithShippingFragment_shippingZones_edges_node;
}
export interface WarehouseWithShippingFragment_shippingZones {
__typename: "ShippingZoneCountableConnection";
edges: WarehouseWithShippingFragment_shippingZones_edges[];
}
export interface WarehouseWithShippingFragment {
__typename: "Warehouse";
id: string;
name: string;
shippingZones: WarehouseWithShippingFragment_shippingZones;
}