feat: pages for creating lesson

This commit is contained in:
Sergey Yarkov 2021-11-15 23:08:40 +03:00
parent 44e8fa3158
commit 320fe09249
20 changed files with 407 additions and 99 deletions

5
package-lock.json generated
View File

@ -1869,6 +1869,11 @@
"resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.2.1.tgz", "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.2.1.tgz",
"integrity": "sha512-hwURgfmP2apX3HQrB55V9DN47kuN3C6KlQvI5UIfJRibXma72C/JudcNt2r9dWjAdFMrcZoz1ivvtXMCkJ2aQA==" "integrity": "sha512-hwURgfmP2apX3HQrB55V9DN47kuN3C6KlQvI5UIfJRibXma72C/JudcNt2r9dWjAdFMrcZoz1ivvtXMCkJ2aQA=="
}, },
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",

View File

@ -19,6 +19,7 @@
"js-cookie": "^3.0.0", "js-cookie": "^3.0.0",
"mobx": "^6.3.2", "mobx": "^6.3.2",
"mobx-react": "^7.2.0", "mobx-react": "^7.2.0",
"moment": "^2.29.1",
"qs": "^6.10.1", "qs": "^6.10.1",
"react": "^17.0.0", "react": "^17.0.0",
"react-dom": "^17.0.0", "react-dom": "^17.0.0",

View File

@ -7,7 +7,7 @@ import { Controller, UseFormReturn } from 'react-hook-form';
/** /**
* Types * Types
*/ */
import { OptionType } from '@educt/types'; import type { OptionType } from '@educt/types';
import { UserRoleEnum } from '@educt/enums'; import { UserRoleEnum } from '@educt/enums';
/** /**
@ -93,6 +93,7 @@ const CourseForm: React.FC<CourseFormPropsType> = ({ onSubmit, buttonLabel, isLo
}; };
const watchTitle = watch('title'); const watchTitle = watch('title');
const watchDescription = watch('description');
return ( return (
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
@ -119,9 +120,14 @@ const CourseForm: React.FC<CourseFormPropsType> = ({ onSubmit, buttonLabel, isLo
<FormControl isRequired id='description' isInvalid={!!errors.description}> <FormControl isRequired id='description' isInvalid={!!errors.description}>
<FormLabel>Description</FormLabel> <FormLabel>Description</FormLabel>
<Textarea resize='none' minH='150px' placeholder='Course description...' {...register('description')} /> <Textarea resize='none' minH='150px' placeholder='Course description...' {...register('description')} />
<Text as='small' color='red.500'> <Flex justifyContent='space-between'>
{errors.description?.message} <Text as='small' color='red.500'>
</Text> {errors.description?.message}
</Text>
<Text as='small' color={!!errors.description || watchDescription?.length > 250 ? 'red.500' : 'gray.500'}>
{watchDescription?.length || 0}/250
</Text>
</Flex>
</FormControl> </FormControl>
<Flex justifyContent='space-between' flexDirection={{ base: 'column', lg: 'row' }}> <Flex justifyContent='space-between' flexDirection={{ base: 'column', lg: 'row' }}>

View File

@ -5,7 +5,10 @@ const SUPPORTED_FORMATS = ['image/jpg', 'image/jpeg', 'image/png', 'image/webp']
const CreateCourseSchema = yup.object().shape({ const CreateCourseSchema = yup.object().shape({
title: yup.string().required('Title field name is required').max(90, 'Title must be at most 90 characters'), title: yup.string().required('Title field name is required').max(90, 'Title must be at most 90 characters'),
description: yup.string().required('Description field name is required'), description: yup
.string()
.required('Description field name is required')
.max(250, 'Description must be at most 250 characters'),
image: yup image: yup
.mixed() .mixed()
.test('fileSize', 'File size is too large', (value: File) => { .test('fileSize', 'File size is too large', (value: File) => {

View File

@ -1,3 +1,6 @@
/**
* Types
*/
import { ICourse } from '@educt/interfaces'; import { ICourse } from '@educt/interfaces';
/** /**

View File

@ -0,0 +1,56 @@
// /**
// * Types
// */
// import { ILesson } from '@educt/interfaces';
// /**
// * Hooks
// */
// import { useEffect, useState } from 'react';
// import { useErrorHandler } from 'react-error-boundary';
// import useIsMountedRef from './useIsMountedRef';
// type LessonStateType = {
// data: ILesson | null;
// error: any;
// loading: boolean;
// fetched: boolean;
// };
// const useFetchLessonQuery = (id: string) => {
// const [state, setState] = useState<LessonStateType>({ data: null, error: null, loading: false, fetched: false });
// const isMountedRef = useIsMountedRef();
// const handleError = useErrorHandler();
// /**
// * Fetch course handler
// */
// useEffect(() => {
// setState(s => ({ ...s, loading: true }));
// lessonService
// .fetchById(id)
// .then(data => {
// if (isMountedRef.current) {
// setState(s => ({ ...s, data: data.data }));
// }
// })
// .catch(error => {
// if (error.response) {
// if (isMountedRef.current) {
// setState(s => ({ ...s, error }));
// }
// } else {
// handleError(error);
// }
// })
// .finally(() => {
// if (isMountedRef.current) {
// setState(s => ({ ...s, loading: false, fetched: true }));
// }
// });
// }, [id]);
// return { ...state };
// };
// export default useFetchLessonQuery;

