Add warehouse stock edit prototype

This commit is contained in:
dominik-zeglen 2020-03-23 13:23:29 +01:00
parent 65dedb4263
commit f94fdba7b9
7 changed files with 211 additions and 5 deletions

View file

@ -64,6 +64,7 @@ export interface ProductUpdatePageProps extends ListActions {
saveButtonBarState: ConfirmButtonTransitionState;
fetchCategories: (query: string) => void;
fetchCollections: (query: string) => void;
onWarehousesEdit: () => void;
onVariantsAdd: () => void;
onVariantShow: (id: string) => () => void;
onImageDelete: (id: string) => () => void;
@ -109,6 +110,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
onVariantAdd,
onVariantsAdd,
onVariantShow,
onWarehousesEdit,
isChecked,
selected,
toggle,
@ -252,6 +254,7 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
stocks={stocks}
onChange={changeStockData}
onFormDataChange={change}
onWarehousesEdit={onWarehousesEdit}
/>
)}
<CardSpacer />

View file

@ -7,6 +7,7 @@ import Typography from "@material-ui/core/Typography";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import makeStyles from "@material-ui/core/styles/makeStyles";
import { diff, DiffData } from "fast-array-diff";
import ConfirmButton, {
ConfirmButtonTransitionState
@ -45,7 +46,7 @@ export interface ProductWarehousesDialogProps {
stocks: Product_variants_stocks[];
warehouses: SearchWarehouses_search_edges_node[];
onClose: () => void;
onConfirm: (data: string[]) => void;
onConfirm: (data: DiffData<string>) => void;
}
const ProductWarehousesDialog: React.FC<ProductWarehousesDialogProps> = ({
@ -61,11 +62,12 @@ const ProductWarehousesDialog: React.FC<ProductWarehousesDialogProps> = ({
const classes = useStyles({});
const intl = useIntl();
const initial = stocks?.map(stock => stock.warehouse.id) || [];
const [selectedWarehouses, setSelectedWarehouses] = useStateFromProps(
stocks?.map(stock => stock.warehouse.id) || []
initial
);
const handleConfirm = () => onConfirm(selectedWarehouses);
const handleConfirm = () => onConfirm(diff(initial, selectedWarehouses));
return (
<Dialog onClose={onClose} maxWidth="sm" fullWidth open={open}>

View file

@ -1,6 +1,7 @@
import gql from "graphql-tag";
import { productErrorFragment } from "@saleor/attributes/mutations";
import makeMutation from "@saleor/hooks/makeMutation";
import { TypedMutation } from "../mutations";
import { ProductCreate, ProductCreateVariables } from "./types/ProductCreate";
import { ProductDelete, ProductDeleteVariables } from "./types/ProductDelete";
@ -37,7 +38,11 @@ import {
} from "./types/VariantImageUnassign";
import { VariantUpdate, VariantUpdateVariables } from "./types/VariantUpdate";
import { fragmentVariant, productFragmentDetails } from "./queries";
import {
fragmentVariant,
productFragmentDetails,
stockFragment
} from "./queries";
import {
productBulkDelete,
productBulkDeleteVariables
@ -54,6 +59,10 @@ import {
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
} from "./types/ProductVariantBulkDelete";
import {
AddOrRemoveStocks,
AddOrRemoveStocksVariables
} from "./types/AddOrRemoveStocks";
export const bulkProductErrorFragment = gql`
fragment BulkProductErrorFragment on BulkProductError {
@ -488,3 +497,42 @@ export const TypedProductVariantBulkDeleteMutation = TypedMutation<
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
>(ProductVariantBulkDeleteMutation);
const addOrRemoveStocks = gql`
${stockFragment}
mutation AddOrRemoveStocks(
$variantId: ID!
$add: [StockInput!]!
$remove: [ID!]!
) {
productVariantStocksCreate(stocks: $add, variantId: $variantId) {
bulkStockErrors {
code
field
index
}
productVariant {
id
stocks {
...StockFragment
}
}
}
productVariantStocksDelete(warehouseIds: $remove, variantId: $variantId) {
stockErrors {
code
field
}
productVariant {
id
stocks {
...StockFragment
}
}
}
}
`;
export const useAddOrRemoveStocks = makeMutation<
AddOrRemoveStocks,
AddOrRemoveStocksVariables
>(addOrRemoveStocks);

View file

@ -0,0 +1,83 @@
/* tslint:disable */
/* eslint-disable */
// This file was automatically generated and should not be edited.
import { StockInput, ProductErrorCode, StockErrorCode } from "./../../types/globalTypes";
// ====================================================
// GraphQL mutation operation: AddOrRemoveStocks
// ====================================================
export interface AddOrRemoveStocks_productVariantStocksCreate_bulkStockErrors {
__typename: "BulkStockError";
code: ProductErrorCode;
field: string | null;
index: number | null;
}
export interface AddOrRemoveStocks_productVariantStocksCreate_productVariant_stocks_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface AddOrRemoveStocks_productVariantStocksCreate_productVariant_stocks {
__typename: "Stock";
id: string;
quantity: number;
warehouse: AddOrRemoveStocks_productVariantStocksCreate_productVariant_stocks_warehouse;
}
export interface AddOrRemoveStocks_productVariantStocksCreate_productVariant {
__typename: "ProductVariant";
id: string;
stocks: (AddOrRemoveStocks_productVariantStocksCreate_productVariant_stocks | null)[] | null;
}
export interface AddOrRemoveStocks_productVariantStocksCreate {
__typename: "ProductVariantStocksCreate";
bulkStockErrors: AddOrRemoveStocks_productVariantStocksCreate_bulkStockErrors[];
productVariant: AddOrRemoveStocks_productVariantStocksCreate_productVariant | null;
}
export interface AddOrRemoveStocks_productVariantStocksDelete_stockErrors {
__typename: "StockError";
code: StockErrorCode;
field: string | null;
}
export interface AddOrRemoveStocks_productVariantStocksDelete_productVariant_stocks_warehouse {
__typename: "Warehouse";
id: string;
name: string;
}
export interface AddOrRemoveStocks_productVariantStocksDelete_productVariant_stocks {
__typename: "Stock";
id: string;
quantity: number;
warehouse: AddOrRemoveStocks_productVariantStocksDelete_productVariant_stocks_warehouse;
}
export interface AddOrRemoveStocks_productVariantStocksDelete_productVariant {
__typename: "ProductVariant";
id: string;
stocks: (AddOrRemoveStocks_productVariantStocksDelete_productVariant_stocks | null)[] | null;
}
export interface AddOrRemoveStocks_productVariantStocksDelete {
__typename: "ProductVariantStocksDelete";
stockErrors: AddOrRemoveStocks_productVariantStocksDelete_stockErrors[];
productVariant: AddOrRemoveStocks_productVariantStocksDelete_productVariant | null;
}
export interface AddOrRemoveStocks {
productVariantStocksCreate: AddOrRemoveStocks_productVariantStocksCreate | null;
productVariantStocksDelete: AddOrRemoveStocks_productVariantStocksDelete | null;
}
export interface AddOrRemoveStocksVariables {
variantId: string;
add: StockInput[];
remove: string[];
}

View file

@ -66,7 +66,11 @@ export const productListUrl = (params?: ProductListUrlQueryParams): string =>
productListPath + "?" + stringifyQs(params);
export const productPath = (id: string) => urlJoin(productSection + id);
export type ProductUrlDialog = "create-variants" | "remove" | "remove-variants";
export type ProductUrlDialog =
| "create-variants"
| "edit-stocks"
| "remove"
| "remove-variants";
export type ProductUrlQueryParams = BulkAction & Dialog<ProductUrlDialog>;
export const productUrl = (id: string, params?: ProductUrlQueryParams) =>
productPath(encodeURIComponent(id)) + "?" + stringifyQs(params);

View file

@ -19,6 +19,9 @@ import useCategorySearch from "@saleor/searches/useCategorySearch";
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import NotFoundPage from "@saleor/components/NotFoundPage";
import ProductWarehousesDialog from "@saleor/products/components/ProductWarehousesDialog";
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
import { useAddOrRemoveStocks } from "@saleor/products/mutations";
import { getMutationState, maybe } from "../../../misc";
import ProductUpdatePage from "../../components/ProductUpdatePage";
import ProductUpdateOperations from "../../containers/ProductUpdateOperations";
@ -71,6 +74,30 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
} = useCollectionSearch({
variables: DEFAULT_INITIAL_SEARCH_DATA
});
const {
loadMore: loadMoreWarehouses,
search: searchWarehouses,
result: searchWarehousesOpts
} = useWarehouseSearch({
variables: {
...DEFAULT_INITIAL_SEARCH_DATA,
first: 20
}
});
const [addOrRemoveStocks, addOrRemoveStocksOpts] = useAddOrRemoveStocks({
onCompleted: data => {
if (
data.productVariantStocksCreate.bulkStockErrors.length === 0 &&
data.productVariantStocksDelete.stockErrors.length === 0
) {
notify({
text: intl.formatMessage(commonMessages.savedChanges)
});
closeModal();
}
}
});
const [openModal, closeModal] = createDialogActionHandlers<
ProductUrlDialog,
@ -273,6 +300,7 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
loading: searchCollectionsOpts.loading,
onFetchMore: loadMoreCollections
}}
onWarehousesEdit={() => openModal("edit-stocks")}
/>
<ActionDialog
open={params.action === "remove"}
@ -345,6 +373,35 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
})
}
/>
{!product?.productType?.hasVariants && (
<ProductWarehousesDialog
confirmButtonState={addOrRemoveStocksOpts.status}
errors={[
...(addOrRemoveStocksOpts.data
?.productVariantStocksCreate.bulkStockErrors || []),
addOrRemoveStocksOpts.data?.productVariantStocksDelete
.stockErrors || []
]}
onClose={closeModal}
stocks={product?.variants[0].stocks || []}
open={params.action === "edit-stocks"}
warehouses={searchWarehousesOpts.data?.search.edges.map(
edge => edge.node
)}
onConfirm={data =>
addOrRemoveStocks({
variables: {
add: data.added.map(id => ({
quantity: 0,
warehouse: id
})),
remove: data.removed,
variantId: product.variants[0].id
}
})
}
/>
)}
</>
);
}}

View file

@ -689,6 +689,15 @@ export enum StockAvailability {
OUT_OF_STOCK = "OUT_OF_STOCK",
}
export enum StockErrorCode {
ALREADY_EXISTS = "ALREADY_EXISTS",
GRAPHQL_ERROR = "GRAPHQL_ERROR",
INVALID = "INVALID",
NOT_FOUND = "NOT_FOUND",
REQUIRED = "REQUIRED",
UNIQUE = "UNIQUE",
}
export enum TaxRateType {
ACCOMMODATION = "ACCOMMODATION",
ADMISSION_TO_CULTURAL_EVENTS = "ADMISSION_TO_CULTURAL_EVENTS",