feat: added category editing

This commit is contained in:
Sergey Yarkov 2022-01-19 23:55:38 +03:00
parent 6e2aa84b4a
commit c3fa9070f1
8 changed files with 198 additions and 13 deletions

View File

@ -0,0 +1,94 @@
import React from 'react';
import BaseModal from '@educt/components/Modal';
import { ICategory } from '@educt/interfaces';
import { MdModeEdit } from 'react-icons/md';
import { FormControl, FormLabel, FormHelperText, Stack, Input, Textarea } from '@chakra-ui/react';
import { UpdateCategoryParamsType } from '@educt/types';
import { yupResolver } from '@hookform/resolvers/yup';
/**
* Schema
*/
import EditCategorySchema from './EditCategoryModal.validator';
import { useUpdateCategory } from '@educt/hooks/queries';
/**
* Hooks
*/
import { useEffect, useMemo } from 'react';
import { useForm } from 'react-hook-form';
type EditCategoryModalPropsType = {
category: ICategory;
isOpen: boolean;
onClose: () => void;
onEdited?: (updatedCategory: ICategory) => void | undefined;
};
type UpdateCategoryInputType = {
title?: string | undefined;
description?: string | undefined;
};
const EditCategoryModal: React.FC<EditCategoryModalPropsType> = props => {
const { category, onClose, onEdited, isOpen } = props;
const defaultValues = {
title: category.title,
description: category.description,
};
const {
register,
handleSubmit,
reset,
formState: { errors, isDirty },
} = useForm<UpdateCategoryInputType>({
resolver: yupResolver(EditCategorySchema),
defaultValues: useMemo(() => {
return defaultValues;
}, [props]),
});
const { updateCategory, isLoading } = useUpdateCategory();
const onSubmit = async (data: UpdateCategoryParamsType) => {
try {
const updated = await updateCategory(category.id, data);
onClose();
onEdited && onEdited(updated);
} catch (error) {
console.error(error);
}
};
useEffect(() => {
reset(defaultValues);
}, [props.category]);
return (
<BaseModal
icon={MdModeEdit}
heading='Edit category'
confirmText='Save'
loadingText='Saving...'
isLoading={isLoading}
isOpen={isOpen}
isDisabled={!isDirty}
onClose={onClose}
onProceed={handleSubmit(onSubmit)}
>
<Stack spacing={'4'}>
<FormControl>
<FormLabel>Name</FormLabel>
<Input placeholder='e.g Management' {...register('title')} isInvalid={!!errors.title} />
{errors.title && <FormHelperText color='red.500'>{errors?.title.message}</FormHelperText>}
</FormControl>
<FormControl>
<FormLabel>Description</FormLabel>
<Textarea resize={'none'} placeholder='Write a description...' {...register('description')} />
</FormControl>
</Stack>
</BaseModal>
);
};
export default EditCategoryModal;

View File

@ -0,0 +1,8 @@
import yup from '@educt/schema';
const EditCategorySchema = yup.object().shape({
title: yup.string().optional(),
desctiption: yup.string().optional(),
});
export default EditCategorySchema;

View File

@ -0,0 +1,3 @@
import EditCategoryModal from './EditCategoryModal';
export default EditCategoryModal;

View File

