feat: create category

This commit is contained in:
Sergey Yarkov 2022-01-19 21:07:58 +03:00
parent 1cefa219a9
commit 94fa16de07
8 changed files with 163 additions and 2 deletions

View File

@ -0,0 +1,80 @@
import React from 'react';
import BaseModal from '@educt/components/Modal';
import { FormControl, FormHelperText, FormLabel, Input, Stack, Textarea } from '@chakra-ui/react';
import { ICategory } from '@educt/interfaces';
import { MdCategory } from 'react-icons/md';
import { yupResolver } from '@hookform/resolvers/yup';
/**
* Schema
*/
import AddCategorySchema from './AddCategoryModal.validator';
/**
* Hooks
*/
import { useForm } from 'react-hook-form';
import { useCreateCategory } from '@educt/hooks/queries';
type AddCategoryModalPropsType = {
isOpen: boolean;
onClose: () => void;
onAdded?: (category: ICategory) => void;
};
type AddCategoryInputType = {
title: string;
description: string;
};
const AddCategoryModal: React.FC<AddCategoryModalPropsType> = ({ isOpen, onClose, onAdded }) => {
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<AddCategoryInputType>({
resolver: yupResolver(AddCategorySchema),
});
const { createCategory, isLoading } = useCreateCategory();
const onSubmit = async (data: AddCategoryInputType) => {
try {
const category = await createCategory(data);
onClose();
onAdded && onAdded(category);
reset({});
} catch (error) {
console.error(error);
}
};
return (
<BaseModal
icon={MdCategory}
heading='Create category'
description='Create new category'
confirmText='Create'
isOpen={isOpen}
onClose={onClose}
onProceed={handleSubmit(onSubmit)}
isLoading={isLoading}
loadingText='Saving...'
>
<Stack spacing={'4'}>
<FormControl isRequired>
<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 AddCategoryModal;

View File

@ -0,0 +1,8 @@
import yup from '@educt/schema';
const AddCategorySchema = yup.object().shape({
title: yup.string().required('Title field name is required'),
description: yup.string().optional(),
});
export default AddCategorySchema;

View File

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

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Flex, Box, Text, Table, Tbody } from '@chakra-ui/react';
import { Flex, Box, Text, Table, Tbody, useDisclosure } from '@chakra-ui/react';
import { CreateButton } from '@educt/components/Buttons';
import { ICategory } from '@educt/interfaces';
import CategoryTableHead from './CategoryTableHead';
@ -9,6 +9,7 @@ import { CategoryTableRowPropsType } from './CategoryTableRow';
* Hooks
*/
import { useState } from 'react';
import AddCategoryModal from '@educt/components/Modals/AddCategoryModal';
type CategoryTableListPropsType = {
render: React.FC<CategoryTableRowPropsType>;
@ -18,9 +19,12 @@ type CategoryTableListPropsType = {
const CategoryTableList: React.FC<CategoryTableListPropsType> = props => {
const { render: Row, categories } = props;
const [rows, setRows] = useState<ICategory[]>(categories);
const { onOpen: onOpenCategoryModal, onClose: onCloseCategoryModal, isOpen: isOpenCategoryModal } = useDisclosure();
const isEmptyRows = rows.length === 0;
const handleAddCategory = (category: ICategory) => setRows(prev => [...prev, category]);
// TODO make delete query
const handleDelete = () => () => undefined;
@ -29,8 +33,13 @@ const CategoryTableList: React.FC<CategoryTableListPropsType> = props => {
return (
<Box>
<AddCategoryModal
isOpen={isOpenCategoryModal}
onClose={onCloseCategoryModal}
onAdded={category => handleAddCategory(category)}
/>
<Flex justifyContent={'flex-end'}>
<CreateButton />
<CreateButton onClick={onOpenCategoryModal} />
</Flex>
<Table borderRadius='lg' mt='2'>
<CategoryTableHead />

View File

@ -0,0 +1,43 @@
import axios from 'axios';
import { useToast } from '@chakra-ui/react';
import useAsync from '@educt/hooks/useAsync';
import { ICategory } from '@educt/interfaces';
import { CategoryServiceInstance } from '@educt/services';
import { CreateCategoryParamsType } from '@educt/types';
import { useErrorHandler } from 'react-error-boundary';
const useCreateCategory = () => {
const toast = useToast();
const handleError = useErrorHandler();
const create = async (data: CreateCategoryParamsType) => {
try {
const result = await CategoryServiceInstance.create(data);
return result.data;
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response) {
switch (error.response.status) {
case 422:
toast({ title: `${error.response.data.errors[0].message}`, status: 'error' });
break;
default:
toast({ title: `${error.message}`, status: 'error' });
break;
}
} else {
handleError(error);
}
} else {
handleError(error);
}
return Promise.reject(error);
}
};
const { execute: createCategory, ...state } = useAsync<ICategory, Parameters<typeof create>>(create);
return { createCategory, ...state };
};
export { useCreateCategory };

View File

@ -33,6 +33,7 @@ import { useUpdateUserEmail } from './user/useUpdateUserEmail';
* Category
*/
import { useFetchCategories } from './category/useFetchCategories';
import { useCreateCategory } from './category/useCreateCategory';
export {
useLogin,
@ -50,5 +51,6 @@ export {
useDeleteUser,
useUpdateUser,
useUpdateUserEmail,
useCreateCategory,
useFetchCategories,
};

View File

@ -1,6 +1,7 @@
import { AxiosInstance } from 'axios';
import { IApiRespose, ICategory } from '@educt/interfaces';
import { ApiServiceInstance } from '.';
import { CreateCategoryParamsType } from '@educt/types';
class CategoryService {
public api: AxiosInstance;
@ -18,6 +19,16 @@ class CategoryService {
const result = await this.api.get('/v1/categories');
return result.data;
}
/**
* Create category
*
* @returns Created category
*/
public async create(data: CreateCategoryParamsType): Promise<IApiRespose<ICategory>> {
const result = await this.api.post('/v1/categories', data);
return result.data;
}
}
export const CategoryServiceInstance = new CategoryService(ApiServiceInstance.api);

View File

@ -178,3 +178,8 @@ export type UpdateLessonParamsType = {
video?: File | undefined;
materials?: File[] | undefined | null;
};
export type CreateCategoryParamsType = {
title: string;
description?: string | undefined;
};