Add simple product stock management
This commit is contained in:
parent
db3b51f931
commit
4b661c9bff
12 changed files with 150 additions and 42 deletions
|
@ -172,7 +172,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
errors={updateResult.data?.categoryUpdate.errors || []}
|
errors={updateResult.data?.categoryUpdate.errors || []}
|
||||||
onAddCategory={() => navigate(categoryAddUrl(id))}
|
onAddCategory={() => navigate(categoryAddUrl(id))}
|
||||||
onAddProduct={() => navigate(productAddUrl)}
|
onAddProduct={() => navigate(productAddUrl())}
|
||||||
onBack={() =>
|
onBack={() =>
|
||||||
navigate(
|
navigate(
|
||||||
maybe(
|
maybe(
|
||||||
|
|
|
@ -46,7 +46,7 @@ export function searchInCommands(
|
||||||
{
|
{
|
||||||
label: intl.formatMessage(messages.createProduct),
|
label: intl.formatMessage(messages.createProduct),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
navigate(productAddUrl);
|
navigate(productAddUrl());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { SearchProductTypes_search_edges_node_productAttributes } from "@saleor/
|
||||||
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||||
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
|
import { ProductErrorFragment } from "@saleor/attributes/types/ProductErrorFragment";
|
||||||
|
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
||||||
import { FetchMoreProps } from "../../../types";
|
import { FetchMoreProps } from "../../../types";
|
||||||
import {
|
import {
|
||||||
createAttributeChangeHandler,
|
createAttributeChangeHandler,
|
||||||
|
@ -81,6 +82,7 @@ interface ProductCreatePageProps {
|
||||||
}>;
|
}>;
|
||||||
header: string;
|
header: string;
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
|
warehouses: SearchWarehouses_search_edges_node[];
|
||||||
fetchCategories: (data: string) => void;
|
fetchCategories: (data: string) => void;
|
||||||
fetchCollections: (data: string) => void;
|
fetchCollections: (data: string) => void;
|
||||||
fetchProductTypes: (data: string) => void;
|
fetchProductTypes: (data: string) => void;
|
||||||
|
@ -103,6 +105,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
header,
|
header,
|
||||||
productTypes: productTypeChoiceList,
|
productTypes: productTypeChoiceList,
|
||||||
saveButtonBarState,
|
saveButtonBarState,
|
||||||
|
warehouses,
|
||||||
onBack,
|
onBack,
|
||||||
fetchProductTypes,
|
fetchProductTypes,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
@ -116,7 +119,18 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
data: attributes,
|
data: attributes,
|
||||||
set: setAttributeData
|
set: setAttributeData
|
||||||
} = useFormset<ProductAttributeInputData>([]);
|
} = useFormset<ProductAttributeInputData>([]);
|
||||||
const { change: changeStockData, data: stocks } = useFormset<null>([]);
|
const { change: changeStockData, data: stocks, set: setStocks } = useFormset<
|
||||||
|
null
|
||||||
|
>([]);
|
||||||
|
React.useEffect(() => {
|
||||||
|
const newStocks = warehouses.map(warehouse => ({
|
||||||
|
data: null,
|
||||||
|
id: warehouse.id,
|
||||||
|
label: warehouse.name,
|
||||||
|
value: stocks.find(stock => stock.id === warehouse.id)?.value || 0
|
||||||
|
}));
|
||||||
|
setStocks(newStocks);
|
||||||
|
}, [JSON.stringify(warehouses)]);
|
||||||
|
|
||||||
// Ensures that it will not change after component rerenders, because it
|
// Ensures that it will not change after component rerenders, because it
|
||||||
// generates different block keys and it causes editor to lose its content.
|
// generates different block keys and it causes editor to lose its content.
|
||||||
|
@ -248,7 +262,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
||||||
onFormDataChange={change}
|
onFormDataChange={change}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
stocks={stocks}
|
stocks={stocks}
|
||||||
onWarehouseEdit={onWarehouseEdit}
|
onWarehousesEdit={onWarehouseEdit}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -21,6 +21,7 @@ import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||||
import FormSpacer from "@saleor/components/FormSpacer";
|
import FormSpacer from "@saleor/components/FormSpacer";
|
||||||
import Hr from "@saleor/components/Hr";
|
import Hr from "@saleor/components/Hr";
|
||||||
import { renderCollection } from "@saleor/misc";
|
import { renderCollection } from "@saleor/misc";
|
||||||
|
import Link from "@saleor/components/Link";
|
||||||
|
|
||||||
export type ProductStockInput = FormsetAtomicData<null, string>;
|
export type ProductStockInput = FormsetAtomicData<null, string>;
|
||||||
export interface ProductStockFormData {
|
export interface ProductStockFormData {
|
||||||
|
@ -35,7 +36,7 @@ export interface ProductStocksProps {
|
||||||
stocks: ProductStockInput[];
|
stocks: ProductStockInput[];
|
||||||
onChange: FormsetChange;
|
onChange: FormsetChange;
|
||||||
onFormDataChange: FormChange;
|
onFormDataChange: FormChange;
|
||||||
onWarehousesEdit: () => undefined;
|
onWarehousesEdit: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(
|
const useStyles = makeStyles(
|
||||||
|
@ -171,24 +172,41 @@ const ProductStocks: React.FC<ProductStocksProps> = ({
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{renderCollection(stocks, stock => (
|
{renderCollection(
|
||||||
<TableRow>
|
stocks,
|
||||||
<TableCell className={classes.colName}>{stock.label}</TableCell>
|
stock => (
|
||||||
<TableCell className={classes.colQuantity}>
|
<TableRow>
|
||||||
<TextField
|
<TableCell className={classes.colName}>{stock.label}</TableCell>
|
||||||
className={classes.inputComponent}
|
<TableCell className={classes.colQuantity}>
|
||||||
fullWidth
|
<TextField
|
||||||
inputProps={{
|
className={classes.inputComponent}
|
||||||
className: classes.input,
|
fullWidth
|
||||||
min: 0,
|
inputProps={{
|
||||||
type: "number"
|
className: classes.input,
|
||||||
}}
|
min: 0,
|
||||||
onChange={event => onChange(stock.id, event.target.value)}
|
type: "number"
|
||||||
value={stock.value}
|
}}
|
||||||
/>
|
onChange={event => onChange(stock.id, event.target.value)}
|
||||||
</TableCell>
|
value={stock.value}
|
||||||
</TableRow>
|
/>
|
||||||
))}
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
),
|
||||||
|
() => (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={2}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage={
|
||||||
|
"This product doesn't have any stock. You can add it <l>here</l>."
|
||||||
|
}
|
||||||
|
values={{
|
||||||
|
l: str => <Link onClick={onWarehousesEdit}>{str}</Link>
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -15,7 +15,6 @@ import ConfirmButton, {
|
||||||
import { buttonMessages } from "@saleor/intl";
|
import { buttonMessages } from "@saleor/intl";
|
||||||
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
||||||
import Skeleton from "@saleor/components/Skeleton";
|
import Skeleton from "@saleor/components/Skeleton";
|
||||||
import { Product_variants_stocks } from "@saleor/products/types/Product";
|
|
||||||
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
|
||||||
import { isSelected, toggle } from "@saleor/utils/lists";
|
import { isSelected, toggle } from "@saleor/utils/lists";
|
||||||
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
import useStateFromProps from "@saleor/hooks/useStateFromProps";
|
||||||
|
@ -44,8 +43,8 @@ export interface ProductWarehousesDialogProps {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
errors: Array<BulkStockErrorFragment | StockErrorFragment>;
|
errors: Array<BulkStockErrorFragment | StockErrorFragment>;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
stocks: Product_variants_stocks[];
|
|
||||||
warehouses: SearchWarehouses_search_edges_node[];
|
warehouses: SearchWarehouses_search_edges_node[];
|
||||||
|
warehousesWithStocks: string[];
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onConfirm: (data: DiffData<string>) => void;
|
onConfirm: (data: DiffData<string>) => void;
|
||||||
}
|
}
|
||||||
|
@ -57,18 +56,18 @@ const ProductWarehousesDialog: React.FC<ProductWarehousesDialogProps> = ({
|
||||||
onClose,
|
onClose,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
open,
|
open,
|
||||||
stocks,
|
warehousesWithStocks,
|
||||||
warehouses
|
warehouses
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles({});
|
const classes = useStyles({});
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const initial = stocks?.map(stock => stock.warehouse.id) || [];
|
|
||||||
const [selectedWarehouses, setSelectedWarehouses] = useStateFromProps(
|
const [selectedWarehouses, setSelectedWarehouses] = useStateFromProps(
|
||||||
initial
|
warehousesWithStocks || []
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleConfirm = () => onConfirm(diff(initial, selectedWarehouses));
|
const handleConfirm = () =>
|
||||||
|
onConfirm(diff(warehousesWithStocks, selectedWarehouses));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog onClose={onClose} maxWidth="sm" fullWidth open={open}>
|
<Dialog onClose={onClose} maxWidth="sm" fullWidth open={open}>
|
||||||
|
|
|
@ -18,9 +18,10 @@ import {
|
||||||
ProductUrlQueryParams,
|
ProductUrlQueryParams,
|
||||||
productVariantAddPath,
|
productVariantAddPath,
|
||||||
productVariantEditPath,
|
productVariantEditPath,
|
||||||
ProductVariantEditUrlQueryParams
|
ProductVariantEditUrlQueryParams,
|
||||||
|
ProductAddUrlQueryParams
|
||||||
} from "./urls";
|
} from "./urls";
|
||||||
import ProductCreate from "./views/ProductCreate";
|
import ProductCreateComponent from "./views/ProductCreate";
|
||||||
import ProductImageComponent from "./views/ProductImage";
|
import ProductImageComponent from "./views/ProductImage";
|
||||||
import ProductListComponent from "./views/ProductList";
|
import ProductListComponent from "./views/ProductList";
|
||||||
import ProductUpdateComponent from "./views/ProductUpdate";
|
import ProductUpdateComponent from "./views/ProductUpdate";
|
||||||
|
@ -92,6 +93,13 @@ const ProductVariantCreate: React.FC<RouteComponentProps<any>> = ({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const ProductCreate: React.FC<RouteComponentProps> = ({ location }) => {
|
||||||
|
const qs = parseQs(location.search.substr(1));
|
||||||
|
const params: ProductAddUrlQueryParams = qs;
|
||||||
|
|
||||||
|
return <ProductCreateComponent params={params} />;
|
||||||
|
};
|
||||||
|
|
||||||
const Component = () => {
|
const Component = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
|
|
@ -302,6 +302,7 @@ export const productCreateMutation = gql`
|
||||||
$sku: String
|
$sku: String
|
||||||
$stockQuantity: Int
|
$stockQuantity: Int
|
||||||
$seo: SeoInput
|
$seo: SeoInput
|
||||||
|
$stocks: [StockInput!]!
|
||||||
) {
|
) {
|
||||||
productCreate(
|
productCreate(
|
||||||
input: {
|
input: {
|
||||||
|
@ -318,6 +319,7 @@ export const productCreateMutation = gql`
|
||||||
sku: $sku
|
sku: $sku
|
||||||
quantity: $stockQuantity
|
quantity: $stockQuantity
|
||||||
seo: $seo
|
seo: $seo
|
||||||
|
stocks: $stocks
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
errors: productErrors {
|
errors: productErrors {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { AttributeValueInput, SeoInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes";
|
import { AttributeValueInput, SeoInput, StockInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL mutation operation: ProductCreate
|
// GraphQL mutation operation: ProductCreate
|
||||||
|
@ -211,4 +211,5 @@ export interface ProductCreateVariables {
|
||||||
sku?: string | null;
|
sku?: string | null;
|
||||||
stockQuantity?: number | null;
|
stockQuantity?: number | null;
|
||||||
seo?: SeoInput | null;
|
seo?: SeoInput | null;
|
||||||
|
stocks: StockInput[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,10 @@ import {
|
||||||
const productSection = "/products/";
|
const productSection = "/products/";
|
||||||
|
|
||||||
export const productAddPath = urlJoin(productSection, "add");
|
export const productAddPath = urlJoin(productSection, "add");
|
||||||
export const productAddUrl = productAddPath;
|
export type ProductAddUrlDialog = "edit-stocks";
|
||||||
|
export type ProductAddUrlQueryParams = Dialog<ProductAddUrlDialog>;
|
||||||
|
export const productAddUrl = (params?: ProductAddUrlQueryParams): string =>
|
||||||
|
productAddPath + "?" + stringifyQs(params);
|
||||||
|
|
||||||
export const productListPath = productSection;
|
export const productListPath = productSection;
|
||||||
export type ProductListUrlDialog =
|
export type ProductListUrlDialog =
|
||||||
|
|
|
@ -9,19 +9,31 @@ import useShop from "@saleor/hooks/useShop";
|
||||||
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
import useCategorySearch from "@saleor/searches/useCategorySearch";
|
||||||
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
|
||||||
import useProductTypeSearch from "@saleor/searches/useProductTypeSearch";
|
import useProductTypeSearch from "@saleor/searches/useProductTypeSearch";
|
||||||
|
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
|
||||||
|
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
|
||||||
|
import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/SearchWarehouses";
|
||||||
import { decimal, maybe } from "../../misc";
|
import { decimal, maybe } from "../../misc";
|
||||||
import ProductCreatePage, {
|
import ProductCreatePage, {
|
||||||
ProductCreatePageSubmitData
|
ProductCreatePageSubmitData
|
||||||
} from "../components/ProductCreatePage";
|
} from "../components/ProductCreatePage";
|
||||||
import { TypedProductCreateMutation } from "../mutations";
|
import { TypedProductCreateMutation } from "../mutations";
|
||||||
import { ProductCreate } from "../types/ProductCreate";
|
import { ProductCreate } from "../types/ProductCreate";
|
||||||
import { productListUrl, productUrl } from "../urls";
|
import {
|
||||||
|
productListUrl,
|
||||||
|
productUrl,
|
||||||
|
ProductAddUrlDialog,
|
||||||
|
ProductAddUrlQueryParams,
|
||||||
|
productAddUrl
|
||||||
|
} from "../urls";
|
||||||
|
import ProductWarehousesDialog from "../components/ProductWarehousesDialog";
|
||||||
|
|
||||||
interface ProductUpdateProps {
|
interface ProductCreateViewProps {
|
||||||
id: string;
|
params: ProductAddUrlQueryParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProductUpdate: React.FC<ProductUpdateProps> = () => {
|
export const ProductCreateView: React.FC<ProductCreateViewProps> = ({
|
||||||
|
params
|
||||||
|
}) => {
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const shop = useShop();
|
const shop = useShop();
|
||||||
|
@ -47,6 +59,24 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = () => {
|
||||||
} = useProductTypeSearch({
|
} = useProductTypeSearch({
|
||||||
variables: DEFAULT_INITIAL_SEARCH_DATA
|
variables: DEFAULT_INITIAL_SEARCH_DATA
|
||||||
});
|
});
|
||||||
|
const {
|
||||||
|
loadMore: loadMoreWarehouses,
|
||||||
|
search: searchWarehouses,
|
||||||
|
result: searchWarehousesOpts
|
||||||
|
} = useWarehouseSearch({
|
||||||
|
variables: {
|
||||||
|
...DEFAULT_INITIAL_SEARCH_DATA,
|
||||||
|
first: 20
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const [warehouses, setWarehouses] = React.useState<
|
||||||
|
SearchWarehouses_search_edges_node[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const [openModal, closeModal] = createDialogActionHandlers<
|
||||||
|
ProductAddUrlDialog,
|
||||||
|
ProductAddUrlQueryParams
|
||||||
|
>(navigate, productAddUrl, params);
|
||||||
|
|
||||||
const handleBack = () => navigate(productListUrl());
|
const handleBack = () => navigate(productListUrl());
|
||||||
|
|
||||||
|
@ -88,8 +118,10 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = () => {
|
||||||
title: formData.seoTitle
|
title: formData.seoTitle
|
||||||
},
|
},
|
||||||
sku: formData.sku,
|
sku: formData.sku,
|
||||||
stockQuantity:
|
stocks: formData.stocks.map(stock => ({
|
||||||
formData.stockQuantity !== null ? formData.stockQuantity : 0
|
quantity: parseInt(stock.value, 0),
|
||||||
|
warehouse: stock.id
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -124,6 +156,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = () => {
|
||||||
productTypes={maybe(() =>
|
productTypes={maybe(() =>
|
||||||
searchProductTypesOpts.data.search.edges.map(edge => edge.node)
|
searchProductTypesOpts.data.search.edges.map(edge => edge.node)
|
||||||
)}
|
)}
|
||||||
|
warehouses={warehouses}
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
saveButtonBarState={productCreateOpts.status}
|
saveButtonBarState={productCreateOpts.status}
|
||||||
|
@ -148,6 +181,32 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = () => {
|
||||||
loading: searchProductTypesOpts.loading,
|
loading: searchProductTypesOpts.loading,
|
||||||
onFetchMore: loadMoreProductTypes
|
onFetchMore: loadMoreProductTypes
|
||||||
}}
|
}}
|
||||||
|
onWarehouseEdit={() => openModal("edit-stocks")}
|
||||||
|
/>
|
||||||
|
<ProductWarehousesDialog
|
||||||
|
confirmButtonState="default"
|
||||||
|
disabled={false}
|
||||||
|
errors={[]}
|
||||||
|
onClose={closeModal}
|
||||||
|
open={params.action === "edit-stocks"}
|
||||||
|
warehouses={searchWarehousesOpts.data?.search.edges.map(
|
||||||
|
edge => edge.node
|
||||||
|
)}
|
||||||
|
warehousesWithStocks={warehouses.map(warehouse => warehouse.id)}
|
||||||
|
onConfirm={data => {
|
||||||
|
setWarehouses(
|
||||||
|
[
|
||||||
|
...warehouses,
|
||||||
|
...data.added.map(
|
||||||
|
addedId =>
|
||||||
|
searchWarehousesOpts.data.search.edges.find(
|
||||||
|
edge => edge.node.id === addedId
|
||||||
|
).node
|
||||||
|
)
|
||||||
|
].filter(warehouse => !data.removed.includes(warehouse.id))
|
||||||
|
);
|
||||||
|
closeModal();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -155,4 +214,4 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = () => {
|
||||||
</TypedProductCreateMutation>
|
</TypedProductCreateMutation>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default ProductUpdate;
|
export default ProductCreateView;
|
||||||
|
|
|
@ -306,7 +306,7 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
|
||||||
.hasNextPage,
|
.hasNextPage,
|
||||||
false
|
false
|
||||||
)}
|
)}
|
||||||
onAdd={() => navigate(productAddUrl)}
|
onAdd={() => navigate(productAddUrl())}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
products={maybe(() =>
|
products={maybe(() =>
|
||||||
data.products.edges.map(edge => edge.node)
|
data.products.edges.map(edge => edge.node)
|
||||||
|
|
|
@ -384,11 +384,15 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
|
||||||
?.productVariantStocksDelete.errors || [])
|
?.productVariantStocksDelete.errors || [])
|
||||||
]}
|
]}
|
||||||
onClose={closeModal}
|
onClose={closeModal}
|
||||||
stocks={product?.variants[0].stocks || []}
|
|
||||||
open={params.action === "edit-stocks"}
|
open={params.action === "edit-stocks"}
|
||||||
warehouses={searchWarehousesOpts.data?.search.edges.map(
|
warehouses={searchWarehousesOpts.data?.search.edges.map(
|
||||||
edge => edge.node
|
edge => edge.node
|
||||||
)}
|
)}
|
||||||
|
warehousesWithStocks={
|
||||||
|
product?.variants[0].stocks.map(
|
||||||
|
stock => stock.warehouse.id
|
||||||
|
) || []
|
||||||
|
}
|
||||||
onConfirm={data =>
|
onConfirm={data =>
|
||||||
addOrRemoveStocks({
|
addOrRemoveStocks({
|
||||||
variables: {
|
variables: {
|
||||||
|
|
Loading…
Reference in a new issue