refactor: update user contacts form

This commit is contained in:
Sergey Yarkov 2021-10-23 15:32:50 +03:00
parent d997f99239
commit b2541ff3e5
4 changed files with 87 additions and 102 deletions

View File

@ -38,13 +38,13 @@ const App = () => {
<Router history={history}> <Router history={history}>
<Switch> <Switch>
<PrivateRoute exact path='/' component={MainPage} title='Home' /> <PrivateRoute exact path='/' component={MainPage} title='Home' />
<PrivateRoute exact path='/courses' component={CoursesPage} title='Courses' />
<PrivateRoute <PrivateRoute
path='/courses/create' path='/courses/create'
component={CreateCoursePage} component={CreateCoursePage}
title='Create new course' title='Create new course'
roles={[UserRoleEnum.ADMIN, UserRoleEnum.TEACHER]} roles={[UserRoleEnum.ADMIN, UserRoleEnum.TEACHER]}
/> />
<PrivateRoute path='/courses' component={CoursesPage} title='Courses' />
<PrivateRoute path='/messages' component={MessagesPage} title='Messages' /> <PrivateRoute path='/messages' component={MessagesPage} title='Messages' />
<PrivateRoute path='/profile' component={ProfilePage} title='Profile' /> <PrivateRoute path='/profile' component={ProfilePage} title='Profile' />
<PrivateRoute <PrivateRoute

View File

@ -1,65 +0,0 @@
import { useToast } from '@chakra-ui/react';
import { IApiRespose, IUserContacts } from '@educt/interfaces';
import { useState } from 'react';
import { useErrorHandler } from 'react-error-boundary';
import useIsMountedRef from './useIsMountedRef';
import { useRootStore } from './useRootStore';
type UpdateContactsStateType = {
result: IApiRespose<{}> | null;
error: any;
loading: boolean;
fetched: boolean;
};
const useUpdateUserContactsQuery = () => {
const isMountedRef = useIsMountedRef();
const [state, setState] = useState<UpdateContactsStateType>({
result: null,
error: null,
loading: false,
fetched: false,
});
const { userStore } = useRootStore();
const toast = useToast();
const handleError = useErrorHandler();
const updateContacts = async (data: IUserContacts) => {
try {
setState(s => ({ ...s, loading: true }));
const result = await userStore.updateCurrentUserContacts(data);
toast({ title: `Contacts saved!`, isClosable: true, status: 'info' });
if (isMountedRef.current) {
setState(s => ({ ...s, result }));
}
} catch (error: any) {
if (isMountedRef.current) {
setState(s => ({ ...s, error }));
}
if (error.response) {
/**
* Catch response error
*/
switch (error.response.status) {
case 422:
toast({ title: 'Validation error.', status: 'error', duration: 2000 });
break;
default:
handleError(error);
}
} else {
console.error(error);
handleError(error);
}
} finally {
if (isMountedRef.current) {
setState(s => ({ ...s, loading: false, fetched: true }));
}
}
};
return { ...state, updateContacts };
};
export default useUpdateUserContactsQuery;

View File

@ -31,7 +31,6 @@ import { useRootStore } from '@educt/hooks/useRootStore';
import CourseStatusBadge from '@educt/components/CourseStatusBadge'; import CourseStatusBadge from '@educt/components/CourseStatusBadge';
import { CourseStatusEnum } from '@educt/enums'; import { CourseStatusEnum } from '@educt/enums';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { API_BASE_URL } from '@educt/constants';
type CourseItemPropsType = { type CourseItemPropsType = {
course: Omit<ICourse, 'teacher' | 'students' | 'lessons'>; course: Omit<ICourse, 'teacher' | 'students' | 'lessons'>;
@ -63,16 +62,27 @@ const CourseItem: React.FC<CourseItemPropsType> = ({ course, onDelete, onSetStat
/> />
<MenuList> <MenuList>
<MenuItem icon={<EditIcon />}>Edit course</MenuItem> <MenuItem icon={<EditIcon />}>Edit course</MenuItem>
{course.status === CourseStatusEnum.DRAFT && ( {(() => {
<MenuItem onClick={() => onSetStatus(course.id, CourseStatusEnum.PUBLISHED)} icon={<CheckIcon />}> switch (course.status) {
case CourseStatusEnum.DRAFT:
return (
<MenuItem
onClick={() => onSetStatus(course.id, CourseStatusEnum.PUBLISHED)}
icon={<CheckIcon />}
>
Publish Publish
</MenuItem> </MenuItem>
)} );
{course.status === CourseStatusEnum.PUBLISHED && ( case CourseStatusEnum.PUBLISHED:
return (
<MenuItem onClick={() => onSetStatus(course.id, CourseStatusEnum.DRAFT)} icon={<MdNote />}> <MenuItem onClick={() => onSetStatus(course.id, CourseStatusEnum.DRAFT)} icon={<MdNote />}>
Mark as Draft Mark as Draft
</MenuItem> </MenuItem>
)} );
default:
return null;
}
})()}
<MenuItem <MenuItem
onClick={() => onDelete({ id: course.id, title: course.title })} onClick={() => onDelete({ id: course.id, title: course.title })}
icon={<DeleteIcon />} icon={<DeleteIcon />}
@ -101,6 +111,7 @@ const CourseItem: React.FC<CourseItemPropsType> = ({ course, onDelete, onSetStat
borderTopRightRadius='lg' borderTopRightRadius='lg'
p='10px' p='10px'
> >
{course.image && (
<Image <Image
position='absolute' position='absolute'
top='0' top='0'
@ -112,8 +123,9 @@ const CourseItem: React.FC<CourseItemPropsType> = ({ course, onDelete, onSetStat
w='100%' w='100%'
borderTopLeftRadius='lg' borderTopLeftRadius='lg'
borderTopRightRadius='lg' borderTopRightRadius='lg'
src={course.image ? `/api/assets/images/courses/${course.image.name}` : ''} src={`/api/assets/images/courses/${course.image.name}`}
/> />
)}
</Box> </Box>
<Box p='0 10px' mt='10px' pb='20px'> <Box p='0 10px' mt='10px' pb='20px'>
<Flex justifyContent='space-between'> <Flex justifyContent='space-between'>

View File

@ -1,13 +1,27 @@
import React from 'react'; import React, { useState } from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { SubmitHandler, useForm } from 'react-hook-form'; import { SubmitHandler, useForm } from 'react-hook-form';
import { MdSave } from 'react-icons/md'; import { MdSave } from 'react-icons/md';
import { IUserContacts } from '@educt/interfaces';
import { Box, Heading, Stack } from '@chakra-ui/layout';
import { Text, Input, Button } from '@chakra-ui/react'; import { Text, Input, Button } from '@chakra-ui/react';
import { Box, Heading, Stack } from '@chakra-ui/layout';
import { FormControl, FormHelperText } from '@chakra-ui/form-control'; import { FormControl, FormHelperText } from '@chakra-ui/form-control';
import { yupResolver } from '@hookform/resolvers/yup'; import { yupResolver } from '@hookform/resolvers/yup';
import useUpdateUserContactsQuery from '@educt/hooks/useUpdateUserContactsQuery';
/**
* Types
*/
import { IUserContacts } from '@educt/interfaces';
/**
* Hooks
*/
import { useToast } from '@chakra-ui/toast';
import { useRootStore } from '@educt/hooks/useRootStore';
import { useErrorHandler } from 'react-error-boundary';
/**
* Schema
*/
import UpdateUserContactsSchema from './UpdateUserContactsForm.validator'; import UpdateUserContactsSchema from './UpdateUserContactsForm.validator';
type UpdateUserContactsInputType = { type UpdateUserContactsInputType = {
@ -17,11 +31,11 @@ type UpdateUserContactsInputType = {
vk_id: string | null; vk_id: string | null;
}; };
type UpdateUserContactsFormProps = { type UpdateUserContactsFormPropsType = {
contacts: IUserContacts | null; contacts: IUserContacts | null;
}; };
const UpdateUserContactsForm: React.FC<UpdateUserContactsFormProps> = ({ contacts }) => { const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ contacts }) => {
const { const {
register, register,
formState: { errors, isDirty }, formState: { errors, isDirty },
@ -36,17 +50,41 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormProps> = ({ contact
vk_id: contacts && (contacts.vk_id ?? ''), vk_id: contacts && (contacts.vk_id ?? ''),
}, },
}); });
const { updateContacts, loading } = useUpdateUserContactsQuery(); const [isLoading, setIsLoading] = useState<boolean>(false);
const { userStore } = useRootStore();
const toast = useToast();
const handleError = useErrorHandler();
const onSubmit: SubmitHandler<UpdateUserContactsInputType> = data => { /**
updateContacts(data).finally(() => * Sumbit form handler
*/
const onSubmit: SubmitHandler<UpdateUserContactsInputType> = async data => {
try {
setIsLoading(true);
await userStore.updateCurrentUserContacts(data);
toast({ title: 'Contacts saved!', status: 'info' });
reset({ reset({
phone_number: data.phone_number ?? '', phone_number: data.phone_number ?? '',
twitter_id: data.twitter_id ?? '', twitter_id: data.twitter_id ?? '',
telegram_id: data.telegram_id ?? '', telegram_id: data.telegram_id ?? '',
vk_id: data.vk_id ?? '', vk_id: data.vk_id ?? '',
}) });
); setIsLoading(false);
} catch (error: any) {
if (error.response) {
switch (error.response.status) {
case 422:
toast({ title: error.response.data.errors[0].message, status: 'error', duration: 2000 });
break;
default:
handleError(error);
}
} else {
handleError(error);
}
} finally {
setIsLoading(false);
}
}; };
return ( return (
@ -121,7 +159,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormProps> = ({ contact
type='submit' type='submit'
size='md' size='md'
variant='outline' variant='outline'
isLoading={loading} isLoading={isLoading}
loadingText='Saving...' loadingText='Saving...'
rightIcon={<MdSave />} rightIcon={<MdSave />}
isDisabled={!isDirty} isDisabled={!isDirty}