@ -1,16 +1,18 @@
import React from 'react';
import { Flex, Box, Text, Table, Tbody, useDisclosure } from '@chakra-ui/react';
import { Flex, Box, Text, Table, Tbody } from '@chakra-ui/react';
import { CreateButton } from '@educt/components/Buttons';
import { ICategory } from '@educt/interfaces';
import CategoryTableHead from './CategoryTableHead';
import { CategoryTableRowPropsType } from './CategoryTableRow';
import AddCategoryModal from '@educt/components/Modals/AddCategoryModal';
import DeleteCategoryDialog from '@educt/components/Dialogs/DeleteCategoryDialog';
import EditCategoryModal from '@educt/components/Modals/EditCategoryModal';
/**
* Hooks
*/
import { useState } from 'react';
import AddCategoryModal from '@educt/components/Modals/AddCategoryModal';
import DeleteCategoryDialog from '@educt/components/Dialogs/DeleteCategoryDialog';
import { useDisclosure } from '@chakra-ui/react';
type CategoryTableListPropsType = {
render: React.FC<CategoryTableRowPropsType>;
@ -21,38 +23,56 @@ const CategoryTableList: React.FC<CategoryTableListPropsType> = props => {
const { render: Row, categories } = props;
const [rows, setRows] = useState<ICategory[]>(categories);
const [deleting, setDeleting] = useState<ICategory | null>(null);
const { onOpen: onOpenCategoryModal, onClose: onCloseCategoryModal, isOpen: isOpenCategoryModal } = useDisclosure();
const [editing, setEditing] = useState<ICategory | null>(null);
const { onOpen: onOpenCreateModal, onClose: onCloseCreateyModal, isOpen: isOpenCreateModal } = useDisclosure();
const { onOpen: onOpenEditModal, onClose: onCloseEditModal, isOpen: isOpenEditModal } = useDisclosure();
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
const isEmptyRows = rows.length === 0;
const handleAddCategory = (category: ICategory) => setRows(prev => [...prev, category]);
const handleAddRow = (category: ICategory) => setRows(prev => [...prev, category]);
const handleRemoveRow = (removed: ICategory) => setRows(prev => prev.filter(category => category.id !== removed.id));
const handleEditRow = (edited: ICategory) => {
console.log(edited);
};
const handleDelete = (category: ICategory) => () => {
setDeleting(category);
onOpenDeleteDialog();
};
// TODO make edit query
const handleEdit = () => () => undefined;
const handleEdit = (category: ICategory) => () => {
setEditing(category);
onOpenEditModal();
};
return (
<Box>
<AddCategoryModal
isOpen={isOpenCategoryModal}
onClose={onCloseCategoryModal}
onAdded={category => handleAddCategory(category)}
isOpen={isOpenCreateModal}
onClose={onCloseCreateyModal}
onAdded={category => handleAddRow(category)}
/>
{deleting && (
<DeleteCategoryDialog
category={deleting}
isOpen={isOpenDeleteDialog}
onClose={onCloseDeleteDialog}
onDeleted={deleted => setRows(prev => prev.filter(category => category.id !== deleted.id))}
onDeleted={category => handleRemoveRow(category)}
/>
)}
{editing && (
<EditCategoryModal
category={editing}
isOpen={isOpenEditModal}
onClose={onCloseEditModal}
onEdited={category => handleEditRow(category)}
/>
)}
<Flex justifyContent={'flex-end'}>
<CreateButton onClick={onOpenCategoryModal} />
<CreateButton onClick={onOpenCreateModal} />
</Flex>
<Table borderRadius='lg' mt='2'>
<CategoryTableHead />

View File

@ -0,0 +1,41 @@
import axios from 'axios';
import { ICategory } from '@educt/interfaces';
import { CategoryServiceInstance } from '@educt/services';
import { UpdateCategoryParamsType } from '@educt/types';
import useAsync from '@educt/hooks/useAsync';
import { useToast } from '@chakra-ui/react';
import { useErrorHandler } from 'react-error-boundary';
const useUpdateCategory = () => {
const toast = useToast();
const handleError = useErrorHandler();
const update = async (id: string, data: UpdateCategoryParamsType) => {
try {
const result = await CategoryServiceInstance.update(id, data);
return result.data;
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response) {
switch (error.response.status) {
default:
toast({ title: `${error.message}`, status: 'error' });
break;
}
} else {
handleError(error);
}
} else {
handleError(error);
}
return Promise.reject(error);
}
};
const { execute: updateCategory, ...state } = useAsync<ICategory, Parameters<typeof update>>(update);
return { updateCategory, ...state };
};
export { useUpdateCategory };

View File

@ -35,6 +35,7 @@ import { useUpdateUserEmail } from './user/useUpdateUserEmail';
import { useFetchCategories } from './category/useFetchCategories';
import { useCreateCategory } from './category/useCreateCategory';
import { useDeleteCategory } from './category/useDeleteCategory';
import { useUpdateCategory } from './category/useUpdateCategory';
export {
useLogin,
@ -54,5 +55,6 @@ export {
useUpdateUserEmail,
useCreateCategory,
useDeleteCategory,
useUpdateCategory,
useFetchCategories,
};

View File

@ -1,7 +1,7 @@
import { AxiosInstance } from 'axios';
import { IApiRespose, ICategory } from '@educt/interfaces';
import { ApiServiceInstance } from '.';
import { CreateCategoryParamsType } from '@educt/types';
import { CreateCategoryParamsType, UpdateCategoryParamsType } from '@educt/types';
class CategoryService {
public api: AxiosInstance;
@ -40,6 +40,18 @@ class CategoryService {
const result = await this.api.delete(`/v1/categories/${id}`);
return result.data;
}
/**
* Update category by id
*
* @param id Category id
* @param data Field to update
* @returns Updated category
*/
public async update(id: string, data: UpdateCategoryParamsType): Promise<IApiRespose<ICategory>> {
const reuslt = await this.api.patch(`/v1/categories/${id}`, data);
return reuslt.data;
}
}
export const CategoryServiceInstance = new CategoryService(ApiServiceInstance.api);

View File

@ -183,3 +183,8 @@ export type CreateCategoryParamsType = {
title: string;
description?: string | undefined;
};
export type UpdateCategoryParamsType = {
title?: string | undefined;
description?: string | undefined;
};