mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
refactor: user updating
This commit is contained in:
parent
ff88ad75a3
commit
6d72fdb1c3
@ -1,5 +1,6 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Flex, Box, Text } from '@chakra-ui/layout';
|
||||
import { Input } from '@chakra-ui/input';
|
||||
import { Button } from '@chakra-ui/button';
|
||||
import { MdAttachFile } from 'react-icons/md';
|
||||
import * as helpres from '@educt/helpers';
|
||||
@ -8,7 +9,6 @@ import * as helpres from '@educt/helpers';
|
||||
* Hooks
|
||||
*/
|
||||
import { useColorModeValue } from '@chakra-ui/color-mode';
|
||||
import { Input } from '@chakra-ui/input';
|
||||
|
||||
type FileSelectPropsType = {
|
||||
onChange: (file: File) => void;
|
||||
|
||||
3
src/components/FileSelect/index.ts
Normal file
3
src/components/FileSelect/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import FileSelect from './FileSelect';
|
||||
|
||||
export default FileSelect;
|
||||
@ -13,10 +13,11 @@ import {
|
||||
InputRightElement,
|
||||
Input,
|
||||
} from '@chakra-ui/react';
|
||||
import { MdAttachFile, MdCloudUpload } from 'react-icons/md';
|
||||
import { MdCloudUpload } from 'react-icons/md';
|
||||
import { SubmitHandler, useForm, useWatch, Controller } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import AsyncSelect from '@educt/components/AsyncSelect';
|
||||
import FileSelect from '@educt/components/FileSelect';
|
||||
|
||||
/**
|
||||
* Types
|
||||
@ -30,14 +31,12 @@ import { CourseStatusEnum, UserRoleEnum } from '@educt/enums';
|
||||
import { useHistory } from 'react-router';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import { useColorModeValue } from '@chakra-ui/color-mode';
|
||||
import useIsMountedRef from '@educt/hooks/useIsMountedRef';
|
||||
|
||||
/**
|
||||
* Schema
|
||||
*/
|
||||
import CreateCourseSchema from './CreateCourseForm.validator';
|
||||
import FileSelect from '@educt/components/FileSelect/FileSelect';
|
||||
|
||||
type CreateCourseInputType = {
|
||||
title: string;
|
||||
@ -154,7 +153,7 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
||||
<Box>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid templateColumns={{ md: '2.5fr 1fr' }} gap='5'>
|
||||
<FormControl id='title' isInvalid={!!errors.title}>
|
||||
<FormControl isRequired id='title' isInvalid={!!errors.title}>
|
||||
<FormLabel>Title</FormLabel>
|
||||
<InputGroup>
|
||||
<Input pr='60px' size='md' placeholder='Type course title' type='text' {...register('title')} />
|
||||
@ -186,7 +185,7 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
||||
</Text>
|
||||
</FormControl>
|
||||
|
||||
<FormControl id='category_id' gridColumn='1' isInvalid={!!errors.category_id}>
|
||||
<FormControl isRequired id='category_id' gridColumn='1' isInvalid={!!errors.category_id}>
|
||||
<FormLabel>Category</FormLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
@ -206,7 +205,13 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
||||
{errors.category_id?.message}
|
||||
</Text>
|
||||
</FormControl>
|
||||
<FormControl id='teacher_id' gridColumn={{ md: '2' }} gridRow={{ md: '1' }} isInvalid={!!errors.teacher_id}>
|
||||
<FormControl
|
||||
isRequired
|
||||
id='teacher_id'
|
||||
gridColumn={{ md: '2' }}
|
||||
gridRow={{ md: '1' }}
|
||||
isInvalid={!!errors.teacher_id}
|
||||
>
|
||||
<FormLabel>Teacher</FormLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
@ -226,7 +231,7 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
||||
{errors.teacher_id?.message}
|
||||
</Text>
|
||||
</FormControl>
|
||||
<FormControl id='description' gridColumn='1' isInvalid={!!errors.description}>
|
||||
<FormControl isRequired id='description' gridColumn='1' isInvalid={!!errors.description}>
|
||||
<FormLabel>Description</FormLabel>
|
||||
<Textarea
|
||||
resize='none'
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
FormHelperText,
|
||||
Input,
|
||||
Button,
|
||||
Stack,
|
||||
Select,
|
||||
Flex,
|
||||
Text,
|
||||
@ -38,6 +39,10 @@ import { useForm } from 'react-hook-form';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
|
||||
/**
|
||||
* Contexts
|
||||
*/
|
||||
import { UsersPageContext } from '@educt/contexts';
|
||||
|
||||
type CreateUserModalPropsType = {
|
||||
@ -128,52 +133,53 @@ const CreateUserModal: React.FC<CreateUserModalPropsType> = ({ me }) => {
|
||||
<ModalCloseButton />
|
||||
<Divider />
|
||||
<ModalBody pb={6}>
|
||||
<FormControl>
|
||||
<Stack spacing='4'>
|
||||
<FormControl isRequired>
|
||||
<FormLabel>First name</FormLabel>
|
||||
<Input
|
||||
placeholder='First name'
|
||||
{...register('first_name')}
|
||||
isInvalid={errors.first_name ? true : false}
|
||||
/>
|
||||
{errors.first_name ? (
|
||||
<FormHelperText color='red.500'>{errors.first_name.message}</FormHelperText>
|
||||
) : null}
|
||||
<Input placeholder='First name' {...register('first_name')} isInvalid={!!errors.first_name} />
|
||||
{errors.first_name && <FormHelperText color='red.500'>{errors.first_name.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
<FormControl mt={4}>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Last name</FormLabel>
|
||||
<Input placeholder='Last name' {...register('last_name')} isInvalid={errors.last_name ? true : false} />
|
||||
{errors.last_name ? <FormHelperText color='red.500'>{errors.last_name.message}</FormHelperText> : null}
|
||||
<Input placeholder='Last name' {...register('last_name')} isInvalid={!!errors.last_name} />
|
||||
{errors.last_name && <FormHelperText color='red.500'>{errors.last_name.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
<FormControl mt={4}>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Login</FormLabel>
|
||||
<Input placeholder='e.g student893' {...register('login')} isInvalid={errors.login ? true : false} />
|
||||
{errors.login ? <FormHelperText color='red.500'>{errors.login.message}</FormHelperText> : null}
|
||||
<Input placeholder='e.g student001' {...register('login')} isInvalid={!!errors.login} />
|
||||
{errors.login && <FormHelperText color='red.500'>{errors.login.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
<FormControl mt={4}>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<Input placeholder='example@email.com' {...register('email')} isInvalid={errors.email ? true : false} />
|
||||
{errors.email ? <FormHelperText color='red.500'>{errors.email.message}</FormHelperText> : null}
|
||||
<Input placeholder='example@email.com' {...register('email')} isInvalid={!!errors.email} />
|
||||
{errors.email && <FormHelperText color='red.500'>{errors.email.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
<FormControl mt={4}>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Role</FormLabel>
|
||||
<Select w='full' mr='2' {...register('role')} isInvalid={errors.role ? true : false}>
|
||||
<Select w='full' mr='2' {...register('role')} isInvalid={!!errors.role}>
|
||||
<option value={UserRoleEnum.STUDENT}>Student</option>
|
||||
{me.isAdmin ? (
|
||||
{me.isAdmin && (
|
||||
<>
|
||||
<option value={UserRoleEnum.TEACHER}>Teacher</option>
|
||||
<option value={UserRoleEnum.ADMIN}>Administrator</option>
|
||||
</>
|
||||
) : null}
|
||||
)}
|
||||
</Select>
|
||||
{errors.role ? <FormHelperText color='red.500'>{errors.role.message}</FormHelperText> : null}
|
||||
{errors.role && <FormHelperText color='red.500'>{errors.role.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
<FormControl mt={4}>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<Input placeholder='******' {...register('password')} isInvalid={errors.password ? true : false} />
|
||||
<Input placeholder='******' {...register('password')} isInvalid={!!errors.password} />
|
||||
<FormHelperText color={errors.password ? 'red' : 'gray.500'}>
|
||||
{errors.password ? errors.password.message : 'Must be at least 6 characters.'}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
|
||||
@ -15,16 +15,28 @@ import {
|
||||
Flex,
|
||||
Text,
|
||||
Divider,
|
||||
Select,
|
||||
Stack,
|
||||
} from '@chakra-ui/react';
|
||||
import { CheckIcon } from '@chakra-ui/icons';
|
||||
import { MdModeEdit } from 'react-icons/md';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import UpdateUserSchema from './EditUserForm.validator';
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
import { IMe } from '@educt/interfaces';
|
||||
import { UserRoleEnum } from '@educt/enums';
|
||||
import { SubmitHandler } from 'react-hook-form';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
*/
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
import useIsMountedRef from '@educt/hooks/useIsMountedRef';
|
||||
|
||||
/**
|
||||
* Contexts
|
||||
@ -32,28 +44,26 @@ import { UserRoleEnum } from '@educt/enums';
|
||||
import { UsersPageContext } from '@educt/contexts';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
* Schema
|
||||
*/
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
import useIsMountedRef from '@educt/hooks/useIsMountedRef';
|
||||
import UpdateUserSchema from './EditUserForm.validator';
|
||||
|
||||
type UpdateUserFormPropsType = {
|
||||
onClose: () => void;
|
||||
isOpen: boolean;
|
||||
me: IMe;
|
||||
};
|
||||
|
||||
type UpdateUserInputType = {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
login: string;
|
||||
login: string | null;
|
||||
email: string;
|
||||
role: UserRoleEnum;
|
||||
password: string;
|
||||
};
|
||||
type UpdateUserFormPropsType = {
|
||||
onClose: () => void;
|
||||
isOpen: boolean;
|
||||
password: string | null;
|
||||
};
|
||||
|
||||
const UpdateUserForm: React.FC<UpdateUserFormPropsType> = ({ onClose, isOpen }) => {
|
||||
const UpdateUserForm: React.FC<UpdateUserFormPropsType> = ({ onClose, isOpen, me }) => {
|
||||
const { userStore } = useRootStore();
|
||||
const { editingUser, setEditingUser } = useContext(UsersPageContext);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
@ -68,6 +78,9 @@ const UpdateUserForm: React.FC<UpdateUserFormPropsType> = ({ onClose, isOpen })
|
||||
first_name: editingUser && editingUser.first_name,
|
||||
last_name: editingUser && editingUser.last_name,
|
||||
email: editingUser && editingUser.email,
|
||||
role: editingUser && editingUser.roles[0].slug,
|
||||
login: null,
|
||||
password: null,
|
||||
},
|
||||
});
|
||||
const isMountedRef = useIsMountedRef();
|
||||
@ -120,29 +133,58 @@ const UpdateUserForm: React.FC<UpdateUserFormPropsType> = ({ onClose, isOpen })
|
||||
<ModalCloseButton />
|
||||
<Divider />
|
||||
<ModalBody pb={6}>
|
||||
<Stack spacing='4'>
|
||||
<FormControl isDisabled>
|
||||
<FormLabel>Identificator</FormLabel>
|
||||
<Input value={editingUser?.id} />
|
||||
</FormControl>
|
||||
<FormControl mt={4}>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel>New Login</FormLabel>
|
||||
<Input placeholder='e.g student001' {...register('login')} isInvalid={!!errors.login} />
|
||||
{errors.login && <FormHelperText color='red.500'>{errors.login.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>First name</FormLabel>
|
||||
<Input
|
||||
placeholder='First name'
|
||||
{...register('first_name')}
|
||||
isInvalid={errors.first_name ? true : false}
|
||||
/>
|
||||
<Input placeholder='First name' {...register('first_name')} isInvalid={!!errors.first_name} />
|
||||
{errors.first_name && <FormHelperText color='red.500'>{errors.first_name.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
<FormControl mt={4}>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Last name</FormLabel>
|
||||
<Input placeholder='Last name' {...register('last_name')} isInvalid={errors.last_name ? true : false} />
|
||||
<Input placeholder='Last name' {...register('last_name')} isInvalid={!!errors.last_name} />
|
||||
{errors.last_name && <FormHelperText color='red.500'>{errors.last_name.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
<FormControl mt={4}>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<Input placeholder='example@email.com' {...register('email')} isInvalid={errors.email ? true : false} />
|
||||
<Input placeholder='example@email.com' {...register('email')} isInvalid={!!errors.email} />
|
||||
{errors.email && <FormHelperText color='red.500'>{errors.email.message}</FormHelperText>}
|
||||
</FormControl>
|
||||
|
||||
<FormControl isRequired>
|
||||
<FormLabel>Role</FormLabel>
|
||||
<Select w='full' mr='2' {...register('role')} isInvalid={!!errors.role}>
|
||||
<option value={UserRoleEnum.STUDENT}>Student</option>
|
||||
{me.isAdmin && (
|
||||
<>
|
||||
<option value={UserRoleEnum.TEACHER}>Teacher</option>
|
||||
<option value={UserRoleEnum.ADMIN}>Administrator</option>
|
||||
</>
|
||||
)}
|
||||
</Select>
|
||||
{errors.role ? <FormHelperText color='red.500'>{errors.role.message}</FormHelperText> : null}
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel>New Password</FormLabel>
|
||||
<Input placeholder='******' {...register('password')} isInvalid={!!errors.password} />
|
||||
<FormHelperText color={errors.password ? 'red' : 'gray.500'}>
|
||||
{errors.password ? errors.password.message : 'Must be at least 6 characters.'}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
|
||||
@ -1,10 +1,23 @@
|
||||
import yup from '@educt/schema';
|
||||
// import { UserRoleEnum } from 'enums';
|
||||
import { UserRoleEnum } from '@educt/enums';
|
||||
|
||||
const UpdateUserSchema = yup.object().shape({
|
||||
first_name: yup.string().required('First field name is required'),
|
||||
last_name: yup.string().required('Last field name is required'),
|
||||
email: yup.string().email('Email is invalid').required('Email field is required'),
|
||||
role: yup
|
||||
.mixed<UserRoleEnum>()
|
||||
.required('Role field is required')
|
||||
.oneOf(Object.values(UserRoleEnum), `Available roles: ${Object.keys(UserRoleEnum).join(', ')}`),
|
||||
login: yup
|
||||
.string()
|
||||
.transform(val => (!val ? null : val))
|
||||
.nullable(true),
|
||||
password: yup
|
||||
.string()
|
||||
.min(6, 'Password must be at least 6 characters.')
|
||||
.transform(val => (!val ? null : val))
|
||||
.nullable(true),
|
||||
});
|
||||
|
||||
export default UpdateUserSchema;
|
||||
|
||||
@ -7,6 +7,7 @@ import { Button } from '@chakra-ui/button';
|
||||
/**
|
||||
* Components
|
||||
*/
|
||||
import LoadingList from '@educt/components/LoadingList';
|
||||
import EditUserForm from '../EditUserForm';
|
||||
import DeleteUserDialog from '../DeleteUserDialog';
|
||||
|
||||
@ -30,7 +31,6 @@ import { useLocation } from 'react-router';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import { useDisclosure } from '@chakra-ui/hooks';
|
||||
import LoadingList from '@educt/components/LoadingList';
|
||||
|
||||
type UserListPropsType = { render: React.FC<UserItemPropsType> };
|
||||
|
||||
@ -42,13 +42,13 @@ const UserList: React.FC<UserListPropsType> = ({ render: Item }) => {
|
||||
const handleError = useErrorHandler();
|
||||
const { onOpen: onOpenEditModal, onClose: onCloseEditModal, isOpen: isOpenEditModal } = useDisclosure();
|
||||
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
|
||||
const { users, pagination } = userStore;
|
||||
const { users, pagination, me } = userStore;
|
||||
|
||||
useEffect(() => {
|
||||
userStore.loadUsersData().catch(error => handleError(error));
|
||||
}, [handleError, userStore, location.search]);
|
||||
|
||||
if (pagination === undefined || users === null) return <LoadingList />;
|
||||
if (pagination === undefined || users === null || me === null) return <LoadingList />;
|
||||
|
||||
const pagesCount = Math.ceil(pagination.total / pagination.per_page);
|
||||
|
||||
@ -117,7 +117,7 @@ const UserList: React.FC<UserListPropsType> = ({ render: Item }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{editingUser && <EditUserForm onClose={onCloseEditModal} isOpen={isOpenEditModal} />}
|
||||
{editingUser && <EditUserForm me={me} onClose={onCloseEditModal} isOpen={isOpenEditModal} />}
|
||||
{deletingUser && <DeleteUserDialog onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />}
|
||||
<Box>
|
||||
<Flex mt='7' p='0 10px' fontWeight='bold' alignItems='center' justifyContent='space-between'>
|
||||
|
||||
@ -76,7 +76,7 @@ export default class UserService {
|
||||
* @returns Updated user
|
||||
*/
|
||||
public async update(id: string, data: UpdateUserParamsType): Promise<IApiRespose<IUser>> {
|
||||
const result = await this.api.patch(`v1/users/${id}`, data);
|
||||
const result = await this.api.patch(`v1/users/${id}`, helpers.removeEmptyValues(data));
|
||||
return result.data;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user