Use mutation hooks

This commit is contained in:
dominik-zeglen 2019-11-13 17:46:08 +01:00
parent 8170f9e14e
commit 1dd0488c62
6 changed files with 506 additions and 490 deletions

View file

@ -47,7 +47,7 @@
"moment-timezone": "^0.5.26", "moment-timezone": "^0.5.26",
"qs": "^6.9.0", "qs": "^6.9.0",
"react": "^16.9.0", "react": "^16.9.0",
"react-apollo": "^3.0.0", "react-apollo": "^3.1.3",
"react-dom": "^16.9.0", "react-dom": "^16.9.0",
"react-dropzone": "^8.2.0", "react-dropzone": "^8.2.0",
"react-error-boundary": "^1.2.5", "react-error-boundary": "^1.2.5",

View file

@ -1,6 +1,6 @@
import gql from "graphql-tag"; import gql from "graphql-tag";
import { TypedMutation } from "../mutations"; import makeMutation from "@saleor/hooks/makeMutation";
import { categoryDetailsFragment } from "./queries"; import { categoryDetailsFragment } from "./queries";
import { import {
CategoryBulkDelete, CategoryBulkDelete,
@ -29,7 +29,7 @@ export const categoryDeleteMutation = gql`
} }
} }
`; `;
export const TypedCategoryDeleteMutation = TypedMutation< export const useCategoryDeleteMutation = makeMutation<
CategoryDelete, CategoryDelete,
CategoryDeleteVariables CategoryDeleteVariables
>(categoryDeleteMutation); >(categoryDeleteMutation);
@ -48,7 +48,7 @@ export const categoryCreateMutation = gql`
} }
} }
`; `;
export const TypedCategoryCreateMutation = TypedMutation< export const useCategoryCreateMutation = makeMutation<
CategoryCreate, CategoryCreate,
CategoryCreateVariables CategoryCreateVariables
>(categoryCreateMutation); >(categoryCreateMutation);
@ -67,7 +67,7 @@ export const categoryUpdateMutation = gql`
} }
} }
`; `;
export const TypedCategoryUpdateMutation = TypedMutation< export const useCategoryUpdateMutation = makeMutation<
CategoryUpdate, CategoryUpdate,
CategoryUpdateVariables CategoryUpdateVariables
>(categoryUpdateMutation); >(categoryUpdateMutation);
@ -82,7 +82,7 @@ export const categoryBulkDeleteMutation = gql`
} }
} }
`; `;
export const TypedCategoryBulkDeleteMutation = TypedMutation< export const useCategoryBulkDeleteMutation = makeMutation<
CategoryBulkDelete, CategoryBulkDelete,
CategoryBulkDeleteVariables CategoryBulkDeleteVariables
>(categoryBulkDeleteMutation); >(categoryBulkDeleteMutation);

View file