View File

@ -23,10 +23,10 @@ export interface ICourse {
title: string; title: string;
description: string; description: string;
status: CourseStatusEnum; status: CourseStatusEnum;
teacher: Pick<IUser, 'id' | 'first_name' | 'last_name' | 'email'>; teacher: Pick<IUser, 'id' | 'first_name' | 'fullname' | 'last_name' | 'email'>;
category: ICategory; category: ICategory;
lessons: ILesson[]; lessons: ILesson[];
students: Pick<IUser, 'id' | 'first_name' | 'last_name' | 'email'>; students: Array<Pick<IUser, 'id' | 'first_name' | 'fullname' | 'last_name' | 'email'>>;
students_count: string; students_count: string;
likes_count: string; likes_count: string;
lessons_count: string; lessons_count: string;
@ -39,6 +39,8 @@ export interface ILesson {
course_id: string; course_id: string;
title: string; title: string;
description: string; description: string;
created_at: string;
updated_at: string;
} }
export interface ICategory { export interface ICategory {

View File

@ -133,7 +133,7 @@ const CourseItem: React.FC<CourseItemPropsType> = ({ course, onDelete, onSetStat
<Heading as='h2' fontSize='xl' mt='5px'> <Heading as='h2' fontSize='xl' mt='5px'>
{course.title} {course.title}
</Heading> </Heading>
<Text>{course.description}</Text> <Text mt='2'>{course.description}</Text>
<Flex mt='20px' flexWrap='wrap'> <Flex mt='20px' flexWrap='wrap'>
<Flex alignItems='center' mr='1'> <Flex alignItems='center' mr='1'>
<Icon as={MdVideoLibrary} mr='2' /> <Icon as={MdVideoLibrary} mr='2' />

View File

@ -0,0 +1,17 @@
import React from 'react';
import { Box, Heading } from '@chakra-ui/react';
import { IPageProps } from '@educt/interfaces';
/**
* Create Lesson page
*/
const CreateLessonPage: React.FC<IPageProps> = () => {
return (
<Box>
<Heading as='h1'>Create Lesson page</Heading>
</Box>
);
};
export default CreateLessonPage;

View File

@ -0,0 +1,84 @@
import React from 'react';
import moment from 'moment';
import { Flex, Box, Text, Icon, Button } from '@chakra-ui/react';
import { EditIcon, TimeIcon } from '@chakra-ui/icons';
import { MdLibraryBooks, MdPlayCircleOutline } from 'react-icons/md';
/**
* Types
*/
import type { ILesson } from '@educt/interfaces';
/**
* Hooks
*/
import { useHistory } from 'react-router';
import { useColorModeValue } from '@chakra-ui/color-mode';
export type LessonItemPropsType = {
lesson: ILesson;
};
const LessonItem: React.FC<LessonItemPropsType> = ({ lesson }) => {
const history = useHistory();
return (
<Box
key={lesson.title}
p='3'
borderBottomWidth='1px'
borderRadius='lg'
bg={useColorModeValue('gray.50', 'gray.700')}
>
<Box>
<Flex justifyContent='space-between'>
<Text lineHeight='1.4' fontSize='lg' fontWeight='medium'>
<Text as='small' fontSize='sm' color='gray.500' fontWeight='normal'>
{/* TODO: need to make lesson order */}
Lesson {lesson.id}
</Text>
<br />
{lesson.title}
</Text>
<Box>
<Text>Created {moment(lesson.created_at).format('ll')}</Text>{' '}
</Box>
</Flex>
<Box>
<Text>{lesson.description}</Text>
</Box>
</Box>
<Flex justifyContent='space-between' alignItems='center' mt='2'>
<Flex alignItems='center'>
<Icon as={TimeIcon} mr='2' />
<Text mr='4' color={useColorModeValue('gray.600', 'gray.400')}>
1:30:00
</Text>
<Icon as={MdLibraryBooks} mr='2' />
<Text color={useColorModeValue('gray.600', 'gray.400')}>4 materials</Text>
</Flex>
<Box>
<Button
onClick={() => history.push(`/lesson/${lesson.id}`)}
size='sm'
colorScheme='blue'
variant='outline'
leftIcon={<Icon as={MdPlayCircleOutline} w='4' h='4' />}
mr='2'
>
Visit page
</Button>
<Button
onClick={() => history.push(`/lessons/edit/${lesson.id}`)}
size='sm'
leftIcon={<Icon as={EditIcon} />}
>
Edit
</Button>
</Box>
</Flex>
</Box>
);
};
export default LessonItem;

View File

@ -0,0 +1,49 @@
import React from 'react';
import { Box, Stack, Text, Button } from '@chakra-ui/react';
/**
* Types
*/
import type { LessonItemPropsType } from './LessonItem';
import type { ICourse } from '@educt/interfaces';
/**
* Hooks
*/
import { useHistory } from 'react-router';
type LessonListPropsType = {
render: React.FC<LessonItemPropsType>;
course: Omit<ICourse, 'students_count' | 'likes_count' | 'lessons_count'>;
};
const LessonList: React.FC<LessonListPropsType> = ({ render: Item, course }) => {
const history = useHistory();
return (
<Box>
{course.lessons.length !== 0 ? (
<Stack mt='4'>
{course.lessons.map(lesson => (
<Item key={lesson.id} lesson={lesson} />
))}
</Stack>
) : (
<Box textAlign='center' mt='10'>
<Text>No lessons have been added to this course yet</Text>
<Button
onClick={() => history.push(`${course.id}/create-lesson`)}
mt='3'
size='sm'
colorScheme='blue'
variant='outline'
>
Create new lesson
</Button>
</Box>
)}
</Box>
);
};
export default LessonList;

View File

@ -0,0 +1,4 @@
import LessonList from './LessonList';
import LessonItem from './LessonItem';
export { LessonList, LessonItem };

View File

@ -0,0 +1,16 @@
import React from 'react';
/**
* Types
*/
import { IUser } from '@educt/interfaces';
export type StudentItemPropsType = {
student: Pick<IUser, 'id' | 'first_name' | 'fullname' | 'last_name' | 'email'>;
};
const StudentItem: React.FC<StudentItemPropsType> = ({ student }) => {
return <div>{student.fullname}</div>;
};
export default StudentItem;

View File

@ -0,0 +1,36 @@
import React from 'react';
import { Stack, Box, Text, Button } from '@chakra-ui/react';
/**
* Types
*/
import { StudentItemPropsType } from './StudentItem';
import { ICourse } from '@educt/interfaces';
type StudentListPropsType = {
render: React.FC<StudentItemPropsType>;
students: ICourse['students'];
};
const StudentList: React.FC<StudentListPropsType> = ({ render: Item, students }) => {
return (
<Box>
{students.length !== 0 ? (
<Stack>
{students.map(student => (
<Item key={student.id} student={student} />
))}
</Stack>
) : (
<Box textAlign='center' mt='10'>
<Text>There are no students in the course yet</Text>
<Button mt='3' size='sm' colorScheme='blue' variant='outline'>
Add student
</Button>
</Box>
)}
</Box>
);
};
export default StudentList;

View File

@ -0,0 +1,4 @@
import StudentList from './StudentList';
import StudentItem from './StudentItem';
export { StudentList, StudentItem };

View File

@ -1,24 +1,26 @@
import React from 'react'; import React from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { Redirect } from 'react-router-dom'; import { Redirect } from 'react-router-dom';
import { Box, Heading, Text, Tab, TabList, Tabs, TabPanels, TabPanel, Button } from '@chakra-ui/react'; import { Box, Heading, Text, Tab, TabList, Flex, Tabs, TabPanels, TabPanel } from '@chakra-ui/react';
/** /**
* Types * Types
*/ */
import { IPageProps } from '@educt/interfaces'; import type { IPageProps } from '@educt/interfaces';
/** /**
* Components * Components
*/ */
import LoadingPage from '@educt/components/LoadingPage'; import LoadingPage from '@educt/components/LoadingPage';
import { EditCourseForm } from '@educt/components/Forms/CourseForm'; import { EditCourseForm } from '@educt/components/Forms/CourseForm';
import { LessonList, LessonItem } from './components/LessonList';
import { StudentList, StudentItem } from './components/StudentList';
/** /**
* Hooks * Hooks
*/ */
import useFetchCourseQuery from '@educt/hooks/useFetchCourseQuery';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import useFetchCourseQuery from '@educt/hooks/useFetchCourseQuery';
/** /**
* Course editor * Course editor
@ -42,10 +44,13 @@ const EditCoursePage: React.FC<IPageProps> = () => {
<Helmet> <Helmet>
<title>Editing {course.title}</title> <title>Editing {course.title}</title>
</Helmet> </Helmet>
<Box> <Flex>
<Heading as='h1'>Course editor</Heading> <Box>
<Text mt='2'>{course.title}</Text> <Heading as='h1'>Course editor</Heading>
</Box> <Text mt='2'>{course.title}</Text>
</Box>
<Box></Box>
</Flex>
<Box> <Box>
<Tabs isLazy mt='8'> <Tabs isLazy mt='8'>
<TabList> <TabList>
@ -56,27 +61,24 @@ const EditCoursePage: React.FC<IPageProps> = () => {
<TabPanels> <TabPanels>
<TabPanel> <TabPanel>
<EditCourseForm <Box>
defaultValues={{ <EditCourseForm
title: course.title, defaultValues={{
description: course.description, title: course.title,
category_id: course.category.id, description: course.description,
teacher_id: course.teacher.id, category_id: course.category.id,
image: undefined, teacher_id: course.teacher.id,
}} image: undefined,
/> }}
/>
</Box>
</TabPanel> </TabPanel>
<TabPanel> <TabPanel>
{course.lessons.length === 0 ? ( <LessonList render={LessonItem} course={course} />
<Box textAlign='center' mt='10'> </TabPanel>
<Text>No lessons have been added to this course yet</Text> <TabPanel>
<Button mt='3' size='sm' colorScheme='blue' variant='outline'> <StudentList render={StudentItem} students={course.students} />
Create new lesson
</Button>
</Box>
) : null}
</TabPanel> </TabPanel>
<TabPanel></TabPanel>
</TabPanels> </TabPanels>
</Tabs> </Tabs>
</Box> </Box>

View File

@ -0,0 +1,27 @@
import React from 'react';
import { Box, Heading } from '@chakra-ui/react';
/**
* Types
*/
import { IPageProps } from '@educt/interfaces';
/**
* Hooks
*/
import { useParams } from 'react-router-dom';
/**
* Lesson Page
*/
const LessonPage: React.FC<IPageProps> = () => {
const { id } = useParams<{ id: string }>();
return (
<Box>
<Heading as='h1'>Lesson {id}</Heading>
</Box>
);
};
export default LessonPage;

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import moment from 'moment';
import { Link as ReactRouterLink } from 'react-router-dom'; import { Link as ReactRouterLink } from 'react-router-dom';
import { Tag, TagLabel, LinkBox, Heading, LinkOverlay, Icon, Link } from '@chakra-ui/react'; import { Tag, TagLabel, LinkBox, Text, LinkOverlay, Icon, Link } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/layout'; import { Box, Flex } from '@chakra-ui/layout';
/** /**
@ -12,81 +13,54 @@ import { TimeIcon } from '@chakra-ui/icons';
/** /**
* Hooks * Hooks
*/ */
import { useColorMode } from '@chakra-ui/react'; import { useColorModeValue } from '@chakra-ui/color-mode';
type CourseItemPropsType = { type CourseItemPropsType = {
course: UserCourseType; course: UserCourseType;
}; };
const CourseItem: React.FC<CourseItemPropsType> = ({ course }) => { const CourseItem: React.FC<CourseItemPropsType> = ({ course }) => {
const { colorMode } = useColorMode();
return ( return (
<Box> <LinkBox as='div' p='3' borderBottomWidth='1px' borderRadius='lg' bg={useColorModeValue('gray.50', 'gray.700')}>
<LinkBox as='div' p='5' borderWidth='1px' rounded='lg'> <Flex justifyContent='space-between'>
<Flex mb='4' justifyContent='space-between'> <Box>
<Box> <Tag variant='outline' borderRadius='full' alignSelf='flex-start'>
<Tag variant='outline' borderRadius='full' alignSelf='flex-start'> <TagLabel>{course.lessons_count} lessons</TagLabel>
<TagLabel>{course.lessons_count} lessons</TagLabel> </Tag>
</Tag> </Box>
</Box>
<Box> <Box>
<Tag variant='subtle' colorScheme='green' borderRadius='full' alignSelf='flex-start'> <Tag variant='subtle' colorScheme='green' borderRadius='full' alignSelf='flex-start'>
<TagLabel>{course.category.title}</TagLabel> <TagLabel>{course.category.title}</TagLabel>
</Tag> </Tag>
</Box> </Box>
</Flex> </Flex>
<Heading size='md' my='2'> <Flex justifyContent='space-between' mt='2'>
<LinkOverlay as={ReactRouterLink} to={`/course/${course.id}`} overflowWrap='anywhere'> <Text>
<LinkOverlay
as={ReactRouterLink}
fontSize='xl'
fontWeight='medium'
to={`/course/${course.id}`}
overflowWrap='anywhere'
>
{course.title} {course.title}
</LinkOverlay> </LinkOverlay>
</Heading> </Text>
<Flex mt='6' alignItems='center'> </Flex>
<Icon as={TimeIcon} w='14px' h='14px' />
<Box
as='time'
color={colorMode === 'dark' ? 'gray.400' : 'gray.600'}
ml='2'
verticalAlign='middle'
dateTime={course.created_at}
>
Last update {new Date(course.created_at).toLocaleDateString()}
</Box>
</Flex>
</LinkBox>
</Box>
// <Flex flexDir='column' borderWidth='1px' borderRadius='lg' p='10px' boxShadow='sm' mb='3'> <Flex mt='2' alignItems='center' sx={{ gap: '9px' }}>
// <Tag colorScheme='purple' borderRadius='full' alignSelf='flex-start'> <Icon as={TimeIcon} w='14px' h='14px' />
// <TagLabel>{course.category.title}</TagLabel> <Box
// </Tag> as='time'
// <Flex justifyContent='space-between' alignItems='center'> verticalAlign='middle'
// <Link dateTime={course.created_at}
// as={ReactRouterLink} color={useColorModeValue('gray.600', 'gray.400')}
// to={`course/${course.id}`} >
// overflowWrap='anywhere' Last update {moment(course.created_at).format('ll')}
// fontWeight='bold' </Box>
// fontSize='xl' </Flex>
// mr='5' </LinkBox>
// >
// {course.title}
// </Link>
// </Flex>
// <Text>{course.description}</Text>
// <Flex mt='5' alignItems='center'>
// <MdVideoLibrary />
// <Text pl='1' pr='4'>
// {course.lessons_count}&nbsp;lessons
// </Text>
// <MdGroup />
// <Text pl='1' pr='4'>
// {course.students_count}&nbsp;students
// </Text>
// <MdThumbUp />
// <Text pl='1' pr='4'>
// {course.likes_count}&nbsp;likes
// </Text>
// </Flex>
// </Flex>
); );
}; };

View File

@ -69,6 +69,7 @@ const UserItem: React.FC<UserItemPropsType> = ({ user, onEdit, onDelete }) => {
</Flex> </Flex>
<Box> <Box>
<IconButton <IconButton
size='sm'
disabled={isDisabledActions} disabled={isDisabledActions}
onClick={() => onEdit(user)} onClick={() => onEdit(user)}
aria-label='Edit' aria-label='Edit'
@ -77,6 +78,7 @@ const UserItem: React.FC<UserItemPropsType> = ({ user, onEdit, onDelete }) => {
icon={<EditIcon />} icon={<EditIcon />}
/> />
<IconButton <IconButton
size='sm'
disabled={isDisabledActions} disabled={isDisabledActions}
onClick={() => onDelete(user)} onClick={() => onDelete(user)}
colorScheme='red' colorScheme='red'

View File

@ -7,12 +7,14 @@ import { IPrivateRouteProps } from './components/PrivateRoute/PrivateRoute';
import AuthPage from '@educt/pages/auth/auth'; import AuthPage from '@educt/pages/auth/auth';
import NotFoundPage from '@educt/pages/404/404'; import NotFoundPage from '@educt/pages/404/404';
import CreateCoursePage from '@educt/pages/courses/create/create'; import CreateCoursePage from '@educt/pages/courses/create/create';
import CreateLessonPage from '@educt/pages/courses/create-lesson/create-lesson';
import EditCoursePage from '@educt/pages/courses/edit/edit'; import EditCoursePage from '@educt/pages/courses/edit/edit';
import ChangePasswordPage from '@educt/pages/profile/change-password/change-password'; import ChangePasswordPage from '@educt/pages/profile/change-password/change-password';
import ChangeEmailPage from '@educt/pages/profile/change-email/change-email'; import ChangeEmailPage from '@educt/pages/profile/change-email/change-email';
import { UserRoleEnum } from './enums'; import { UserRoleEnum } from './enums';
const CoursesPage = lazy(() => import('@educt/pages/courses/courses')); const CoursesPage = lazy(() => import('@educt/pages/courses/courses'));
const LessonsPage = lazy(() => import('@educt/pages/lessons/lessons')); const LessonsPage = lazy(() => import('@educt/pages/lessons/lessons'));
const LessonPage = lazy(() => import('@educt/pages/lesson/lesson'));
const MainPage = lazy(() => import('@educt/pages/main/main')); const MainPage = lazy(() => import('@educt/pages/main/main'));
const MessagesPage = lazy(() => import('@educt/pages/messages/messages')); const MessagesPage = lazy(() => import('@educt/pages/messages/messages'));
const ProfilePage = lazy(() => import('@educt/pages/profile/profile')); const ProfilePage = lazy(() => import('@educt/pages/profile/profile'));
@ -52,6 +54,14 @@ export const routes: IRouteOption[] = [
exact: true, exact: true,
roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER], roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
}, },
{
path: '/courses/edit/:id/create-lesson',
title: 'Create Lesson',
component: CreateLessonPage,
isPrivate: true,
exact: true,
roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
},
{ {
path: '/lessons', path: '/lessons',
title: 'Lessons', title: 'Lessons',
@ -60,6 +70,13 @@ export const routes: IRouteOption[] = [
exact: true, exact: true,
roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER], roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
}, },
{
path: '/lesson/:id',
title: 'Lesson',
component: LessonPage,
isPrivate: true,
exact: true,
},
{ {
path: '/messages', path: '/messages',
title: 'Messages', title: 'Messages',