mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 02:09:08 +03:00
feat: create category
This commit is contained in:
parent
1cefa219a9
commit
94fa16de07
80
src/components/Modals/AddCategoryModal/AddCategoryModal.tsx
Normal file
80
src/components/Modals/AddCategoryModal/AddCategoryModal.tsx
Normal 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;
|
||||
@ -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;
|
||||
3
src/components/Modals/AddCategoryModal/index.ts
Normal file
3
src/components/Modals/AddCategoryModal/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import AddCategoryModal from './AddCategoryModal';
|
||||
|
||||
export default AddCategoryModal;
|
||||
@ -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 />
|
||||
|
||||
43
src/hooks/queries/category/useCreateCategory.ts
Normal file
43
src/hooks/queries/category/useCreateCategory.ts
Normal 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 };
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
5
src/types/index.d.ts
vendored
5
src/types/index.d.ts
vendored
@ -178,3 +178,8 @@ export type UpdateLessonParamsType = {
|
||||
video?: File | undefined;
|
||||
materials?: File[] | undefined | null;
|
||||
};
|
||||
|
||||
export type CreateCategoryParamsType = {
|
||||
title: string;
|
||||
description?: string | undefined;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user