@ -6,7 +6,7 @@ import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier"; import useNotifier from "@saleor/hooks/useNotifier";
import { getMutationState, maybe } from "../../misc"; import { getMutationState, maybe } from "../../misc";
import CategoryCreatePage from "../components/CategoryCreatePage"; import CategoryCreatePage from "../components/CategoryCreatePage";
import { TypedCategoryCreateMutation } from "../mutations"; import { useCategoryCreateMutation } from "../mutations";
import { CategoryCreate } from "../types/CategoryCreate"; import { CategoryCreate } from "../types/CategoryCreate";
import { categoryListUrl, categoryUrl } from "../urls"; import { categoryListUrl, categoryUrl } from "../urls";
@ -31,55 +31,54 @@ export const CategoryCreateView: React.FC<CategoryCreateViewProps> = ({
navigate(categoryUrl(data.categoryCreate.category.id)); navigate(categoryUrl(data.categoryCreate.category.id));
} }
}; };
const [createCategory, createCategoryResult] = useCategoryCreateMutation({
onCompleted: handleSuccess
});
const errors = maybe(
() => createCategoryResult.data.categoryCreate.errors,
[]
);
const formTransitionState = getMutationState(
createCategoryResult.called,
createCategoryResult.loading,
errors
);
return ( return (
<TypedCategoryCreateMutation onCompleted={handleSuccess}> <>
{(createCategory, createCategoryResult) => { <WindowTitle
const errors = maybe( title={intl.formatMessage({
() => createCategoryResult.data.categoryCreate.errors, defaultMessage: "Create category",
[] description: "window title"
); })}
/>
const formTransitionState = getMutationState( <CategoryCreatePage
createCategoryResult.called, saveButtonBarState={formTransitionState}
createCategoryResult.loading, errors={errors}
errors disabled={createCategoryResult.loading}
); onBack={() =>
navigate(parentId ? categoryUrl(parentId) : categoryListUrl())
return ( }
<> onSubmit={formData =>
<WindowTitle createCategory({
title={intl.formatMessage({ variables: {
defaultMessage: "Create category", input: {
description: "window title" descriptionJson: JSON.stringify(formData.description),
})} name: formData.name,
/> seo: {
<CategoryCreatePage description: formData.seoDescription,
saveButtonBarState={formTransitionState} title: formData.seoTitle
errors={errors} }
disabled={createCategoryResult.loading} },
onBack={() => parent: parentId || null
navigate(parentId ? categoryUrl(parentId) : categoryListUrl()) }
} })
onSubmit={formData => }
createCategory({ />
variables: { </>
input: {
descriptionJson: JSON.stringify(formData.description),
name: formData.name,
seo: {
description: formData.seoDescription,
title: formData.seoTitle
}
},
parent: parentId || null
}
})
}
/>
</>
);
}}
</TypedCategoryCreateMutation>
); );
}; };
export default CategoryCreateView; export default CategoryCreateView;

View file

