Remove ProductUpdateOperations component

This commit is contained in:
dominik-zeglen 2020-08-24 11:50:55 +02:00
parent 8f66954590
commit 7726bacb77
4 changed files with 267 additions and 424 deletions

View file

@ -1,153 +0,0 @@
import React from "react";
import { getMutationProviderData, maybe } from "../../misc";
import { PartialMutationProviderOutput } from "../../types";
import {
TypedProductDeleteMutation,
TypedProductImageCreateMutation,
TypedProductImageDeleteMutation,
TypedProductUpdateMutation,
TypedProductVariantBulkDeleteMutation,
TypedSimpleProductUpdateMutation
} from "../mutations";
import { ProductDelete, ProductDeleteVariables } from "../types/ProductDelete";
import { ProductDetails_product } from "../types/ProductDetails";
import {
ProductImageCreate,
ProductImageCreateVariables
} from "../types/ProductImageCreate";
import {
ProductImageDelete,
ProductImageDeleteVariables
} from "../types/ProductImageDelete";
import {
ProductImageReorder,
ProductImageReorderVariables
} from "../types/ProductImageReorder";
import { ProductUpdate, ProductUpdateVariables } from "../types/ProductUpdate";
import {
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
} from "../types/ProductVariantBulkDelete";
import {
SimpleProductUpdate,
SimpleProductUpdateVariables
} from "../types/SimpleProductUpdate";
import ProductImagesReorderProvider from "./ProductImagesReorder";
interface ProductUpdateOperationsProps {
product: ProductDetails_product;
children: (props: {
bulkProductVariantDelete: PartialMutationProviderOutput<
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
>;
createProductImage: PartialMutationProviderOutput<
ProductImageCreate,
ProductImageCreateVariables
>;
deleteProduct: PartialMutationProviderOutput<
ProductDelete,
ProductDeleteVariables
>;
deleteProductImage: PartialMutationProviderOutput<
ProductImageDelete,
ProductImageDeleteVariables
>;
reorderProductImages: PartialMutationProviderOutput<
ProductImageReorder,
ProductImageReorderVariables
>;
updateProduct: PartialMutationProviderOutput<
ProductUpdate,
ProductUpdateVariables
>;
updateSimpleProduct: PartialMutationProviderOutput<
SimpleProductUpdate,
SimpleProductUpdateVariables
>;
}) => React.ReactNode;
onBulkProductVariantDelete?: (data: ProductVariantBulkDelete) => void;
onDelete?: (data: ProductDelete) => void;
onImageCreate?: (data: ProductImageCreate) => void;
onImageDelete?: (data: ProductImageDelete) => void;
onImageReorder?: (data: ProductImageReorder) => void;
onUpdate?: (data: ProductUpdate) => void;
}
const ProductUpdateOperations: React.FC<ProductUpdateOperationsProps> = ({
product,
children,
onBulkProductVariantDelete,
onDelete,
onImageDelete,
onImageCreate,
onImageReorder,
onUpdate
}) => {
const productId = product ? product.id : "";
return (
<TypedProductUpdateMutation onCompleted={onUpdate}>
{(...updateProduct) => (
<ProductImagesReorderProvider
productId={productId}
productImages={maybe(() => product.images, [])}
onCompleted={onImageReorder}
>
{(...reorderProductImages) => (
<TypedProductImageCreateMutation onCompleted={onImageCreate}>
{(...createProductImage) => (
<TypedProductDeleteMutation onCompleted={onDelete}>
{(...deleteProduct) => (
<TypedProductImageDeleteMutation
onCompleted={onImageDelete}
>
{(...deleteProductImage) => (
<TypedSimpleProductUpdateMutation
onCompleted={onUpdate}
>
{(...updateSimpleProduct) => (
<TypedProductVariantBulkDeleteMutation
onCompleted={onBulkProductVariantDelete}
>
{(...bulkProductVariantDelete) =>
children({
bulkProductVariantDelete: getMutationProviderData(
...bulkProductVariantDelete
),
createProductImage: getMutationProviderData(
...createProductImage
),
deleteProduct: getMutationProviderData(
...deleteProduct
),
deleteProductImage: getMutationProviderData(
...deleteProductImage
),
reorderProductImages: getMutationProviderData(
...reorderProductImages
),
updateProduct: getMutationProviderData(
...updateProduct
),
updateSimpleProduct: getMutationProviderData(
...updateSimpleProduct
)
})
}
</TypedProductVariantBulkDeleteMutation>
)}
</TypedSimpleProductUpdateMutation>
)}
</TypedProductImageDeleteMutation>
)}
</TypedProductDeleteMutation>
)}
</TypedProductImageCreateMutation>
)}
</ProductImagesReorderProvider>
)}
</TypedProductUpdateMutation>
);
};
export default ProductUpdateOperations;

