mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
refactor: update user contacts form
This commit is contained in:
parent
d997f99239
commit
b2541ff3e5
@ -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
|
||||||
|
|||||||
@ -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;
|
|
||||||
@ -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'>
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user