@ -24,9 +24,9 @@ import {
CategoryUpdatePage CategoryUpdatePage
} from "../components/CategoryUpdatePage/CategoryUpdatePage"; } from "../components/CategoryUpdatePage/CategoryUpdatePage";
import { import {
TypedCategoryBulkDeleteMutation, useCategoryBulkDeleteMutation,
TypedCategoryDeleteMutation, useCategoryDeleteMutation,
TypedCategoryUpdateMutation useCategoryUpdateMutation
} from "../mutations"; } from "../mutations";
import { TypedCategoryDetailsQuery } from "../queries"; import { TypedCategoryDetailsQuery } from "../queries";
import { CategoryBulkDelete } from "../types/CategoryBulkDelete"; import { CategoryBulkDelete } from "../types/CategoryBulkDelete";
@ -73,6 +73,11 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
navigate(categoryListUrl()); navigate(categoryListUrl());
} }
}; };
const [deleteCategory, deleteResult] = useCategoryDeleteMutation({
onCompleted: handleCategoryDelete
});
const handleCategoryUpdate = (data: CategoryUpdate) => { const handleCategoryUpdate = (data: CategoryUpdate) => {
if (data.categoryUpdate.errors.length > 0) { if (data.categoryUpdate.errors.length > 0) {
const backgroundImageError = data.categoryUpdate.errors.find( const backgroundImageError = data.categoryUpdate.errors.find(
@ -86,6 +91,27 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
} }
}; };
const [updateCategory, updateResult] = useCategoryUpdateMutation({
onCompleted: handleCategoryUpdate
});
const handleBulkCategoryDelete = (data: CategoryBulkDelete) => {
if (data.categoryBulkDelete.errors.length === 0) {
closeModal();
notify({
text: intl.formatMessage(commonMessages.savedChanges)
});
reset();
}
};
const [
categoryBulkDelete,
categoryBulkDeleteOpts
] = useCategoryBulkDeleteMutation({
onCompleted: handleBulkCategoryDelete
});
const changeTab = (tabName: CategoryPageTab) => { const changeTab = (tabName: CategoryPageTab) => {
reset(); reset();
navigate( navigate(
@ -114,329 +140,257 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
}) })
); );
const paginationState = createPaginationState(PAGINATE_BY, params);
const formTransitionState = getMutationState(
updateResult.called,
updateResult.loading,
maybe(() => updateResult.data.categoryUpdate.errors)
);
const removeDialogTransitionState = getMutationState(
deleteResult.called,
deleteResult.loading,
maybe(() => deleteResult.data.categoryDelete.errors)
);
return ( return (
<TypedCategoryDeleteMutation onCompleted={handleCategoryDelete}> <TypedCategoryDetailsQuery
{(deleteCategory, deleteResult) => ( displayLoader
<TypedCategoryUpdateMutation onCompleted={handleCategoryUpdate}> variables={{ ...paginationState, id }}
{(updateCategory, updateResult) => { require={["category"]}
const paginationState = createPaginationState(PAGINATE_BY, params); >
const formTransitionState = getMutationState( {({ data, loading, refetch }) => {
updateResult.called, const handleBulkProductDelete = (data: productBulkDelete) => {
updateResult.loading, if (data.productBulkDelete.errors.length === 0) {
maybe(() => updateResult.data.categoryUpdate.errors) closeModal();
); notify({
const removeDialogTransitionState = getMutationState( text: intl.formatMessage(commonMessages.savedChanges)
deleteResult.called, });
deleteResult.loading, refetch();
maybe(() => deleteResult.data.categoryDelete.errors) reset();
); }
return ( };
<TypedCategoryDetailsQuery
displayLoader
variables={{ ...paginationState, id }}
require={["category"]}
>
{({ data, loading, refetch }) => {
const handleBulkCategoryDelete = (
data: CategoryBulkDelete
) => {
if (data.categoryBulkDelete.errors.length === 0) {
closeModal();
notify({
text: intl.formatMessage(commonMessages.savedChanges)
});
refetch();
reset();
}
};
const handleBulkProductDelete = (data: productBulkDelete) => { const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
if (data.productBulkDelete.errors.length === 0) { params.activeTab === CategoryPageTab.categories
closeModal(); ? maybe(() => data.category.children.pageInfo)
notify({ : maybe(() => data.category.products.pageInfo),
text: intl.formatMessage(commonMessages.savedChanges) paginationState,
}); params
refetch(); );
reset();
}
};
const { loadNextPage, loadPreviousPage, pageInfo } = paginate( return (
params.activeTab === CategoryPageTab.categories <>
? maybe(() => data.category.children.pageInfo) <WindowTitle title={maybe(() => data.category.name)} />
: maybe(() => data.category.products.pageInfo), <TypedProductBulkDeleteMutation
paginationState, onCompleted={handleBulkProductDelete}
params >
); {(productBulkDelete, productBulkDeleteOpts) => {
const categoryBulkDeleteMutationState = getMutationState(
categoryBulkDeleteOpts.called,
categoryBulkDeleteOpts.loading,
maybe(
() => categoryBulkDeleteOpts.data.categoryBulkDelete.errors
)
);
const productBulkDeleteMutationState = getMutationState(
productBulkDeleteOpts.called,
productBulkDeleteOpts.loading,
maybe(
() => productBulkDeleteOpts.data.productBulkDelete.errors
)
);
return ( return (
<> <>
<WindowTitle title={maybe(() => data.category.name)} /> <CategoryUpdatePage
<TypedCategoryBulkDeleteMutation changeTab={changeTab}
onCompleted={handleBulkCategoryDelete} currentTab={params.activeTab}
> category={maybe(() => data.category)}
{(categoryBulkDelete, categoryBulkDeleteOpts) => ( disabled={loading}
<TypedProductBulkDeleteMutation errors={maybe(
onCompleted={handleBulkProductDelete} () => updateResult.data.categoryUpdate.errors
> )}
{(productBulkDelete, productBulkDeleteOpts) => { onAddCategory={() => navigate(categoryAddUrl(id))}
const categoryBulkDeleteMutationState = getMutationState( onAddProduct={() => navigate(productAddUrl)}
categoryBulkDeleteOpts.called, onBack={() =>
categoryBulkDeleteOpts.loading, navigate(
maybe( maybe(
() => () => categoryUrl(data.category.parent.id),
categoryBulkDeleteOpts.data categoryListUrl()
.categoryBulkDelete.errors )
) )
); }
const productBulkDeleteMutationState = getMutationState( onCategoryClick={id => () => navigate(categoryUrl(id))}
productBulkDeleteOpts.called, onDelete={() => openModal("delete")}
productBulkDeleteOpts.loading, onImageDelete={() =>
maybe( updateCategory({
() => variables: {
productBulkDeleteOpts.data.productBulkDelete id,
.errors input: {
) backgroundImage: null
); }
}
return ( })
<> }
<CategoryUpdatePage onImageUpload={file =>
changeTab={changeTab} updateCategory({
currentTab={params.activeTab} variables: {
category={maybe(() => data.category)} id,
disabled={loading} input: {
errors={maybe( backgroundImage: file
() => }
updateResult.data.categoryUpdate.errors }
)} })
onAddCategory={() => }
navigate(categoryAddUrl(id)) onNextPage={loadNextPage}
} onPreviousPage={loadPreviousPage}
onAddProduct={() => navigate(productAddUrl)} pageInfo={pageInfo}
onBack={() => onProductClick={id => () => navigate(productUrl(id))}
navigate( onSubmit={formData =>
maybe( updateCategory({
() => variables: {
categoryUrl( id,
data.category.parent.id input: {
), backgroundImageAlt: formData.backgroundImageAlt,
categoryListUrl() descriptionJson: JSON.stringify(
) formData.description
) ),
} name: formData.name,
onCategoryClick={id => () => seo: {
navigate(categoryUrl(id))} description: formData.seoDescription,
onDelete={() => openModal("delete")} title: formData.seoTitle
onImageDelete={() => }
updateCategory({ }
variables: { }
id, })
input: { }
backgroundImage: null products={maybe(() =>
} data.category.products.edges.map(edge => edge.node)
} )}
}) saveButtonBarState={formTransitionState}
} subcategories={maybe(() =>
onImageUpload={file => data.category.children.edges.map(edge => edge.node)
updateCategory({ )}
variables: { subcategoryListToolbar={
id, <IconButton
input: { color="primary"
backgroundImage: file onClick={() =>
} openModal("delete-categories", listElements)
} }
}) >
} <DeleteIcon />
onNextPage={loadNextPage} </IconButton>
onPreviousPage={loadPreviousPage} }
pageInfo={pageInfo} productListToolbar={
onProductClick={id => () => <IconButton
navigate(productUrl(id))} color="primary"
onSubmit={formData => onClick={() =>
updateCategory({ openModal("delete-products", listElements)
variables: { }
id, >
input: { <DeleteIcon />
backgroundImageAlt: </IconButton>
formData.backgroundImageAlt, }
descriptionJson: JSON.stringify( isChecked={isSelected}
formData.description selected={listElements.length}
), toggle={toggle}
name: formData.name, toggleAll={toggleAll}
seo: { />
description: <ActionDialog
formData.seoDescription, confirmButtonState={removeDialogTransitionState}
title: formData.seoTitle onClose={closeModal}
} onConfirm={() => deleteCategory({ variables: { id } })}
} open={params.action === "delete"}
} title={intl.formatMessage({
}) defaultMessage: "Delete category",
} description: "dialog title"
products={maybe(() => })}
data.category.products.edges.map( variant="delete"
edge => edge.node >
) <DialogContentText>
)} <FormattedMessage
saveButtonBarState={formTransitionState} defaultMessage="Are you sure you want to delete {categoryName}?"
subcategories={maybe(() => values={{
data.category.children.edges.map( categoryName: (
edge => edge.node <strong>
) {maybe(() => data.category.name, "...")}
)} </strong>
subcategoryListToolbar={ )
<IconButton }}
color="primary" />
onClick={() => </DialogContentText>
openModal( <DialogContentText>
"delete-categories", <FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." />
listElements </DialogContentText>
) </ActionDialog>
} <ActionDialog
> open={
<DeleteIcon /> params.action === "delete-categories" &&
</IconButton> maybe(() => params.ids.length > 0)
} }
productListToolbar={ confirmButtonState={categoryBulkDeleteMutationState}
<IconButton onClose={closeModal}
color="primary" onConfirm={() =>
onClick={() => categoryBulkDelete({
openModal( variables: { ids: params.ids }
"delete-products", }).then(() => refetch())
listElements }
) title={intl.formatMessage({
} defaultMessage: "Delete categories",
> description: "dialog title"
<DeleteIcon /> })}
</IconButton> variant="delete"
} >
isChecked={isSelected} <DialogContentText>
selected={listElements.length} <FormattedMessage
toggle={toggle} defaultMessage="Are you sure you want to delete {counter,plural,one{this category} other{{displayQuantity} categories}}?"
toggleAll={toggleAll} values={{
/> counter: maybe(() => params.ids.length),
<ActionDialog displayQuantity: (
confirmButtonState={ <strong>{maybe(() => params.ids.length)}</strong>
removeDialogTransitionState )
} }}
onClose={closeModal} />
onConfirm={() => </DialogContentText>
deleteCategory({ variables: { id } }) <DialogContentText>
} <FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." />
open={params.action === "delete"} </DialogContentText>
title={intl.formatMessage({ </ActionDialog>
defaultMessage: "Delete category", <ActionDialog
description: "dialog title" open={params.action === "delete-products"}
})} confirmButtonState={productBulkDeleteMutationState}
variant="delete" onClose={closeModal}
> onConfirm={() =>
<DialogContentText> productBulkDelete({
<FormattedMessage variables: { ids: params.ids }
defaultMessage="Are you sure you want to delete {categoryName}?" }).then(() => refetch())
values={{ }
categoryName: ( title={intl.formatMessage({
<strong> defaultMessage: "Delete products",
{maybe( description: "dialog title"
() => data.category.name, })}
"..." variant="delete"
)} >
</strong> <DialogContentText>
) <FormattedMessage
}} defaultMessage="Are you sure you want to delete {counter,plural,one{this product} other{{displayQuantity} products}}?"
/> values={{
</DialogContentText> counter: maybe(() => params.ids.length),
<DialogContentText> displayQuantity: (
<FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." /> <strong>{maybe(() => params.ids.length)}</strong>
</DialogContentText> )
</ActionDialog> }}
<ActionDialog />
open={ </DialogContentText>
params.action === "delete-categories" && </ActionDialog>
maybe(() => params.ids.length > 0) </>
} );
confirmButtonState={ }}
categoryBulkDeleteMutationState </TypedProductBulkDeleteMutation>
} </>
onClose={closeModal} );
onConfirm={() => }}
categoryBulkDelete({ </TypedCategoryDetailsQuery>
variables: { ids: params.ids }
})
}
title={intl.formatMessage({
defaultMessage: "Delete categories",
description: "dialog title"
})}
variant="delete"
>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {counter,plural,one{this attribute} other{{displayQuantity} categories}}?"
values={{
counter: maybe(
() => params.ids.length
),
displayQuantity: (
<strong>
{maybe(() => params.ids.length)}
</strong>
)
}}
/>
</DialogContentText>
<DialogContentText>
<FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." />
</DialogContentText>
</ActionDialog>
<ActionDialog
open={params.action === "delete-products"}
confirmButtonState={
productBulkDeleteMutationState
}
onClose={closeModal}
onConfirm={() =>
productBulkDelete({
variables: { ids: params.ids }
})
}
title={intl.formatMessage({
defaultMessage: "Delete products",
description: "dialog title"
})}
variant="delete"
>
{" "}
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {counter,plural,one{this attribute} other{{displayQuantity} products}}?"
values={{
counter: maybe(
() => params.ids.length
),
displayQuantity: (
<strong>
{maybe(() => params.ids.length)}
</strong>
)
}}
/>
</DialogContentText>
</ActionDialog>
</>
);
}}
</TypedProductBulkDeleteMutation>
)}
</TypedCategoryBulkDeleteMutation>
</>
);
}}
</TypedCategoryDetailsQuery>
);
}}
</TypedCategoryUpdateMutation>
)}
</TypedCategoryDeleteMutation>
); );
}; };
export default CategoryDetails; export default CategoryDetails;

