Add stock to variant matrix
This commit is contained in:
parent
422ede89a8
commit
e153b59bc3
18 changed files with 395 additions and 252 deletions
|
@ -4643,7 +4643,7 @@ input ProductVariantBulkCreateInput {
|
|||
costPrice: Decimal
|
||||
priceOverride: Decimal
|
||||
sku: String!
|
||||
quantity: Int
|
||||
stocks: [StockInput!]!
|
||||
trackInventory: Boolean
|
||||
weight: WeightScalar
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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]
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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] : []
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -63,6 +63,7 @@ const ProductVariantCreator: React.FC<ProductVariantCreatorProps> = ({
|
|||
variables: { id, inputs }
|
||||
})
|
||||
}
|
||||
warehouses={data?.warehouses.edges.map(edge => edge.node)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
30
src/warehouses/types/WarehouseWithShippingFragment.ts
Normal file
30
src/warehouses/types/WarehouseWithShippingFragment.ts
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue