From 6e2aa84b4ae1c55eb91f1ebfc510f3a3b88984ad Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Wed, 19 Jan 2022 21:46:37 +0300 Subject: [PATCH] feat: category deleting --- src/components/ConfirmDialog/index.ts | 3 ++ .../DeleteCategoryDialog.tsx | 47 +++++++++++++++++++ .../Dialogs/DeleteCategoryDialog/index.ts | 3 ++ .../CategoryTableList/CategoryTableList.tsx | 17 ++++++- .../CategoryTableList/CategoryTableRow.tsx | 4 +- .../queries/category/useDeleteCategory.ts | 34 ++++++++++++++ src/hooks/queries/index.ts | 2 + src/services/CategoryService.ts | 11 +++++ 8 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 src/components/ConfirmDialog/index.ts create mode 100644 src/components/Dialogs/DeleteCategoryDialog/DeleteCategoryDialog.tsx create mode 100644 src/components/Dialogs/DeleteCategoryDialog/index.ts create mode 100644 src/hooks/queries/category/useDeleteCategory.ts diff --git a/src/components/ConfirmDialog/index.ts b/src/components/ConfirmDialog/index.ts new file mode 100644 index 0000000..8e034e4 --- /dev/null +++ b/src/components/ConfirmDialog/index.ts @@ -0,0 +1,3 @@ +import ConfirmDialog from './ConfirmDialog'; + +export default ConfirmDialog; diff --git a/src/components/Dialogs/DeleteCategoryDialog/DeleteCategoryDialog.tsx b/src/components/Dialogs/DeleteCategoryDialog/DeleteCategoryDialog.tsx new file mode 100644 index 0000000..750cee7 --- /dev/null +++ b/src/components/Dialogs/DeleteCategoryDialog/DeleteCategoryDialog.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import ConfirmDialog from '@educt/components/ConfirmDialog'; +import { ICategory } from '@educt/interfaces'; +import useIsMountedRef from '@educt/hooks/useIsMountedRef'; +import { useDeleteCategory } from '@educt/hooks/queries'; + +type DeleteCategoryDialogPropsType = { + category: ICategory; + isOpen: boolean; + onClose: () => void; + onDeleted?: (category: ICategory) => void | undefined; +}; + +const DeleteCategoryDialog: React.FC = ({ onClose, onDeleted, isOpen, category }) => { + const { deleteCategory, isLoading } = useDeleteCategory(); + const isMountedRef = useIsMountedRef(); + const handleDelete = async (isConfirmed: boolean, id: string) => { + if (isConfirmed) { + try { + await deleteCategory(id); + onDeleted && onDeleted(category); + } catch (error) { + console.error(error); + } + } + + if (isMountedRef.current) { + /** + * Close dialog + */ + onClose(); + } + }; + + return ( + handleDelete(isConfirmed, category.id)} + title='Confirm deleting' + message={`The category with title "${category.title}" will be removed from the system. After deleting a category, the courses in it will also be deleted.`} + confirmMessage='Delete' + /> + ); +}; + +export default DeleteCategoryDialog; diff --git a/src/components/Dialogs/DeleteCategoryDialog/index.ts b/src/components/Dialogs/DeleteCategoryDialog/index.ts new file mode 100644 index 0000000..81e92bb --- /dev/null +++ b/src/components/Dialogs/DeleteCategoryDialog/index.ts @@ -0,0 +1,3 @@ +import DeleteCategoryDialog from './DeleteCategoryDialog'; + +export default DeleteCategoryDialog; diff --git a/src/components/Tables/CategoryTableList/CategoryTableList.tsx b/src/components/Tables/CategoryTableList/CategoryTableList.tsx index b07d5fe..dcd10a3 100644 --- a/src/components/Tables/CategoryTableList/CategoryTableList.tsx +++ b/src/components/Tables/CategoryTableList/CategoryTableList.tsx @@ -10,6 +10,7 @@ import { CategoryTableRowPropsType } from './CategoryTableRow'; */ import { useState } from 'react'; import AddCategoryModal from '@educt/components/Modals/AddCategoryModal'; +import DeleteCategoryDialog from '@educt/components/Dialogs/DeleteCategoryDialog'; type CategoryTableListPropsType = { render: React.FC; @@ -19,14 +20,18 @@ type CategoryTableListPropsType = { const CategoryTableList: React.FC = props => { const { render: Row, categories } = props; const [rows, setRows] = useState(categories); + const [deleting, setDeleting] = useState(null); const { onOpen: onOpenCategoryModal, onClose: onCloseCategoryModal, isOpen: isOpenCategoryModal } = useDisclosure(); + const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure(); const isEmptyRows = rows.length === 0; const handleAddCategory = (category: ICategory) => setRows(prev => [...prev, category]); - // TODO make delete query - const handleDelete = () => () => undefined; + const handleDelete = (category: ICategory) => () => { + setDeleting(category); + onOpenDeleteDialog(); + }; // TODO make edit query const handleEdit = () => () => undefined; @@ -38,6 +43,14 @@ const CategoryTableList: React.FC = props => { onClose={onCloseCategoryModal} onAdded={category => handleAddCategory(category)} /> + {deleting && ( + setRows(prev => prev.filter(category => category.id !== deleted.id))} + /> + )} diff --git a/src/components/Tables/CategoryTableList/CategoryTableRow.tsx b/src/components/Tables/CategoryTableList/CategoryTableRow.tsx index f48b1ad..be03599 100644 --- a/src/components/Tables/CategoryTableList/CategoryTableRow.tsx +++ b/src/components/Tables/CategoryTableList/CategoryTableRow.tsx @@ -7,7 +7,7 @@ export type CategoryTableRowPropsType = { category: ICategory; actions: { onEdit: (category: ICategory) => () => void; - onDelete: (id: string) => () => void; + onDelete: (category: ICategory) => () => void; }; }; @@ -18,7 +18,7 @@ const CategoryTableRow: React.FC = ({ category, actio - + ); }; diff --git a/src/hooks/queries/category/useDeleteCategory.ts b/src/hooks/queries/category/useDeleteCategory.ts new file mode 100644 index 0000000..6d657c5 --- /dev/null +++ b/src/hooks/queries/category/useDeleteCategory.ts @@ -0,0 +1,34 @@ +import axios from 'axios'; +import { CategoryServiceInstance } from '@educt/services'; +import { useErrorHandler } from 'react-error-boundary'; +import { ICategory } from '@educt/interfaces'; +import { useToast } from '@chakra-ui/react'; +import useAsync from '@educt/hooks/useAsync'; + +const useDeleteCategory = () => { + const toast = useToast(); + const handleError = useErrorHandler(); + + const query = async (id: string) => { + try { + const result = await CategoryServiceInstance.delete(id); + toast({ title: 'Category deleted.', status: 'info' }); + return result.data; + } catch (error) { + if (axios.isAxiosError(error)) { + if (error.response) { + console.error('Error: ', error); + } else { + handleError(error); + } + } + return Promise.reject(error); + } + }; + + const { execute: deleteCategory, ...state } = useAsync>(query); + + return { deleteCategory, ...state }; +}; + +export { useDeleteCategory }; diff --git a/src/hooks/queries/index.ts b/src/hooks/queries/index.ts index da9aace..6d59efd 100644 --- a/src/hooks/queries/index.ts +++ b/src/hooks/queries/index.ts @@ -34,6 +34,7 @@ import { useUpdateUserEmail } from './user/useUpdateUserEmail'; */ import { useFetchCategories } from './category/useFetchCategories'; import { useCreateCategory } from './category/useCreateCategory'; +import { useDeleteCategory } from './category/useDeleteCategory'; export { useLogin, @@ -52,5 +53,6 @@ export { useUpdateUser, useUpdateUserEmail, useCreateCategory, + useDeleteCategory, useFetchCategories, }; diff --git a/src/services/CategoryService.ts b/src/services/CategoryService.ts index d3f5204..df42242 100644 --- a/src/services/CategoryService.ts +++ b/src/services/CategoryService.ts @@ -29,6 +29,17 @@ class CategoryService { const result = await this.api.post('/v1/categories', data); return result.data; } + + /** + * Delete category by id + * + * @param id Category id + * @returns Deleted category + */ + public async delete(id: string): Promise> { + const result = await this.api.delete(`/v1/categories/${id}`); + return result.data; + } } export const CategoryServiceInstance = new CategoryService(ApiServiceInstance.api);