View file

@ -18,7 +18,7 @@ import usePaginator, {
import { getMutationState, maybe } from "@saleor/misc"; import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types"; import { ListViews } from "@saleor/types";
import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage"; import { CategoryListPage } from "../../components/CategoryListPage/CategoryListPage";
import { TypedCategoryBulkDeleteMutation } from "../../mutations"; import { useCategoryBulkDeleteMutation } from "../../mutations";
import { TypedRootCategoriesQuery } from "../../queries"; import { TypedRootCategoriesQuery } from "../../queries";
import { CategoryBulkDelete } from "../../types/CategoryBulkDelete"; import { CategoryBulkDelete } from "../../types/CategoryBulkDelete";
import { import {
@ -138,120 +138,118 @@ export const CategoryList: React.FC<CategoryListProps> = ({ params }) => {
reset(); reset();
} }
}; };
return (
<TypedCategoryBulkDeleteMutation
onCompleted={handleCategoryBulkDelete}
>
{(categoryBulkDelete, categoryBulkDeleteOpts) => {
const bulkDeleteState = getMutationState(
categoryBulkDeleteOpts.called,
categoryBulkDeleteOpts.loading,
maybe(
() => categoryBulkDeleteOpts.data.categoryBulkDelete.errors
)
);
return ( const [
<> categoryBulkDelete,
<CategoryListPage categoryBulkDeleteOpts
categories={maybe( ] = useCategoryBulkDeleteMutation({
() => data.categories.edges.map(edge => edge.node), onCompleted: handleCategoryBulkDelete
[] });
)}
currentTab={currentTab} const bulkDeleteState = getMutationState(
initialSearch={params.query || ""} categoryBulkDeleteOpts.called,
onSearchChange={query => changeFilterField({ query })} categoryBulkDeleteOpts.loading,
onAll={() => navigate(categoryListUrl())} maybe(() => categoryBulkDeleteOpts.data.categoryBulkDelete.errors)
onTabChange={handleTabChange} );
onTabDelete={() => openModal("delete-search")}
onTabSave={() => openModal("save-search")} return (
tabs={tabs.map(tab => tab.name)} <>
settings={settings} <CategoryListPage
onAdd={() => navigate(categoryAddUrl())} categories={maybe(
onRowClick={id => () => navigate(categoryUrl(id))} () => data.categories.edges.map(edge => edge.node),
disabled={loading} []
onNextPage={loadNextPage} )}
onPreviousPage={loadPreviousPage} currentTab={currentTab}
onUpdateListSettings={updateListSettings} initialSearch={params.query || ""}
pageInfo={pageInfo} onSearchChange={query => changeFilterField({ query })}
isChecked={isSelected} onAll={() => navigate(categoryListUrl())}
selected={listElements.length} onTabChange={handleTabChange}
toggle={toggle} onTabDelete={() => openModal("delete-search")}
toggleAll={toggleAll} onTabSave={() => openModal("save-search")}
toolbar={ tabs={tabs.map(tab => tab.name)}
<IconButton settings={settings}
color="primary" onAdd={() => navigate(categoryAddUrl())}
onClick={() => onRowClick={id => () => navigate(categoryUrl(id))}
navigate( disabled={loading}
categoryListUrl({ onNextPage={loadNextPage}
...params, onPreviousPage={loadPreviousPage}
action: "delete", onUpdateListSettings={updateListSettings}
ids: listElements pageInfo={pageInfo}
}) isChecked={isSelected}
) selected={listElements.length}
} toggle={toggle}
> toggleAll={toggleAll}
<DeleteIcon /> toolbar={
</IconButton> <IconButton
} color="primary"
/> onClick={() =>
<ActionDialog navigate(
confirmButtonState={bulkDeleteState} categoryListUrl({
onClose={() => ...params,
navigate( action: "delete",
categoryListUrl({ ids: listElements
...params,
action: undefined,
ids: undefined
})
)
}
onConfirm={() =>
categoryBulkDelete({
variables: {
ids: params.ids
}
}) })
} )
open={params.action === "delete"} }
title={intl.formatMessage({ >
defaultMessage: "Delete categories", <DeleteIcon />
description: "dialog title" </IconButton>
})} }
variant="delete" />
> <ActionDialog
<DialogContentText> confirmButtonState={bulkDeleteState}
<FormattedMessage onClose={() =>
defaultMessage="Are you sure you want to delete {counter,plural,one{this category} other{{displayQuantity} categories}}?" navigate(
values={{ categoryListUrl({
counter: maybe(() => params.ids.length), ...params,
displayQuantity: ( action: undefined,
<strong>{maybe(() => params.ids.length)}</strong> ids: undefined
) })
}} )
/> }
</DialogContentText> onConfirm={() =>
<DialogContentText> categoryBulkDelete({
<FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." /> variables: {
</DialogContentText> ids: params.ids
</ActionDialog> }
<SaveFilterTabDialog })
open={params.action === "save-search"} }
confirmButtonState="default" open={params.action === "delete"}
onClose={closeModal} title={intl.formatMessage({
onSubmit={handleTabSave} defaultMessage: "Delete categories",
/> description: "dialog title"
<DeleteFilterTabDialog })}
open={params.action === "delete-search"} variant="delete"
confirmButtonState="default" >
onClose={closeModal} <DialogContentText>
onSubmit={handleTabDelete} <FormattedMessage
tabName={maybe(() => tabs[currentTab - 1].name, "...")} defaultMessage="Are you sure you want to delete {counter,plural,one{this category} other{{displayQuantity} categories}}?"
/> values={{
</> counter: maybe(() => params.ids.length),
); displayQuantity: (
}} <strong>{maybe(() => params.ids.length)}</strong>
</TypedCategoryBulkDeleteMutation> )
}}
/>
</DialogContentText>
<DialogContentText>
<FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." />
</DialogContentText>
</ActionDialog>
<SaveFilterTabDialog
open={params.action === "save-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleTabSave}
/>
<DeleteFilterTabDialog
open={params.action === "delete-search"}
confirmButtonState="default"
onClose={closeModal}
onSubmit={handleTabDelete}
tabName={maybe(() => tabs[currentTab - 1].name, "...")}
/>
</>
); );
}} }}
</TypedRootCategoriesQuery> </TypedRootCategoriesQuery>

