refactor: added routes config & refactor layout

This commit is contained in:
Sergey Yarkov 2021-10-31 20:34:13 +03:00
parent 27e1cdbfa5
commit c92163accb
19 changed files with 264 additions and 191 deletions

29
package-lock.json generated
View File

@ -984,17 +984,36 @@
"integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
}, },
"@emotion/react": { "@emotion/react": {
"version": "11.4.1", "version": "11.5.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.4.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.5.0.tgz",
"integrity": "sha512-pRegcsuGYj4FCdZN6j5vqCALkNytdrKw3TZMekTzNXixRg4wkLsU5QEaBG5LC6l01Vppxlp7FE3aTHpIG5phLg==", "integrity": "sha512-MYq/bzp3rYbee4EMBORCn4duPQfgpiEB5XzrZEBnUZAL80Qdfr7CEv/T80jwaTl/dnZmt9SnTa8NkTrwFNpLlw==",
"requires": { "requires": {
"@babel/runtime": "^7.13.10", "@babel/runtime": "^7.13.10",
"@emotion/cache": "^11.4.0", "@emotion/cache": "^11.5.0",
"@emotion/serialize": "^1.0.2", "@emotion/serialize": "^1.0.2",
"@emotion/sheet": "^1.0.2", "@emotion/sheet": "^1.0.3",
"@emotion/utils": "^1.0.0", "@emotion/utils": "^1.0.0",
"@emotion/weak-memoize": "^0.2.5", "@emotion/weak-memoize": "^0.2.5",
"hoist-non-react-statics": "^3.3.1" "hoist-non-react-statics": "^3.3.1"
},
"dependencies": {
"@emotion/cache": {
"version": "11.5.0",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.5.0.tgz",
"integrity": "sha512-mAZ5QRpLriBtaj/k2qyrXwck6yeoz1V5lMt/jfj6igWU35yYlNKs2LziXVgvH81gnJZ+9QQNGelSsnuoAy6uIw==",
"requires": {
"@emotion/memoize": "^0.7.4",
"@emotion/sheet": "^1.0.3",
"@emotion/utils": "^1.0.0",
"@emotion/weak-memoize": "^0.2.5",
"stylis": "^4.0.10"
}
},
"@emotion/sheet": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.3.tgz",
"integrity": "sha512-YoX5GyQ4db7LpbmXHMuc8kebtBGP6nZfRC5Z13OKJMixBEwdZrJ914D6yJv/P+ZH/YY3F5s89NYX2hlZAf3SRQ=="
}
} }
}, },
"@emotion/serialize": { "@emotion/serialize": {

View File

@ -9,10 +9,10 @@
"lint": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"" "lint": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\""
}, },
"dependencies": { "dependencies": {
"@chakra-ui/icons": "^1.0.15", "@chakra-ui/icons": "^1.0.16",
"@chakra-ui/react": "^1.0.0", "@chakra-ui/react": "^1.6.10",
"@emotion/react": "^11.0.0", "@emotion/react": "^11.5.0",
"@emotion/styled": "^11.0.0", "@emotion/styled": "^11.3.0",
"@hookform/resolvers": "^2.8.0", "@hookform/resolvers": "^2.8.0",
"axios": "^0.21.1", "axios": "^0.21.1",
"framer-motion": "^4.0.0", "framer-motion": "^4.0.0",

View File

@ -1,10 +1,6 @@
import React from 'react'; import React from 'react';
import { Router, Switch } from 'react-router-dom'; import { Router, Switch } from 'react-router-dom';
import { routes } from './routes.config';
/**
* Types
*/
import { UserRoleEnum } from '@educt/enums';
/** /**
* Hooks * Hooks
@ -17,20 +13,6 @@ import { useRootStore } from '@educt/hooks/useRootStore';
import PrivateRoute from '@educt/components/PrivateRoute'; import PrivateRoute from '@educt/components/PrivateRoute';
import PublicRoute from '@educt/components/PublicRoute'; import PublicRoute from '@educt/components/PublicRoute';
/**
* Pages
*/
import AuthPage from '@educt/pages/auth/auth';
import NotFoundPage from '@educt/pages/404/404';
import CreateCoursePage from '@educt/pages/courses/create/create';
import ChangePasswordPage from '@educt/pages/profile/change-password/change-password';
import ChangeEmailPage from '@educt/pages/profile/change-email/change-email';
const CoursesPage = React.lazy(() => import('@educt/pages/courses/courses'));
const MainPage = React.lazy(() => import('@educt/pages/main/main'));
const MessagesPage = React.lazy(() => import('@educt/pages/messages/messages'));
const ProfilePage = React.lazy(() => import('@educt/pages/profile/profile'));
const UsersPage = React.lazy(() => import('@educt/pages/users/users'));
const App = () => { const App = () => {
const { const {
uiStore: { history }, uiStore: { history },
@ -39,26 +21,10 @@ const App = () => {
return ( return (
<Router history={history}> <Router history={history}>
<Switch> <Switch>
<PrivateRoute exact path='/' component={MainPage} title='Home' /> {routes.map((routeProps, i) => {
<PrivateRoute exact path='/courses' component={CoursesPage} title='Courses' /> const { isPrivate, ...props } = routeProps;
<PrivateRoute return isPrivate ? <PrivateRoute key={i} {...props} /> : <PublicRoute key={i} {...props} />;
path='/courses/create' })}
component={CreateCoursePage}
title='Create new course'
roles={[UserRoleEnum.ADMIN, UserRoleEnum.TEACHER]}
/>
<PrivateRoute path='/messages' component={MessagesPage} title='Messages' />
<PrivateRoute exact path='/profile' component={ProfilePage} title='Profile' />
<PrivateRoute path='/profile/change-password' component={ChangePasswordPage} title='Change Password' />
<PrivateRoute path='/profile/change-email' component={ChangeEmailPage} title='Change Email' />
<PrivateRoute
path='/users'
component={UsersPage}
title='Users Management'
roles={[UserRoleEnum.ADMIN, UserRoleEnum.TEACHER]}
/>
<PublicRoute path='/auth' component={AuthPage} />
<PublicRoute component={NotFoundPage} />
</Switch> </Switch>
</Router> </Router>
); );

View File

@ -50,8 +50,8 @@ const Nav: React.FC = () => {
<NavList links={config.links} /> <NavList links={config.links} />
</Flex> </Flex>
<Box textAlign='center' mt='auto' mb='5'> <Box textAlign='center' mt='auto' mb='5'>
<Text fontSize='xs' color='gray.500' lineHeight='1.5'> <Text fontSize='xs' color='gray.500' lineHeight='1.6'>
Learning Management System <br /> Made by Sergey Yarkov Learning Management System <br /> Made by Sergey Yarkov <br />v{config.metaData.appVersion}
</Text> </Text>
<Box textAlign='center' mt='2'> <Box textAlign='center' mt='2'>
<IconButton variant='link' aria-label='GitHub repository'> <IconButton variant='link' aria-label='GitHub repository'>

View File

@ -3,16 +3,13 @@ import React from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import { Route, Redirect } from 'react-router-dom'; import { Route, Redirect } from 'react-router-dom';
import { RouteProps } from 'react-router';
import { IPageProps } from '@educt/interfaces';
import { UserRoleEnum } from '@educt/enums'; import { UserRoleEnum } from '@educt/enums';
import { useRootStore } from '@educt/hooks/useRootStore'; import { useRootStore } from '@educt/hooks/useRootStore';
import Layout from '@educt/components/Layout'; import Layout from '@educt/components/Layout';
import ErrorFallback from '@educt/components/ErrorFallback'; import ErrorFallback from '@educt/components/ErrorFallback';
import { IPublicRouteProps } from '../PublicRoute/PublicRoute';
export interface IPrivateRouteProps extends RouteProps { export interface IPrivateRouteProps extends IPublicRouteProps {
component: React.FC<IPageProps>;
title?: string;
/** /**
* This field means who can access the page * This field means who can access the page
*/ */

View File

@ -7,7 +7,7 @@ import ErrorFallback from '@educt/components/ErrorFallback';
export interface IPublicRouteProps extends RouteProps { export interface IPublicRouteProps extends RouteProps {
component: React.FC<IPageProps>; component: React.FC<IPageProps>;
title?: string; title: string;
} }
/** /**

View File

@ -1,6 +1,7 @@
import { UserRoleEnum } from '@educt/enums'; import { UserRoleEnum } from '@educt/enums';
import { MdAccountCircle, MdCollectionsBookmark, MdGroup, MdHome, MdMessage } from 'react-icons/md'; import { MdAccountCircle, MdCollectionsBookmark, MdGroup, MdHome, MdMessage } from 'react-icons/md';
import { IAppConfig } from './interfaces'; import { IAppConfig } from './interfaces';
import packageJson from '../package.json';
const config: IAppConfig = { const config: IAppConfig = {
/** /**
@ -8,7 +9,8 @@ const config: IAppConfig = {
*/ */
metaData: { metaData: {
appName: 'Educt', appName: 'Educt',
appDescription: 'Learn management system', appDescription: 'Learning management system',
appVersion: packageJson.version,
}, },
/** /**
* Links that are rendered in the navigation * Links that are rendered in the navigation

View File

@ -6,6 +6,7 @@ export interface IAppConfig {
metaData: { metaData: {
appName: string; appName: string;
appDescription: string; appDescription: string;
appVersion: string;
}; };
links: LinkType[]; links: LinkType[];
} }
@ -127,10 +128,5 @@ export interface IApiRespose<T> {
} }
export interface IPageProps extends RouteComponentProps { export interface IPageProps extends RouteComponentProps {
title?: string; title: string;
}
export interface IWindowDimensions {
width: number;
height: number;
} }

View File

@ -1,10 +1,15 @@
import React from 'react'; import React from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { MdError } from 'react-icons/md';
import { Flex, Box, Heading, Text, useToast, Link } from '@chakra-ui/react';
import { Link as ReactRouterLink } from 'react-router-dom'; import { Link as ReactRouterLink } from 'react-router-dom';
import { Flex, Box, Heading, Text, useToast, Link } from '@chakra-ui/react';
import { MdError } from 'react-icons/md';
const NotFoundPage: React.FC = () => { /**
* Types
*/
import { IPageProps } from '@educt/interfaces';
const NotFoundPage: React.FC<IPageProps> = ({ title }) => {
const toast = useToast(); const toast = useToast();
React.useEffect(() => { React.useEffect(() => {
@ -14,7 +19,7 @@ const NotFoundPage: React.FC = () => {
return ( return (
<> <>
<Helmet> <Helmet>
<title>Error 404</title> <title>{title}</title>
</Helmet> </Helmet>
<Box textAlign='center' mt={40}> <Box textAlign='center' mt={40}>
<Flex justifyContent='center'> <Flex justifyContent='center'>

View File

@ -11,7 +11,7 @@ import AuthForm from './components/AuthForm';
/** /**
* Auth page * Auth page
*/ */
const AuthPage: React.FC<IPageProps> = () => { const AuthPage: React.FC<IPageProps> = ({ title }) => {
const { authStore } = useRootStore(); const { authStore } = useRootStore();
/** /**
@ -24,7 +24,7 @@ const AuthPage: React.FC<IPageProps> = () => {
return ( return (
<> <>
<Helmet> <Helmet>
<title>Auth</title> <title>{title}</title>
</Helmet> </Helmet>
<Flex minHeight='100vh' align='center' justifyContent='center' padding='5'> <Flex minHeight='100vh' align='center' justifyContent='center' padding='5'>
<AuthForm /> <AuthForm />

View File

@ -44,7 +44,7 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
const toast = useToast(); const toast = useToast();
/** /**
* Fetch handler * Fetch courses handler
*/ */
useEffect(() => { useEffect(() => {
if (me !== null) { if (me !== null) {
@ -53,7 +53,7 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
status: me.isAdmin || me.isTeacher ? courseStatus : CourseStatusEnum.PUBLISHED, status: me.isAdmin || me.isTeacher ? courseStatus : CourseStatusEnum.PUBLISHED,
}); });
} }
}, [courseStore, selectedCategory, courseStatus, me]); }, [courseStore, selectedCategory, courseStatus]);
/** /**
* Handle delete course * Handle delete course

View File

@ -30,7 +30,7 @@ import { useHistory } from 'react-router';
/** /**
* Courses page * Courses page
*/ */
const CoursesPage: React.FC<IPageProps> = ({ title }) => { const CoursesPage: React.FC<IPageProps> = () => {
const { const {
userStore: { me }, userStore: { me },
categoryStore, categoryStore,
@ -46,7 +46,7 @@ const CoursesPage: React.FC<IPageProps> = ({ title }) => {
<Flex alignItems='center' justifyContent='space-between'> <Flex alignItems='center' justifyContent='space-between'>
<Box> <Box>
<Heading as='h1'>Courses</Heading> <Heading as='h1'>Courses</Heading>
<Text mt='2'>List of all available courses.</Text> <Text mt='2'>List of all courses.</Text>
</Box> </Box>
{(me.isAdmin || me.isTeacher) && ( {(me.isAdmin || me.isTeacher) && (
<Button <Button

View File

@ -14,6 +14,11 @@ import { IPageProps } from '@educt/interfaces';
import LoadingPage from '@educt/components/LoadingPage'; import LoadingPage from '@educt/components/LoadingPage';
import UpdateEmailForm from './components/UpdateEmailForm'; import UpdateEmailForm from './components/UpdateEmailForm';
/**
* Containers
*/
import ConfirmEmailContainer from './containers/ConfirmEmailContainer';
/** /**
* Hooks * Hooks
*/ */
@ -21,22 +26,15 @@ import { useHistory } from 'react-router';
import { useRootStore } from '@educt/hooks/useRootStore'; import { useRootStore } from '@educt/hooks/useRootStore';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
/**
* Contexts
*/
import { ChangeEmailPageContext } from '@educt/contexts';
/** /**
* Providers * Providers
*/ */
import { ChangeEmailPageContextProvider } from '@educt/providers'; import { ChangeEmailPageContextProvider } from '@educt/providers';
import { ChangeEmailPageContext } from '@educt/contexts';
import ConfirmEmailContainer from './containers/ConfirmEmailContainer';
{
/* <>
{!isCodeSent ? (
<UpdateEmailForm currentEmail={me.email} />
) : (
confirmEmailData && <ConfirmEmailContainer data={confirmEmailData} />
)}
</> */
}
/** /**
* Change email page * Change email page

View File

@ -1,40 +1,59 @@
import React from 'react'; import React from 'react';
import { Link as ReactRouterLink } from 'react-router-dom'; import { Link as ReactRouterLink } from 'react-router-dom';
import { Tag, TagLabel, LinkBox, Heading, LinkOverlay } from '@chakra-ui/react'; import { Tag, TagLabel, LinkBox, Heading, LinkOverlay, Icon, Link } from '@chakra-ui/react';
import { Text, Link, Box, Flex } from '@chakra-ui/layout'; import { Box, Flex } from '@chakra-ui/layout';
import { MdVideoLibrary, MdGroup, MdThumbUp } from 'react-icons/md';
/** /**
* Types * Types
*/ */
import { UserCourseType } from '@educt/interfaces'; import { UserCourseType } from '@educt/interfaces';
import { TimeIcon } from '@chakra-ui/icons';
/**
* Hooks
*/
import { useColorMode } from '@chakra-ui/react';
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='5' borderWidth='1px' rounded='lg'> <LinkBox as='div' p='5' borderWidth='1px' rounded='lg'>
<Flex mb='4' justifyContent='space-between'>
<Box>
<Tag variant='outline' borderRadius='full' alignSelf='flex-start'>
<TagLabel>{course.lessons_count} lessons</TagLabel>
</Tag>
</Box>
<Box>
<Tag variant='subtle' colorScheme='green' borderRadius='full' alignSelf='flex-start'>
<TagLabel>{course.category.title}</TagLabel>
</Tag>
</Box>
</Flex>
<Heading size='md' my='2'> <Heading size='md' my='2'>
<LinkOverlay href='#'>{course.title}</LinkOverlay> <LinkOverlay as={ReactRouterLink} to={`/course/${course.id}`} overflowWrap='anywhere'>
{course.title}
</LinkOverlay>
</Heading> </Heading>
<Text>{course.description}</Text> <Flex mt='6' alignItems='center'>
<Flex mt='2' alignItems='center'> <Icon as={TimeIcon} w='14px' h='14px' />
<MdVideoLibrary /> <Box
<Text pl='1' pr='4'> as='time'
{course.lessons_count}&nbsp;lessons color={colorMode === 'dark' ? 'gray.400' : 'gray.600'}
</Text> ml='2'
<MdGroup /> verticalAlign='middle'
<Text pl='1' pr='4'> dateTime={course.created_at}
{course.students_count}&nbsp;students >
</Text> Last update {new Date(course.created_at).toLocaleDateString()}
<MdThumbUp /> </Box>
<Text pl='1' pr='4'>
{course.likes_count}&nbsp;likes
</Text>
</Flex> </Flex>
</LinkBox> </LinkBox>
</Box>
// <Flex flexDir='column' borderWidth='1px' borderRadius='lg' p='10px' boxShadow='sm' mb='3'> // <Flex flexDir='column' borderWidth='1px' borderRadius='lg' p='10px' boxShadow='sm' mb='3'>
// <Tag colorScheme='purple' borderRadius='full' alignSelf='flex-start'> // <Tag colorScheme='purple' borderRadius='full' alignSelf='flex-start'>

View File

@ -29,11 +29,11 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses }) => {
} }
return ( return (
<Flex flexDir='column'> <Stack>
{courses.map(course => ( {courses.map(course => (
<CourseItem course={course} key={course.id} /> <CourseItem course={course} key={course.id} />
))} ))}
</Flex> </Stack>
); );
}; };

View File

@ -90,7 +90,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
return ( return (
<Box> <Box>
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<Box boxShadow='sm' borderRadius='lg' borderWidth='1px' padding='20px'> <Box boxShadow='sm' borderRadius='lg' borderWidth='1px' padding='5'>
<Stack spacing='5px'> <Stack spacing='5px'>
<FormControl id='phone_number'> <FormControl id='phone_number'>
<FormHelperText color='gray.500'>Phone number with country code</FormHelperText> <FormHelperText color='gray.500'>Phone number with country code</FormHelperText>
@ -106,6 +106,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
{errors.phone_number?.message} {errors.phone_number?.message}
</Text> </Text>
</FormControl> </FormControl>
<FormControl id='twitter_id'> <FormControl id='twitter_id'>
<FormHelperText color='gray.500'>Twitter</FormHelperText> <FormHelperText color='gray.500'>Twitter</FormHelperText>
<Input <Input
@ -120,6 +121,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
{errors.twitter_id?.message} {errors.twitter_id?.message}
</Text> </Text>
</FormControl> </FormControl>
<FormControl id='telegram'> <FormControl id='telegram'>
<FormHelperText color='gray.500'>Telegram</FormHelperText> <FormHelperText color='gray.500'>Telegram</FormHelperText>
<Input <Input
@ -134,6 +136,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
{errors.telegram_id?.message} {errors.telegram_id?.message}
</Text> </Text>
</FormControl> </FormControl>
<FormControl id='vk'> <FormControl id='vk'>
<FormHelperText color='gray.500'>VKontakte</FormHelperText> <FormHelperText color='gray.500'>VKontakte</FormHelperText>
<Input <Input

View File

@ -1,7 +1,20 @@
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Avatar, Tabs, TabList, Tab, TabPanels, TabPanel, Button } from '@chakra-ui/react'; import {
import { Box, Flex, Heading, Text, Stack, StackDivider } from '@chakra-ui/layout'; Avatar,
Tabs,
TabList,
Tab,
TabPanels,
TabPanel,
Button,
FormControl,
FormHelperText,
Input,
InputGroup,
InputRightElement,
} from '@chakra-ui/react';
import { Box, Flex, Heading, Text, Stack } from '@chakra-ui/layout';
/** /**
* Types * Types
@ -69,52 +82,50 @@ const ProfilePage: React.FC<IPageProps> = () => {
</TabList> </TabList>
<TabPanels mt='2'> <TabPanels mt='2'>
<TabPanel> <TabPanel>
<Box borderRadius='lg' borderWidth='1px' padding='20px' boxShadow='sm'> <form>
<Stack spacing='10px' divider={<StackDivider />}> <Box boxShadow='sm' borderRadius='lg' borderWidth='1px' padding='5'>
<Flex> <Stack spacing='5px'>
<Box> <FormControl isDisabled>
<Text as='small' color='gray.500'> <FormHelperText color='gray.500'>First name</FormHelperText>
First Name <Input type='text' value={me.first_name} size='md' variant='flushed' />
</Text> </FormControl>
<Text fontWeight='medium' fontSize='lg'>
{me.first_name} <FormControl isDisabled>
</Text> <FormHelperText color='gray.500'>Last name</FormHelperText>
</Box> <Input type='text' value={me.last_name} size='md' variant='flushed' />
</Flex> </FormControl>
<Flex>
<Box> <FormControl isDisabled>
<Text as='small' color='gray.500'> <FormHelperText color='gray.500'>Email</FormHelperText>
Last Name <InputGroup>
</Text> <Input type='text' value={me.email} size='md' variant='flushed' />
<Text fontWeight='medium' fontSize='lg'> <InputRightElement width='4rem'>
{me.last_name} <Button size='sm' h='1.90rem' w='full' onClick={() => history.push('/profile/change-email')}>
</Text> Edit
</Box> </Button>
</Flex> </InputRightElement>
<Flex justifyContent='space-between' alignItems='center'> </InputGroup>
<Box> </FormControl>
<Text as='small' color='gray.500'>
Email <FormControl isDisabled>
</Text> <FormHelperText color='gray.500'>Password</FormHelperText>
<Text fontWeight='medium' fontSize='lg'> <InputGroup>
{me.email} <Input type='text' value='**********' size='md' variant='flushed' />
</Text> <InputRightElement width='5rem'>
</Box> <Button
<Button onClick={() => history.push('/profile/change-email')}>Edit</Button> size='sm'
</Flex> h='1.90rem'
<Flex justifyContent='space-between' alignItems='center'> w='full'
<Box> onClick={() => history.push('/profile/change-password')}
<Text as='small' color='gray.500'> >
Password Change
</Text> </Button>
<Text fontWeight='medium' fontSize='lg'> </InputRightElement>
********** </InputGroup>
</Text> </FormControl>
</Box>
<Button onClick={() => history.push('/profile/change-password')}>Change</Button>
</Flex>
</Stack> </Stack>
</Box> </Box>
</form>
</TabPanel> </TabPanel>
<TabPanel> <TabPanel>
<UpdateUserContactsForm contacts={me.contacts} /> <UpdateUserContactsForm contacts={me.contacts} />

87
src/routes.config.ts Normal file
View File

@ -0,0 +1,87 @@
import { lazy } from 'react';
import { IPrivateRouteProps } from './components/PrivateRoute/PrivateRoute';
/**
* Pages
*/
import AuthPage from '@educt/pages/auth/auth';
import NotFoundPage from '@educt/pages/404/404';
import CreateCoursePage from '@educt/pages/courses/create/create';
import ChangePasswordPage from '@educt/pages/profile/change-password/change-password';
import ChangeEmailPage from '@educt/pages/profile/change-email/change-email';
import { UserRoleEnum } from './enums';
const CoursesPage = lazy(() => import('@educt/pages/courses/courses'));
const MainPage = lazy(() => import('@educt/pages/main/main'));
const MessagesPage = lazy(() => import('@educt/pages/messages/messages'));
const ProfilePage = lazy(() => import('@educt/pages/profile/profile'));
const UsersPage = lazy(() => import('@educt/pages/users/users'));
interface IRouteOption extends IPrivateRouteProps {
isPrivate: boolean;
}
export const routes: IRouteOption[] = [
{
path: '/',
title: 'Main',
component: MainPage,
exact: true,
isPrivate: true,
},
{
path: '/courses',
title: 'Courses',
component: CoursesPage,
exact: true,
isPrivate: true,
},
{
path: '/courses/create',
title: 'Create new course',
component: CreateCoursePage,
isPrivate: true,
},
{
path: '/messages',
title: 'Messages',
component: MessagesPage,
isPrivate: true,
},
{
path: '/profile',
title: 'Profile',
component: ProfilePage,
isPrivate: true,
exact: true,
},
{
path: '/profile/change-password',
title: 'Change password',
component: ChangePasswordPage,
isPrivate: true,
},
{
path: '/profile/change-email',
title: 'Change email',
component: ChangeEmailPage,
isPrivate: true,
},
{
path: '/users',
title: 'Users management',
component: UsersPage,
isPrivate: true,
roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
},
{
path: '/auth',
title: 'Auth',
component: AuthPage,
isPrivate: false,
},
{
title: 'Page not found',
component: NotFoundPage,
isPrivate: false,
},
];

View File

@ -1,11 +1,6 @@
import { makeAutoObservable, observable, runInAction } from 'mobx'; import { makeAutoObservable } from 'mobx';
import { createBrowserHistory } from 'history'; import { createBrowserHistory } from 'history';
/**
* Types
*/
import { IWindowDimensions } from '@educt/interfaces';
/** /**
* Stores * Stores
*/ */
@ -18,15 +13,11 @@ export default class UIStore {
public history: ReturnType<typeof createBrowserHistory>; public history: ReturnType<typeof createBrowserHistory>;
public windowDimensions: IWindowDimensions = { width: window.innerWidth, height: window.innerHeight };
constructor(root: RootStore) { constructor(root: RootStore) {
this.root = root; this.root = root;
this.history = createBrowserHistory(); this.history = createBrowserHistory();
makeAutoObservable(this, { makeAutoObservable(this);
windowDimensions: observable.struct,
});
this.setupListeners(); this.setupListeners();
} }
@ -41,31 +32,10 @@ export default class UIStore {
this.history.listen(({ pathname }) => { this.history.listen(({ pathname }) => {
this.setLocation(pathname); this.setLocation(pathname);
}); });
/**
* Window dimensions updated
*/
window.onresize = () => this.updateWindowDimensions();
} }
public setLocation(location: string): void { public setLocation(location: string): void {
console.log(`[history]: location changed to "${location}"`); console.log(`[history]: location changed to "${location}"`);
this.location = location; this.location = location;
} }
private updateWindowDimensions(): void {
this.windowDimensions = { width: window.innerWidth, height: window.innerHeight };
}
get isDesktop() {
return this.windowDimensions.width > 991;
}
get isTablet() {
return this.windowDimensions.width < 991;
}
get isMobile() {
return this.windowDimensions.width < 568;
}
} }