mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
refactor: added routes config & refactor layout
This commit is contained in:
parent
27e1cdbfa5
commit
c92163accb
29
package-lock.json
generated
29
package-lock.json
generated
@ -984,17 +984,36 @@
|
||||
"integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
|
||||
},
|
||||
"@emotion/react": {
|
||||
"version": "11.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.4.1.tgz",
|
||||
"integrity": "sha512-pRegcsuGYj4FCdZN6j5vqCALkNytdrKw3TZMekTzNXixRg4wkLsU5QEaBG5LC6l01Vppxlp7FE3aTHpIG5phLg==",
|
||||
"version": "11.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.5.0.tgz",
|
||||
"integrity": "sha512-MYq/bzp3rYbee4EMBORCn4duPQfgpiEB5XzrZEBnUZAL80Qdfr7CEv/T80jwaTl/dnZmt9SnTa8NkTrwFNpLlw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@emotion/cache": "^11.4.0",
|
||||
"@emotion/cache": "^11.5.0",
|
||||
"@emotion/serialize": "^1.0.2",
|
||||
"@emotion/sheet": "^1.0.2",
|
||||
"@emotion/sheet": "^1.0.3",
|
||||
"@emotion/utils": "^1.0.0",
|
||||
"@emotion/weak-memoize": "^0.2.5",
|
||||
"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": {
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
"lint": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/icons": "^1.0.15",
|
||||
"@chakra-ui/react": "^1.0.0",
|
||||
"@emotion/react": "^11.0.0",
|
||||
"@emotion/styled": "^11.0.0",
|
||||
"@chakra-ui/icons": "^1.0.16",
|
||||
"@chakra-ui/react": "^1.6.10",
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@hookform/resolvers": "^2.8.0",
|
||||
"axios": "^0.21.1",
|
||||
"framer-motion": "^4.0.0",
|
||||
|
||||
44
src/App.tsx
44
src/App.tsx
@ -1,10 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Router, Switch } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
import { UserRoleEnum } from '@educt/enums';
|
||||
import { routes } from './routes.config';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
@ -17,20 +13,6 @@ import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import PrivateRoute from '@educt/components/PrivateRoute';
|
||||
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 {
|
||||
uiStore: { history },
|
||||
@ -39,26 +21,10 @@ const App = () => {
|
||||
return (
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<PrivateRoute exact path='/' component={MainPage} title='Home' />
|
||||
<PrivateRoute exact path='/courses' component={CoursesPage} title='Courses' />
|
||||
<PrivateRoute
|
||||
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} />
|
||||
{routes.map((routeProps, i) => {
|
||||
const { isPrivate, ...props } = routeProps;
|
||||
return isPrivate ? <PrivateRoute key={i} {...props} /> : <PublicRoute key={i} {...props} />;
|
||||
})}
|
||||
</Switch>
|
||||
</Router>
|
||||
);
|
||||
|
||||
@ -50,8 +50,8 @@ const Nav: React.FC = () => {
|
||||
<NavList links={config.links} />
|
||||
</Flex>
|
||||
<Box textAlign='center' mt='auto' mb='5'>
|
||||
<Text fontSize='xs' color='gray.500' lineHeight='1.5'>
|
||||
Learning Management System <br /> Made by Sergey Yarkov
|
||||
<Text fontSize='xs' color='gray.500' lineHeight='1.6'>
|
||||
Learning Management System <br /> Made by Sergey Yarkov <br />v{config.metaData.appVersion}
|
||||
</Text>
|
||||
<Box textAlign='center' mt='2'>
|
||||
<IconButton variant='link' aria-label='GitHub repository'>
|
||||
|
||||
@ -3,16 +3,13 @@ import React from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { Route, Redirect } from 'react-router-dom';
|
||||
import { RouteProps } from 'react-router';
|
||||
import { IPageProps } from '@educt/interfaces';
|
||||
import { UserRoleEnum } from '@educt/enums';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import Layout from '@educt/components/Layout';
|
||||
import ErrorFallback from '@educt/components/ErrorFallback';
|
||||
import { IPublicRouteProps } from '../PublicRoute/PublicRoute';
|
||||
|
||||
export interface IPrivateRouteProps extends RouteProps {
|
||||
component: React.FC<IPageProps>;
|
||||
title?: string;
|
||||
export interface IPrivateRouteProps extends IPublicRouteProps {
|
||||
/**
|
||||
* This field means who can access the page
|
||||
*/
|
||||
|
||||
@ -7,7 +7,7 @@ import ErrorFallback from '@educt/components/ErrorFallback';
|
||||
|
||||
export interface IPublicRouteProps extends RouteProps {
|
||||
component: React.FC<IPageProps>;
|
||||
title?: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { UserRoleEnum } from '@educt/enums';
|
||||
import { MdAccountCircle, MdCollectionsBookmark, MdGroup, MdHome, MdMessage } from 'react-icons/md';
|
||||
import { IAppConfig } from './interfaces';
|
||||
import packageJson from '../package.json';
|
||||
|
||||
const config: IAppConfig = {
|
||||
/**
|
||||
@ -8,7 +9,8 @@ const config: IAppConfig = {
|
||||
*/
|
||||
metaData: {
|
||||
appName: 'Educt',
|
||||
appDescription: 'Learn management system',
|
||||
appDescription: 'Learning management system',
|
||||
appVersion: packageJson.version,
|
||||
},
|
||||
/**
|
||||
* Links that are rendered in the navigation
|
||||
|
||||
@ -6,6 +6,7 @@ export interface IAppConfig {
|
||||
metaData: {
|
||||
appName: string;
|
||||
appDescription: string;
|
||||
appVersion: string;
|
||||
};
|
||||
links: LinkType[];
|
||||
}
|
||||
@ -127,10 +128,5 @@ export interface IApiRespose<T> {
|
||||
}
|
||||
|
||||
export interface IPageProps extends RouteComponentProps {
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface IWindowDimensions {
|
||||
width: number;
|
||||
height: number;
|
||||
title: string;
|
||||
}
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
import React from 'react';
|
||||
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 { 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();
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -14,7 +19,7 @@ const NotFoundPage: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Error 404</title>
|
||||
<title>{title}</title>
|
||||
</Helmet>
|
||||
<Box textAlign='center' mt={40}>
|
||||
<Flex justifyContent='center'>
|
||||
|
||||
@ -11,7 +11,7 @@ import AuthForm from './components/AuthForm';
|
||||
/**
|
||||
* Auth page
|
||||
*/
|
||||
const AuthPage: React.FC<IPageProps> = () => {
|
||||
const AuthPage: React.FC<IPageProps> = ({ title }) => {
|
||||
const { authStore } = useRootStore();
|
||||
|
||||
/**
|
||||
@ -24,7 +24,7 @@ const AuthPage: React.FC<IPageProps> = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Auth</title>
|
||||
<title>{title}</title>
|
||||
</Helmet>
|
||||
<Flex minHeight='100vh' align='center' justifyContent='center' padding='5'>
|
||||
<AuthForm />
|
||||
|
||||
@ -44,7 +44,7 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
|
||||
const toast = useToast();
|
||||
|
||||
/**
|
||||
* Fetch handler
|
||||
* Fetch courses handler
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (me !== null) {
|
||||
@ -53,7 +53,7 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
|
||||
status: me.isAdmin || me.isTeacher ? courseStatus : CourseStatusEnum.PUBLISHED,
|
||||
});
|
||||
}
|
||||
}, [courseStore, selectedCategory, courseStatus, me]);
|
||||
}, [courseStore, selectedCategory, courseStatus]);
|
||||
|
||||
/**
|
||||
* Handle delete course
|
||||
|
||||
@ -30,7 +30,7 @@ import { useHistory } from 'react-router';
|
||||
/**
|
||||
* Courses page
|
||||
*/
|
||||
const CoursesPage: React.FC<IPageProps> = ({ title }) => {
|
||||
const CoursesPage: React.FC<IPageProps> = () => {
|
||||
const {
|
||||
userStore: { me },
|
||||
categoryStore,
|
||||
@ -46,7 +46,7 @@ const CoursesPage: React.FC<IPageProps> = ({ title }) => {
|
||||
<Flex alignItems='center' justifyContent='space-between'>
|
||||
<Box>
|
||||
<Heading as='h1'>Courses</Heading>
|
||||
<Text mt='2'>List of all available courses.</Text>
|
||||
<Text mt='2'>List of all courses.</Text>
|
||||
</Box>
|
||||
{(me.isAdmin || me.isTeacher) && (
|
||||
<Button
|
||||
|
||||
@ -14,6 +14,11 @@ import { IPageProps } from '@educt/interfaces';
|
||||
import LoadingPage from '@educt/components/LoadingPage';
|
||||
import UpdateEmailForm from './components/UpdateEmailForm';
|
||||
|
||||
/**
|
||||
* Containers
|
||||
*/
|
||||
import ConfirmEmailContainer from './containers/ConfirmEmailContainer';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
*/
|
||||
@ -21,22 +26,15 @@ import { useHistory } from 'react-router';
|
||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
/**
|
||||
* Contexts
|
||||
*/
|
||||
import { ChangeEmailPageContext } from '@educt/contexts';
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -1,40 +1,59 @@
|
||||
import React from 'react';
|
||||
import { Link as ReactRouterLink } from 'react-router-dom';
|
||||
import { Tag, TagLabel, LinkBox, Heading, LinkOverlay } from '@chakra-ui/react';
|
||||
import { Text, Link, Box, Flex } from '@chakra-ui/layout';
|
||||
import { MdVideoLibrary, MdGroup, MdThumbUp } from 'react-icons/md';
|
||||
import { Tag, TagLabel, LinkBox, Heading, LinkOverlay, Icon, Link } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/layout';
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
import { UserCourseType } from '@educt/interfaces';
|
||||
import { TimeIcon } from '@chakra-ui/icons';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
*/
|
||||
import { useColorMode } from '@chakra-ui/react';
|
||||
|
||||
type CourseItemPropsType = {
|
||||
course: UserCourseType;
|
||||
};
|
||||
|
||||
const CourseItem: React.FC<CourseItemPropsType> = ({ course }) => {
|
||||
const { colorMode } = useColorMode();
|
||||
return (
|
||||
<LinkBox as='div' p='5' borderWidth='1px' rounded='lg'>
|
||||
<Heading size='md' my='2'>
|
||||
<LinkOverlay href='#'>{course.title}</LinkOverlay>
|
||||
</Heading>
|
||||
<Text>{course.description}</Text>
|
||||
<Flex mt='2' alignItems='center'>
|
||||
<MdVideoLibrary />
|
||||
<Text pl='1' pr='4'>
|
||||
{course.lessons_count} lessons
|
||||
</Text>
|
||||
<MdGroup />
|
||||
<Text pl='1' pr='4'>
|
||||
{course.students_count} students
|
||||
</Text>
|
||||
<MdThumbUp />
|
||||
<Text pl='1' pr='4'>
|
||||
{course.likes_count} likes
|
||||
</Text>
|
||||
</Flex>
|
||||
</LinkBox>
|
||||
<Box>
|
||||
<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'>
|
||||
<LinkOverlay as={ReactRouterLink} to={`/course/${course.id}`} overflowWrap='anywhere'>
|
||||
{course.title}
|
||||
</LinkOverlay>
|
||||
</Heading>
|
||||
<Flex mt='6' alignItems='center'>
|
||||
<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'>
|
||||
// <Tag colorScheme='purple' borderRadius='full' alignSelf='flex-start'>
|
||||
|
||||
@ -29,11 +29,11 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex flexDir='column'>
|
||||
<Stack>
|
||||
{courses.map(course => (
|
||||
<CourseItem course={course} key={course.id} />
|
||||
))}
|
||||
</Flex>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
|
||||
return (
|
||||
<Box>
|
||||
<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'>
|
||||
<FormControl id='phone_number'>
|
||||
<FormHelperText color='gray.500'>Phone number with country code</FormHelperText>
|
||||
@ -106,6 +106,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
|
||||
{errors.phone_number?.message}
|
||||
</Text>
|
||||
</FormControl>
|
||||
|
||||
<FormControl id='twitter_id'>
|
||||
<FormHelperText color='gray.500'>Twitter</FormHelperText>
|
||||
<Input
|
||||
@ -120,6 +121,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
|
||||
{errors.twitter_id?.message}
|
||||
</Text>
|
||||
</FormControl>
|
||||
|
||||
<FormControl id='telegram'>
|
||||
<FormHelperText color='gray.500'>Telegram</FormHelperText>
|
||||
<Input
|
||||
@ -134,6 +136,7 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
|
||||
{errors.telegram_id?.message}
|
||||
</Text>
|
||||
</FormControl>
|
||||
|
||||
<FormControl id='vk'>
|
||||
<FormHelperText color='gray.500'>VKontakte</FormHelperText>
|
||||
<Input
|
||||
|
||||
@ -1,7 +1,20 @@
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Avatar, Tabs, TabList, Tab, TabPanels, TabPanel, Button } from '@chakra-ui/react';
|
||||
import { Box, Flex, Heading, Text, Stack, StackDivider } from '@chakra-ui/layout';
|
||||
import {
|
||||
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
|
||||
@ -69,52 +82,50 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
||||
</TabList>
|
||||
<TabPanels mt='2'>
|
||||
<TabPanel>
|
||||
<Box borderRadius='lg' borderWidth='1px' padding='20px' boxShadow='sm'>
|
||||
<Stack spacing='10px' divider={<StackDivider />}>
|
||||
<Flex>
|
||||
<Box>
|
||||
<Text as='small' color='gray.500'>
|
||||
First Name
|
||||
</Text>
|
||||
<Text fontWeight='medium' fontSize='lg'>
|
||||
{me.first_name}
|
||||
</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex>
|
||||
<Box>
|
||||
<Text as='small' color='gray.500'>
|
||||
Last Name
|
||||
</Text>
|
||||
<Text fontWeight='medium' fontSize='lg'>
|
||||
{me.last_name}
|
||||
</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex justifyContent='space-between' alignItems='center'>
|
||||
<Box>
|
||||
<Text as='small' color='gray.500'>
|
||||
Email
|
||||
</Text>
|
||||
<Text fontWeight='medium' fontSize='lg'>
|
||||
{me.email}
|
||||
</Text>
|
||||
</Box>
|
||||
<Button onClick={() => history.push('/profile/change-email')}>Edit</Button>
|
||||
</Flex>
|
||||
<Flex justifyContent='space-between' alignItems='center'>
|
||||
<Box>
|
||||
<Text as='small' color='gray.500'>
|
||||
Password
|
||||
</Text>
|
||||
<Text fontWeight='medium' fontSize='lg'>
|
||||
**********
|
||||
</Text>
|
||||
</Box>
|
||||
<Button onClick={() => history.push('/profile/change-password')}>Change</Button>
|
||||
</Flex>
|
||||
</Stack>
|
||||
</Box>
|
||||
<form>
|
||||
<Box boxShadow='sm' borderRadius='lg' borderWidth='1px' padding='5'>
|
||||
<Stack spacing='5px'>
|
||||
<FormControl isDisabled>
|
||||
<FormHelperText color='gray.500'>First name</FormHelperText>
|
||||
<Input type='text' value={me.first_name} size='md' variant='flushed' />
|
||||
</FormControl>
|
||||
|
||||
<FormControl isDisabled>
|
||||
<FormHelperText color='gray.500'>Last name</FormHelperText>
|
||||
<Input type='text' value={me.last_name} size='md' variant='flushed' />
|
||||
</FormControl>
|
||||
|
||||
<FormControl isDisabled>
|
||||
<FormHelperText color='gray.500'>Email</FormHelperText>
|
||||
<InputGroup>
|
||||
<Input type='text' value={me.email} size='md' variant='flushed' />
|
||||
<InputRightElement width='4rem'>
|
||||
<Button size='sm' h='1.90rem' w='full' onClick={() => history.push('/profile/change-email')}>
|
||||
Edit
|
||||
</Button>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</FormControl>
|
||||
|
||||
<FormControl isDisabled>
|
||||
<FormHelperText color='gray.500'>Password</FormHelperText>
|
||||
<InputGroup>
|
||||
<Input type='text' value='**********' size='md' variant='flushed' />
|
||||
<InputRightElement width='5rem'>
|
||||
<Button
|
||||
size='sm'
|
||||
h='1.90rem'
|
||||
w='full'
|
||||
onClick={() => history.push('/profile/change-password')}
|
||||
>
|
||||
Change
|
||||
</Button>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</Box>
|
||||
</form>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<UpdateUserContactsForm contacts={me.contacts} />
|
||||
|
||||
87
src/routes.config.ts
Normal file
87
src/routes.config.ts
Normal 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,
|
||||
},
|
||||
];
|
||||
@ -1,11 +1,6 @@
|
||||
import { makeAutoObservable, observable, runInAction } from 'mobx';
|
||||
import { makeAutoObservable } from 'mobx';
|
||||
import { createBrowserHistory } from 'history';
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
import { IWindowDimensions } from '@educt/interfaces';
|
||||
|
||||
/**
|
||||
* Stores
|
||||
*/
|
||||
@ -18,15 +13,11 @@ export default class UIStore {
|
||||
|
||||
public history: ReturnType<typeof createBrowserHistory>;
|
||||
|
||||
public windowDimensions: IWindowDimensions = { width: window.innerWidth, height: window.innerHeight };
|
||||
|
||||
constructor(root: RootStore) {
|
||||
this.root = root;
|
||||
this.history = createBrowserHistory();
|
||||
|
||||
makeAutoObservable(this, {
|
||||
windowDimensions: observable.struct,
|
||||
});
|
||||
makeAutoObservable(this);
|
||||
|
||||
this.setupListeners();
|
||||
}
|
||||
@ -41,31 +32,10 @@ export default class UIStore {
|
||||
this.history.listen(({ pathname }) => {
|
||||
this.setLocation(pathname);
|
||||
});
|
||||
|
||||
/**
|
||||
* Window dimensions updated
|
||||
*/
|
||||
window.onresize = () => this.updateWindowDimensions();
|
||||
}
|
||||
|
||||
public setLocation(location: string): void {
|
||||
console.log(`[history]: location changed to "${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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user