65
src/hooks/makeMutation.ts Normal file
View file

@ -0,0 +1,65 @@
import { ApolloError } from "apollo-client";
import { DocumentNode } from "graphql";
import {
MutationFunction,
MutationResult,
useMutation as useBaseMutation
} from "react-apollo";
import { useIntl } from "react-intl";
import { commonMessages } from "@saleor/intl";
import { maybe } from "@saleor/misc";
import useNotifier from "./useNotifier";
type UseMutation<TData, TVariables> = [
MutationFunction<TData, TVariables>,
MutationResult<TData>
];
type UseMutationCbs<TData> = Partial<{
onCompleted: (data: TData) => void;
onError: (error: ApolloError) => void;
}>;
type UseMutationHook<TData, TVariables> = (
cbs: UseMutationCbs<TData>
) => UseMutation<TData, TVariables>;
function makeMutation<TData, TVariables>(
mutation: DocumentNode
): UseMutationHook<TData, TVariables> {
function useMutation<TData, TVariables>({
onCompleted,
onError
}: UseMutationCbs<TData>): UseMutation<TData, TVariables> {
const notify = useNotifier();
const intl = useIntl();
const [mutateFn, result] = useBaseMutation(mutation, {
onCompleted,
onError: (err: ApolloError) => {
if (
maybe(
() =>
err.graphQLErrors[0].extensions.exception.code ===
"ReadOnlyException"
)
) {
notify({
text: intl.formatMessage(commonMessages.readOnly)
});
} else {
notify({
text: intl.formatMessage(commonMessages.somethingWentWrong)
});
}
if (onError) {
onError(err);
}
}
});
return [mutateFn, result];
}
return useMutation;
}
export default makeMutation;