refactor: creating page layout

This commit is contained in:
Sergey Yarkov 2021-11-09 23:58:00 +03:00
parent 2bcaad519e
commit cd2af7eae1
7 changed files with 121 additions and 94 deletions

View File

@ -1,5 +1,5 @@
import { UserRoleEnum } from '@educt/enums';
import { MdAccountCircle, MdCollectionsBookmark, MdGroup, MdHome, MdMessage } from 'react-icons/md';
import { MdAccountCircle, MdCollectionsBookmark, MdGroup, MdHome, MdMessage, MdList } from 'react-icons/md';
import { IAppConfig } from './interfaces';
import packageJson from '../package.json';
@ -18,6 +18,13 @@ const config: IAppConfig = {
links: [
{ location: '/', title: 'Main', public: true, icon: MdHome },
{ location: '/courses', title: 'Courses', public: true, icon: MdCollectionsBookmark },
{
location: '/lessons',
title: 'Lessons',
public: false,
icon: MdList,
roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
},
{ location: '/messages', title: 'Messages', public: true, icon: MdMessage },
{ location: '/profile', title: 'Profile', public: true, icon: MdAccountCircle },
{

View File

@ -5,7 +5,6 @@ import { Box, Grid, Text } from '@chakra-ui/react';
/**
* Types
*/
import { ICourse } from '@educt/interfaces';
import { CourseStatusEnum } from '@educt/enums';
import { CourseItemPropsType } from './CourseItem';

View File

@ -5,6 +5,7 @@ import {
useToast,
Text,
Flex,
Stack,
Button,
Textarea,
FormControl,
@ -12,8 +13,9 @@ import {
InputGroup,
InputRightElement,
Input,
FormHelperText,
Divider,
} from '@chakra-ui/react';
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';
@ -150,11 +152,11 @@ const CreateCourseForm: React.FC<CreateCourseFormPropsType> = () => {
return (
<Box>
<form onSubmit={handleSubmit(onSubmit)}>
<Grid templateColumns={{ md: '2.5fr 1fr' }} gap='5'>
<Stack spacing='5'>
<FormControl isRequired id='title' isInvalid={!!errors.title}>
<FormLabel>Title</FormLabel>
<FormLabel>Name</FormLabel>
<InputGroup>
<Input pr='60px' size='md' placeholder='Type course title' type='text' {...register('title')} />
<Input pr='60px' size='md' placeholder='Course name' type='text' {...register('title')} />
<InputRightElement
mr='2'
children={
@ -164,107 +166,98 @@ const CreateCourseForm: React.FC<CreateCourseFormPropsType> = () => {
}
/>
</InputGroup>
<FormHelperText>e.g Web Development in Javascript</FormHelperText>
<Text as='small' color='red.500'>
{errors.title?.message}
</Text>
</FormControl>
<FormControl id='image' isInvalid={!!errors.image}>
<FormLabel>Image</FormLabel>
<Controller
control={control}
name='image'
render={({ field: { onChange } }) => (
<FileSelect onChange={file => onChange(file)} supportedFormats={['JPG', 'JPEG', 'PNG', 'WEBP']} />
)}
/>
<Text as='small' color='red.500'>
{errors.image?.message}
</Text>
</FormControl>
<FormControl isRequired id='category_id' gridColumn='1' isInvalid={!!errors.category_id}>
<FormLabel>Category</FormLabel>
<Controller
control={control}
name='category_id'
render={({ field: { onChange, value } }) => (
<AsyncSelect
loadOptions={loadCategoriesOptions}
onChange={selected => onChange(selected)}
value={value}
placeholder='Select Category'
noOptionsMessage='There are no categories.'
loadingText='Wait...'
/>
)}
/>
<Text as='small' color='red.500'>
{errors.category_id?.message}
</Text>
</FormControl>
<FormControl
isRequired
id='teacher_id'
gridColumn={{ md: '2' }}
gridRow={{ md: '1' }}
isInvalid={!!errors.teacher_id}
>
<FormLabel>Teacher</FormLabel>
<Controller
control={control}
name='teacher_id'
render={({ field: { onChange, value } }) => (
<AsyncSelect
loadOptions={loadUsersOptions}
onChange={selected => onChange(selected)}
value={value}
placeholder='Select Teacher'
noOptionsMessage='There are no teachers.'
loadingText='Wait...'
/>
)}
/>
<Text as='small' color='red.500'>
{errors.teacher_id?.message}
</Text>
</FormControl>
<FormControl isRequired id='description' gridColumn='1' isInvalid={!!errors.description}>
<FormControl isRequired id='description' isInvalid={!!errors.description}>
<FormLabel>Description</FormLabel>
<Textarea
resize='none'
minH='150px'
placeholder='Write some description for course...'
{...register('description')}
/>
<Textarea resize='none' minH='150px' placeholder='Course description...' {...register('description')} />
<Text as='small' color='red.500'>
{errors.description?.message}
</Text>
</FormControl>
</Grid>
<Flex mt='6'>
<Flex justifyContent='space-between' flexDirection={{ base: 'column', lg: 'row' }}>
<Box flexGrow={1} flexBasis='0' mr={{ base: '0', lg: '7' }}>
<Stack spacing='5'>
<FormControl isRequired id='category_id' isInvalid={!!errors.category_id}>
<FormLabel>Category</FormLabel>
<Controller
control={control}
name='category_id'
render={({ field: { onChange, value } }) => (
<AsyncSelect
loadOptions={loadCategoriesOptions}
onChange={selected => onChange(selected)}
value={value}
placeholder='Select Category'
noOptionsMessage='There are no categories.'
loadingText='Wait...'
/>
)}
/>
<Text as='small' color='red.500'>
{errors.category_id?.message}
</Text>
</FormControl>
<FormControl isRequired id='teacher_id' isInvalid={!!errors.teacher_id}>
<FormLabel>Author</FormLabel>
<Controller
control={control}
name='teacher_id'
render={({ field: { onChange, value } }) => (
<AsyncSelect
loadOptions={loadUsersOptions}
onChange={selected => onChange(selected)}
value={value}
placeholder='Select Author'
noOptionsMessage='There are no teachers.'
loadingText='Wait...'
/>
)}
/>
<Text as='small' color='red.500'>
{errors.teacher_id?.message}
</Text>
</FormControl>
</Stack>
</Box>
<Box flexGrow={0} flexBasis='50%' mt={{ base: '5', lg: '0' }}>
<FormControl id='image' isInvalid={!!errors.image}>
<FormLabel>Image</FormLabel>
<Controller
control={control}
name='image'
render={({ field: { onChange } }) => (
<FileSelect onChange={file => onChange(file)} supportedFormats={['JPG', 'JPEG', 'PNG', 'WEBP']} />
)}
/>
<Text as='small' color='red.500'>
{errors.image?.message}
</Text>
</FormControl>
</Box>
</Flex>
</Stack>
<Divider mt='5' />
<Box mt='5'>
<Button
isLoading={isLoading}
loadingText='Saving...'
type='submit'
mr='2'
colorScheme='blue'
variant='outline'
rightIcon={<MdCloudUpload size='14px' />}
onClick={() => setStatus(CourseStatusEnum.DRAFT)}
>
Save as Draft
</Button>
<Button
loadingText='Saving...'
type='submit'
colorScheme='green'
onClick={() => setStatus(CourseStatusEnum.PUBLISHED)}
>
Publish
Create
</Button>
</Flex>
</Box>
</form>
</Box>
);

View File

@ -17,15 +17,15 @@ import CreateCourseForm from './components';
*/
const CreateCoursePage: React.FC<IPageProps> = () => {
return (
<Box maxW='1200px'>
<Box>
<Box>
<Flex alignItems='center'>
<PrevPageButton prevPage='/courses' />
<Heading as='h1'>Create course</Heading>
<Heading as='h1'>Course creation</Heading>
</Flex>
<Text mt='3'>Fill in the required fields to create a course</Text>
</Box>
<Box mt='6'>
<Box mt='5'>
<CreateCourseForm />
</Box>
</Box>

View File

@ -0,0 +1,18 @@
import React from 'react';
import { Box, Heading, Text } from '@chakra-ui/react';
import { IPageProps } from '@educt/interfaces';
/**
* Lessons page
*/
const LessonsPage: React.FC<IPageProps> = () => {
return (
<Box>
<Heading as='h1'>Lessons page</Heading>
<Text mt='2'>List of all created lessons in the system.</Text>
</Box>
);
};
export default LessonsPage;

View File

@ -37,7 +37,7 @@ const UsersPage: React.FC<IPageProps> = () => {
return (
<UsersPageContextProvider>
<Box h='100%'>
<Box>
<Heading as='h1'>User management</Heading>
<Text mt='2'>You can add or delete users on this page.</Text>
<Flex mt='5' flexDir='column' h='full'>

View File

@ -11,6 +11,7 @@ import ChangePasswordPage from '@educt/pages/profile/change-password/change-pass
import ChangeEmailPage from '@educt/pages/profile/change-email/change-email';
import { UserRoleEnum } from './enums';
const CoursesPage = lazy(() => import('@educt/pages/courses/courses'));
const LessonsPage = lazy(() => import('@educt/pages/lessons/lessons'));
const MainPage = lazy(() => import('@educt/pages/main/main'));
const MessagesPage = lazy(() => import('@educt/pages/messages/messages'));
const ProfilePage = lazy(() => import('@educt/pages/profile/profile'));
@ -40,6 +41,15 @@ export const routes: IRouteOption[] = [
title: 'Create new course',
component: CreateCoursePage,
isPrivate: true,
roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
},
{
path: '/lessons',
title: 'Lessons',
component: LessonsPage,
isPrivate: true,
exact: true,
roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
},
{
path: '/messages',