View file

@ -13,7 +13,6 @@ import {
import makeMutation from "@saleor/hooks/makeMutation";
import gql from "graphql-tag";
import { TypedMutation } from "../mutations";
import {
productBulkDelete,
productBulkDeleteVariables
@ -80,7 +79,7 @@ export const productImageCreateMutation = gql`
}
}
`;
export const TypedProductImageCreateMutation = TypedMutation<
export const useProductImageCreateMutation = makeMutation<
ProductImageCreate,
ProductImageCreateVariables
>(productImageCreateMutation);
@ -98,7 +97,7 @@ export const productDeleteMutation = gql`
}
}
`;
export const TypedProductDeleteMutation = TypedMutation<
export const useProductDeleteMutation = makeMutation<
ProductDelete,
ProductDeleteVariables
>(productDeleteMutation);
@ -122,7 +121,7 @@ export const productImagesReorder = gql`
}
}
`;
export const TypedProductImagesReorder = TypedMutation<
export const useProductImagesReorder = makeMutation<
ProductImageReorder,
ProductImageReorderVariables
>(productImagesReorder);
@ -167,7 +166,7 @@ export const productUpdateMutation = gql`
}
}
`;
export const TypedProductUpdateMutation = TypedMutation<
export const useProductUpdateMutation = makeMutation<
ProductUpdate,
ProductUpdateVariables
>(productUpdateMutation);
@ -263,7 +262,7 @@ export const simpleProductUpdateMutation = gql`
}
}
`;
export const TypedSimpleProductUpdateMutation = TypedMutation<
export const useSimpleProductUpdateMutation = makeMutation<
SimpleProductUpdate,
SimpleProductUpdateVariables
>(simpleProductUpdateMutation);
@ -316,7 +315,7 @@ export const productCreateMutation = gql`
}
}
`;
export const TypedProductCreateMutation = TypedMutation<
export const useProductCreateMutation = makeMutation<
ProductCreate,
ProductCreateVariables
>(productCreateMutation);
@ -334,7 +333,7 @@ export const variantDeleteMutation = gql`
}
}
`;
export const TypedVariantDeleteMutation = TypedMutation<
export const useVariantDeleteMutation = makeMutation<
VariantDelete,
VariantDeleteVariables
>(variantDeleteMutation);
@ -406,7 +405,7 @@ export const variantUpdateMutation = gql`
}
}
`;
export const TypedVariantUpdateMutation = TypedMutation<
export const useVariantUpdateMutation = makeMutation<
VariantUpdate,
VariantUpdateVariables
>(variantUpdateMutation);
@ -425,7 +424,7 @@ export const variantCreateMutation = gql`
}
}
`;
export const TypedVariantCreateMutation = TypedMutation<
export const useVariantCreateMutation = makeMutation<
VariantCreate,
VariantCreateVariables
>(variantCreateMutation);
@ -446,7 +445,7 @@ export const productImageDeleteMutation = gql`
}
}
`;
export const TypedProductImageDeleteMutation = TypedMutation<
export const useProductImageDeleteMutation = makeMutation<
ProductImageDelete,
ProductImageDeleteVariables
>(productImageDeleteMutation);
@ -465,7 +464,7 @@ export const productImageUpdateMutation = gql`
}
}
`;
export const TypedProductImageUpdateMutation = TypedMutation<
export const useProductImageUpdateMutation = makeMutation<
ProductImageUpdate,
ProductImageUpdateVariables
>(productImageUpdateMutation);
@ -484,7 +483,7 @@ export const variantImageAssignMutation = gql`
}
}
`;
export const TypedVariantImageAssignMutation = TypedMutation<
export const useVariantImageAssignMutation = makeMutation<
VariantImageAssign,
VariantImageAssignVariables
>(variantImageAssignMutation);
@ -503,7 +502,7 @@ export const variantImageUnassignMutation = gql`
}
}
`;
export const TypedVariantImageUnassignMutation = TypedMutation<
export const useVariantImageUnassignMutation = makeMutation<
VariantImageUnassign,
VariantImageUnassignVariables
>(variantImageUnassignMutation);
@ -518,7 +517,7 @@ export const productBulkDeleteMutation = gql`
}
}
`;
export const TypedProductBulkDeleteMutation = TypedMutation<
export const useProductBulkDeleteMutation = makeMutation<
productBulkDelete,
productBulkDeleteVariables
>(productBulkDeleteMutation);
@ -533,7 +532,7 @@ export const productBulkPublishMutation = gql`
}
}
`;
export const TypedProductBulkPublishMutation = TypedMutation<
export const useProductBulkPublishMutation = makeMutation<
productBulkPublish,
productBulkPublishVariables
>(productBulkPublishMutation);
@ -566,7 +565,7 @@ export const ProductVariantBulkDeleteMutation = gql`
}
}
`;
export const TypedProductVariantBulkDeleteMutation = TypedMutation<
export const useProductVariantBulkDeleteMutation = makeMutation<
ProductVariantBulkDelete,
ProductVariantBulkDeleteVariables
>(ProductVariantBulkDeleteMutation);

View file

@ -174,7 +174,7 @@ const productDetailsQuery = gql`
}
}
`;
export const TypedProductDetailsQuery = TypedQuery<
export const useProductDetails = makeQuery<
ProductDetails,
ProductDetailsVariables
>(productDetailsQuery);

View file

@ -11,6 +11,15 @@ import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop";
import { commonMessages } from "@saleor/intl";
import {
useProductDeleteMutation,
useProductImageCreateMutation,
useProductImageDeleteMutation,
useProductImagesReorder,
useProductUpdateMutation,
useProductVariantBulkDeleteMutation,
useSimpleProductUpdateMutation
} from "@saleor/products/mutations";
import useCategorySearch from "@saleor/searches/useCategorySearch";
import useCollectionSearch from "@saleor/searches/useCollectionSearch";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
@ -20,14 +29,9 @@ import { FormattedMessage, useIntl } from "react-intl";
import { getMutationState, maybe } from "../../../misc";
import ProductUpdatePage from "../../components/ProductUpdatePage";
import ProductUpdateOperations from "../../containers/ProductUpdateOperations";
import { TypedProductDetailsQuery } from "../../queries";
import {
ProductImageCreate,
ProductImageCreateVariables
} from "../../types/ProductImageCreate";
import { useProductDetails } from "../../queries";
import { ProductImageCreateVariables } from "../../types/ProductImageCreate";
import { ProductUpdate as ProductUpdateMutationResult } from "../../types/ProductUpdate";
import { ProductVariantBulkDelete } from "../../types/ProductVariantBulkDelete";
import {
productImageUrl,
productListUrl,
@ -78,6 +82,86 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
});
const shop = useShop();
const { data, loading, refetch } = useProductDetails({
displayLoader: true,
variables: {
id
}
});
const handleUpdate = (data: ProductUpdateMutationResult) => {
if (data.productUpdate.errors.length === 0) {
notify({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges)
});
}
};
const [updateProduct, updateProductOpts] = useProductUpdateMutation({
onCompleted: handleUpdate
});
const [
updateSimpleProduct,
updateSimpleProductOpts
] = useSimpleProductUpdateMutation({
onCompleted: handleUpdate
});
const [
reorderProductImages,
reorderProductImagesOpts
] = useProductImagesReorder({});
const [deleteProduct, deleteProductOpts] = useProductDeleteMutation({
onCompleted: () => {
notify({
status: "success",
text: intl.formatMessage({
defaultMessage: "Product removed"
})
});
navigate(productListUrl());
}
});
const [
createProductImage,
createProductImageOpts
] = useProductImageCreateMutation({
onCompleted: data => {
const imageError = data.productImageCreate.errors.find(
error => error.field === ("image" as keyof ProductImageCreateVariables)
);
if (imageError) {
notify({
status: "error",
text: intl.formatMessage(commonMessages.somethingWentWrong)
});
}
}
});
const [deleteProductImage] = useProductImageDeleteMutation({
onCompleted: () =>
notify({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges)
})
});
const [
bulkProductVariantDelete,
bulkProductVariantDeleteOpts
] = useProductVariantBulkDeleteMutation({
onCompleted: data => {
if (data.productVariantBulkDelete.errors.length === 0) {
closeModal();
reset();
refetch();
}
}
});
const [openModal, closeModal] = createDialogActionHandlers<
ProductUrlDialog,
ProductUrlQueryParams
@ -85,256 +169,169 @@ export const ProductUpdate: React.FC<ProductUpdateProps> = ({ id, params }) => {
const handleBack = () => navigate(productListUrl());
const product = data?.product;
if (product === null) {
return <NotFoundPage onBack={handleBack} />;
}
const handleVariantAdd = () => navigate(productVariantAddUrl(id));
const handleImageDelete = (id: string) => () =>
deleteProductImage({ variables: { id } });
const handleImageEdit = (imageId: string) => () =>
navigate(productImageUrl(id, imageId));
const handleSubmit = createUpdateHandler(
product,
variables => updateProduct({ variables }),
variables => updateSimpleProduct({ variables })
);
const handleImageUpload = createImageUploadHandler(id, variables =>
createProductImage({ variables })
);
const handleImageReorder = createImageReorderHandler(product, variables =>
reorderProductImages({ variables })
);
const disableFormSave =
createProductImageOpts.loading ||
deleteProductOpts.loading ||
reorderProductImagesOpts.loading ||
updateProductOpts.loading ||
loading;
const formTransitionState = getMutationState(
updateProductOpts.called || updateSimpleProductOpts.called,
updateProductOpts.loading || updateSimpleProductOpts.loading,
maybe(() => updateProductOpts.data.productUpdate.errors),
maybe(() => updateSimpleProductOpts.data.productUpdate.errors),
maybe(() => updateSimpleProductOpts.data.productVariantUpdate.errors)
);
const categories = maybe(
() => searchCategoriesOpts.data.search.edges,
[]
).map(edge => edge.node);
const collections = maybe(
() => searchCollectionsOpts.data.search.edges,
[]
).map(edge => edge.node);
const errors = [
...maybe(() => updateProductOpts.data.productUpdate.errors, []),
...maybe(() => updateSimpleProductOpts.data.productUpdate.errors, [])
];
return (
<TypedProductDetailsQuery displayLoader variables={{ id }}>
{({ data, loading, refetch }) => {
const product = data?.product;
if (product === null) {
return <NotFoundPage onBack={handleBack} />;
<>
<WindowTitle title={maybe(() => data.product.name)} />
<ProductUpdatePage
categories={categories}
collections={collections}
defaultWeightUnit={shop?.defaultWeightUnit}
disabled={disableFormSave}
errors={errors}
fetchCategories={searchCategories}
fetchCollections={searchCollections}
saveButtonBarState={formTransitionState}
images={maybe(() => data.product.images)}
header={maybe(() => product.name)}
placeholderImage={placeholderImg}
product={product}
warehouses={
warehouses.data?.warehouses.edges.map(edge => edge.node) || []
}
const handleDelete = () => {
notify({
status: "success",
text: intl.formatMessage({
defaultMessage: "Product removed"
})
});
navigate(productListUrl());
};
const handleUpdate = (data: ProductUpdateMutationResult) => {
if (data.productUpdate.errors.length === 0) {
notify({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges)
});
}
};
const handleImageCreate = (data: ProductImageCreate) => {
const imageError = data.productImageCreate.errors.find(
error =>
error.field === ("image" as keyof ProductImageCreateVariables)
);
if (imageError) {
notify({
status: "error",
text: intl.formatMessage(commonMessages.somethingWentWrong)
});
}
};
const handleImageDeleteSuccess = () =>
notify({
status: "success",
text: intl.formatMessage(commonMessages.savedChanges)
});
const handleVariantAdd = () => navigate(productVariantAddUrl(id));
const handleBulkProductVariantDelete = (
data: ProductVariantBulkDelete
) => {
if (data.productVariantBulkDelete.errors.length === 0) {
closeModal();
reset();
refetch();
}
};
return (
<ProductUpdateOperations
product={product}
onBulkProductVariantDelete={handleBulkProductVariantDelete}
onDelete={handleDelete}
onImageCreate={handleImageCreate}
onImageDelete={handleImageDeleteSuccess}
onUpdate={handleUpdate}
variants={maybe(() => product.variants)}
onBack={handleBack}
onDelete={() => openModal("remove")}
onImageReorder={handleImageReorder}
onSubmit={handleSubmit}
onVariantAdd={handleVariantAdd}
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
onVariantShow={variantId => () =>
navigate(productVariantEditUrl(product.id, variantId))}
onImageUpload={handleImageUpload}
onImageEdit={handleImageEdit}
onImageDelete={handleImageDelete}
toolbar={
<IconButton
color="primary"
onClick={() =>
openModal("remove-variants", {
ids: listElements
})
}
>
{({
bulkProductVariantDelete,
createProductImage,
deleteProduct,
deleteProductImage,
reorderProductImages,
updateProduct,
updateSimpleProduct
}) => {
const handleImageDelete = (id: string) => () =>
deleteProductImage.mutate({ id });
const handleImageEdit = (imageId: string) => () =>
navigate(productImageUrl(id, imageId));
const handleSubmit = createUpdateHandler(
product,
updateProduct.mutate,
updateSimpleProduct.mutate
);
const handleImageUpload = createImageUploadHandler(
id,
createProductImage.mutate
);
const handleImageReorder = createImageReorderHandler(
product,
reorderProductImages.mutate
);
const disableFormSave =
createProductImage.opts.loading ||
deleteProduct.opts.loading ||
reorderProductImages.opts.loading ||
updateProduct.opts.loading ||
loading;
const formTransitionState = getMutationState(
updateProduct.opts.called || updateSimpleProduct.opts.called,
updateProduct.opts.loading || updateSimpleProduct.opts.loading,
maybe(() => updateProduct.opts.data.productUpdate.errors),
maybe(() => updateSimpleProduct.opts.data.productUpdate.errors),
maybe(
() =>
updateSimpleProduct.opts.data.productVariantUpdate.errors
)
);
const categories = maybe(
() => searchCategoriesOpts.data.search.edges,
[]
).map(edge => edge.node);
const collections = maybe(
() => searchCollectionsOpts.data.search.edges,
[]
).map(edge => edge.node);
const errors = [
...maybe(
() => updateProduct.opts.data.productUpdate.errors,
[]
),
...maybe(
() => updateSimpleProduct.opts.data.productUpdate.errors,
[]
)
];
return (
<>
<WindowTitle title={maybe(() => data.product.name)} />
<ProductUpdatePage
categories={categories}
collections={collections}
defaultWeightUnit={shop?.defaultWeightUnit}
disabled={disableFormSave}
errors={errors}
fetchCategories={searchCategories}
fetchCollections={searchCollections}
saveButtonBarState={formTransitionState}
images={maybe(() => data.product.images)}
header={maybe(() => product.name)}
placeholderImage={placeholderImg}
product={product}
warehouses={
warehouses.data?.warehouses.edges.map(
edge => edge.node
) || []
}
variants={maybe(() => product.variants)}
onBack={handleBack}
onDelete={() => openModal("remove")}
onImageReorder={handleImageReorder}
onSubmit={handleSubmit}
onVariantAdd={handleVariantAdd}
onVariantsAdd={() => navigate(productVariantCreatorUrl(id))}
onVariantShow={variantId => () =>
navigate(productVariantEditUrl(product.id, variantId))}
onImageUpload={handleImageUpload}
onImageEdit={handleImageEdit}
onImageDelete={handleImageDelete}
toolbar={
<IconButton
color="primary"
onClick={() =>
openModal("remove-variants", {
ids: listElements
})
}
>
<DeleteIcon />
</IconButton>
}
isChecked={isSelected}
selected={listElements.length}
toggle={toggle}
toggleAll={toggleAll}
fetchMoreCategories={{
hasMore: maybe(
() =>
searchCategoriesOpts.data.search.pageInfo.hasNextPage
),
loading: searchCategoriesOpts.loading,
onFetchMore: loadMoreCategories
}}
fetchMoreCollections={{
hasMore: maybe(
() =>
searchCollectionsOpts.data.search.pageInfo.hasNextPage
),
loading: searchCollectionsOpts.loading,
onFetchMore: loadMoreCollections
}}
/>
<ActionDialog
open={params.action === "remove"}
onClose={closeModal}
confirmButtonState={deleteProduct.opts.status}
onConfirm={() => deleteProduct.mutate({ id })}
variant="delete"
title={intl.formatMessage({
defaultMessage: "Delete Product",
description: "dialog header"
})}
>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {name}?"
description="delete product"
values={{
name: product ? product.name : undefined
}}
/>
</DialogContentText>
</ActionDialog>
<ActionDialog
open={params.action === "remove-variants"}
onClose={closeModal}
confirmButtonState={bulkProductVariantDelete.opts.status}
onConfirm={() =>
bulkProductVariantDelete.mutate({
ids: params.ids
})
}
variant="delete"
title={intl.formatMessage({
defaultMessage: "Delete Product Variants",
description: "dialog header"
})}
>
<DialogContentText>
<FormattedMessage
defaultMessage="{counter,plural,one{Are you sure you want to delete this variant?} other{Are you sure you want to delete {displayQuantity} variants?}}"
description="dialog content"
values={{
counter: maybe(() => params.ids.length),
displayQuantity: (
<strong>{maybe(() => params.ids.length)}</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
</>
);
<DeleteIcon />
</IconButton>
}
isChecked={isSelected}
selected={listElements.length}
toggle={toggle}
toggleAll={toggleAll}
fetchMoreCategories={{
hasMore: maybe(
() => searchCategoriesOpts.data.search.pageInfo.hasNextPage
),
loading: searchCategoriesOpts.loading,
onFetchMore: loadMoreCategories
}}
fetchMoreCollections={{
hasMore: maybe(
() => searchCollectionsOpts.data.search.pageInfo.hasNextPage
),
loading: searchCollectionsOpts.loading,
onFetchMore: loadMoreCollections
}}
/>
<ActionDialog
open={params.action === "remove"}
onClose={closeModal}
confirmButtonState={deleteProductOpts.status}
onConfirm={() => deleteProduct({ variables: { id } })}
variant="delete"
title={intl.formatMessage({
defaultMessage: "Delete Product",
description: "dialog header"
})}
>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {name}?"
description="delete product"
values={{
name: product ? product.name : undefined
}}
</ProductUpdateOperations>
);
}}
</TypedProductDetailsQuery>
/>
</DialogContentText>
</ActionDialog>
<ActionDialog
open={params.action === "remove-variants"}
onClose={closeModal}
confirmButtonState={bulkProductVariantDeleteOpts.status}
onConfirm={() =>
bulkProductVariantDelete({
variables: {
ids: params.ids
}
})
}
variant="delete"
title={intl.formatMessage({
defaultMessage: "Delete Product Variants",
description: "dialog header"
})}
>
<DialogContentText>
<FormattedMessage
defaultMessage="{counter,plural,one{Are you sure you want to delete this variant?} other{Are you sure you want to delete {displayQuantity} variants?}}"
description="dialog content"
values={{
counter: maybe(() => params.ids.length),
displayQuantity: <strong>{maybe(() => params.ids.length)}</strong>
}}
/>
</DialogContentText>
</ActionDialog>
</>
);
};
export default ProductUpdate;