mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
refactor: page
This commit is contained in:
parent
7f3eabe3e4
commit
a14ed5eff9
@ -53,7 +53,7 @@ const AddStudentsModal: React.FC<AddStudentsModalPropsType> = ({
|
|||||||
icon={MdGroup}
|
icon={MdGroup}
|
||||||
heading='Add new students'
|
heading='Add new students'
|
||||||
description='Attach new students to current course.'
|
description='Attach new students to current course.'
|
||||||
confirmText='Add students'
|
confirmText='Add'
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onProceed={handleAttachStudents}
|
onProceed={handleAttachStudents}
|
||||||
|
|||||||
@ -1,17 +1,23 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { BoxProps, Box, Flex, Heading, Text, Divider } from '@chakra-ui/react';
|
import { BoxProps, Box, Flex, Heading, Text, Divider } from '@chakra-ui/react';
|
||||||
|
|
||||||
interface IPageHeadingProps extends BoxProps {
|
interface IPageComposition {
|
||||||
|
Heading: React.FC<IPageHeadingProps & BoxProps>;
|
||||||
|
Content: React.FC<BoxProps>;
|
||||||
|
Footer: React.FC<BoxProps>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Page: React.FC<BoxProps> & IPageComposition = props => {
|
||||||
|
return <Box {...props}>{props.children}</Box>;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IPageHeadingProps {
|
||||||
headingPrefix?: JSX.Element;
|
headingPrefix?: JSX.Element;
|
||||||
heading: string;
|
heading: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PageWrapper: React.FC<BoxProps> = props => {
|
Page.Heading = props => {
|
||||||
return <Box {...props}>{props.children}</Box>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PageHeading: React.FC<IPageHeadingProps> = props => {
|
|
||||||
const { headingPrefix, heading, description, ...boxProps } = props;
|
const { headingPrefix, heading, description, ...boxProps } = props;
|
||||||
return (
|
return (
|
||||||
<Box {...boxProps}>
|
<Box {...boxProps}>
|
||||||
@ -26,15 +32,17 @@ export const PageHeading: React.FC<IPageHeadingProps> = props => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PageContent: React.FC<BoxProps> = props => (
|
Page.Content = props => (
|
||||||
<Box mt='4' {...props}>
|
<Box mt='4' {...props}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const PageFooter: React.FC<BoxProps> = props => (
|
Page.Footer = props => (
|
||||||
<Box mt='4' {...props}>
|
<Box mt='4' {...props}>
|
||||||
<Divider m='20px 0' />
|
<Divider m='20px 0' />
|
||||||
{props.children}
|
{props.children}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export { Page };
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const Avatar: React.FC<IAvatarProps> = props => {
|
|||||||
return (
|
return (
|
||||||
<Flex flexDir={'column'} alignItems={'center'}>
|
<Flex flexDir={'column'} alignItems={'center'}>
|
||||||
<ChakraAvatar border={`4px solid ${borderColor}`} borderColor={borderColor} name={name} size='2xl'>
|
<ChakraAvatar border={`4px solid ${borderColor}`} borderColor={borderColor} name={name} size='2xl'>
|
||||||
<ChakraAvatarBadge boxSize='0.75em' bg={onlineColor} />
|
{isOnline && <ChakraAvatarBadge boxSize='0.75em' bg={onlineColor} />}
|
||||||
</ChakraAvatar>
|
</ChakraAvatar>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { ChatButton } from './ChatButton';
|
|||||||
|
|
||||||
export interface IAvatarProps {
|
export interface IAvatarProps {
|
||||||
name: string;
|
name: string;
|
||||||
isOnline: boolean;
|
isOnline?: boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IChatButtonProps {
|
export interface IChatButtonProps {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import React, { createContext } from 'react';
|
import React from 'react';
|
||||||
import { BoxProps, Flex } from '@chakra-ui/react';
|
import { BoxProps, Flex, FlexProps } from '@chakra-ui/react';
|
||||||
import { IUser } from '@educt/interfaces';
|
|
||||||
import { Avatar, ChatButton, IAvatarProps, IChatButtonProps } from './Avatar';
|
import { Avatar, ChatButton, IAvatarProps, IChatButtonProps } from './Avatar';
|
||||||
import { About, Details, Heading, IAboutProps, IDetailsProps, IHeadingProps, Info } from './Info';
|
import { About, Details, Heading, IAboutProps, IDetailsProps, IHeadingProps, Info } from './Info';
|
||||||
|
|
||||||
@ -13,10 +12,12 @@ interface IUserProfileComposition {
|
|||||||
About: React.FC<IAboutProps>;
|
About: React.FC<IAboutProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UserProfileContext = createContext<IUser | undefined>(undefined);
|
const UserProfile: React.FC<FlexProps> & IUserProfileComposition = props => {
|
||||||
|
return (
|
||||||
const UserProfile: React.FC & IUserProfileComposition = props => {
|
<Flex flexDir={{ base: 'column', md: 'row' }} {...props}>
|
||||||
return <Flex flexDir={{ base: 'column', md: 'row' }}>{props.children}</Flex>;
|
{props.children}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
UserProfile.Avatar = Avatar;
|
UserProfile.Avatar = Avatar;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { MdError } from 'react-icons/md';
|
|||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
import { IPageProps } from '@educt/interfaces';
|
import { IPageProps } from '@educt/interfaces';
|
||||||
import { PageContent, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
@ -28,8 +28,8 @@ const NotFoundPage: React.FC<IPageProps> = ({ title }) => {
|
|||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<PageWrapper textAlign='center' mt={40}>
|
<Page textAlign='center' mt={40}>
|
||||||
<PageContent>
|
<Page.Content>
|
||||||
<Icon as={MdError} w='16' h='16' />
|
<Icon as={MdError} w='16' h='16' />
|
||||||
<Heading display='block' as='h1' fontSize='6xl'>
|
<Heading display='block' as='h1' fontSize='6xl'>
|
||||||
Page not found
|
Page not found
|
||||||
@ -38,8 +38,8 @@ const NotFoundPage: React.FC<IPageProps> = ({ title }) => {
|
|||||||
<Link as={ReactRouterLink} to='/' color='blue.500'>
|
<Link as={ReactRouterLink} to='/' color='blue.500'>
|
||||||
Return to main page
|
Return to main page
|
||||||
</Link>
|
</Link>
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IPageProps } from '@educt/interfaces';
|
import { IPageProps } from '@educt/interfaces';
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { CategoryTableList, CategoryTableRow } from '@educt/components/Tables/CategoryTableList';
|
import { CategoryTableList, CategoryTableRow } from '@educt/components/Tables/CategoryTableList';
|
||||||
import { useFetchCategories } from '@educt/hooks/queries';
|
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useFetchCategories } from '@educt/hooks/queries';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Categories page
|
* Categories page
|
||||||
*/
|
*/
|
||||||
@ -14,12 +18,12 @@ const CategoriesPage: React.FC<IPageProps> = () => {
|
|||||||
if (isLoading || categories === null) return <LoadingPage />;
|
if (isLoading || categories === null) return <LoadingPage />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<PageHeading heading='Categories' description='Edit categories in the system.' />
|
<Page.Heading heading='Categories' description='Edit categories in the system.' />
|
||||||
<PageContent>
|
<Page.Content>
|
||||||
<CategoryTableList categories={categories} render={CategoryTableRow} />
|
<CategoryTableList categories={categories} render={CategoryTableRow} />
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import type { IPageProps } from '@educt/interfaces';
|
|||||||
* Components
|
* Components
|
||||||
*/
|
*/
|
||||||
import { CourseBackgroundImage, CourseHeader, CourseHeading, CourseInfo, LessonItem, LessonList } from '.';
|
import { CourseBackgroundImage, CourseHeader, CourseHeading, CourseInfo, LessonItem, LessonList } from '.';
|
||||||
import { PageWrapper, PageContent } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +50,7 @@ const CoursePage: React.FC<IPageProps> = () => {
|
|||||||
const handleEditCourse = () => history.push(`/courses/edit/${course.id}`);
|
const handleEditCourse = () => history.push(`/courses/edit/${course.id}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper maxW='1200px'>
|
<Page maxW='1200px'>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{course.title}</title>
|
<title>{course.title}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
@ -64,7 +64,7 @@ const CoursePage: React.FC<IPageProps> = () => {
|
|||||||
/>
|
/>
|
||||||
</CourseHeader>
|
</CourseHeader>
|
||||||
|
|
||||||
<PageContent mt={{ base: '5', sm: '2' }}>
|
<Page.Content mt={{ base: '5', sm: '2' }}>
|
||||||
<Grid templateColumns={{ base: '1fr', xl: '3fr 1fr' }} gap={'7'}>
|
<Grid templateColumns={{ base: '1fr', xl: '3fr 1fr' }} gap={'7'}>
|
||||||
<GridItem>
|
<GridItem>
|
||||||
<Box>
|
<Box>
|
||||||
@ -158,8 +158,8 @@ const CoursePage: React.FC<IPageProps> = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
</Grid>
|
</Grid>
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { IPageProps } from '@educt/interfaces';
|
|||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
*/
|
*/
|
||||||
import { PageWrapper, PageHeading, PageContent } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { CreateButton } from '@educt/components/Buttons';
|
import { CreateButton } from '@educt/components/Buttons';
|
||||||
import { Box } from '@chakra-ui/layout';
|
import { Box } from '@chakra-ui/layout';
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
@ -38,18 +38,18 @@ const CoursesPage: React.FC<IPageProps> = () => {
|
|||||||
if (me === null) return <LoadingPage />;
|
if (me === null) return <LoadingPage />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<PageHeading heading='Courses' description='List of all courses'>
|
<Page.Heading heading='Courses' description='List of all courses'>
|
||||||
<Box mt='5'>
|
<Box mt='5'>
|
||||||
{(me.isAdmin || me.isTeacher) && <CreateButton onClick={handleCreateCourse} ml='auto' display='block' />}
|
{(me.isAdmin || me.isTeacher) && <CreateButton onClick={handleCreateCourse} ml='auto' display='block' />}
|
||||||
</Box>
|
</Box>
|
||||||
</PageHeading>
|
</Page.Heading>
|
||||||
<PageContent mt='10'>
|
<Page.Content mt='10'>
|
||||||
<CoursesTabs mb='7' />
|
<CoursesTabs mb='7' />
|
||||||
<CategoryList render={CategoryItem} />
|
<CategoryList render={CategoryItem} />
|
||||||
<CourseList render={CourseItem} />
|
<CourseList render={CourseItem} />
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { IPageProps } from '@educt/interfaces';
|
import { IPageProps } from '@educt/interfaces';
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import CreateLessonForm from '@educt/components/Forms/LessonForm/CreateLessonForm';
|
import CreateLessonForm from '@educt/components/Forms/LessonForm/CreateLessonForm';
|
||||||
import { Redirect, useParams } from 'react-router-dom';
|
import { Redirect, useParams } from 'react-router-dom';
|
||||||
import { useFetchCourse } from '@educt/hooks/queries';
|
import { useFetchCourse } from '@educt/hooks/queries';
|
||||||
@ -21,12 +21,12 @@ const CreateLessonPage: React.FC<IPageProps> = () => {
|
|||||||
if (!course || isLoading) return <LoadingPage />;
|
if (!course || isLoading) return <LoadingPage />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<PageHeading heading='Lesson Creation' description='Create new lesson' />
|
<Page.Heading heading='Lesson Creation' description='Create new lesson' />
|
||||||
<PageContent>
|
<Page.Content>
|
||||||
<CreateLessonForm />
|
<CreateLessonForm />
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Flex, Box, Heading, Text } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
@ -11,23 +10,23 @@ import { IPageProps } from '@educt/interfaces';
|
|||||||
*/
|
*/
|
||||||
import PrevPageButton from '@educt/components/PrevPageButton';
|
import PrevPageButton from '@educt/components/PrevPageButton';
|
||||||
import { CreateCourseForm } from '@educt/components/Forms/CourseForm';
|
import { CreateCourseForm } from '@educt/components/Forms/CourseForm';
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create course page
|
* Create course page
|
||||||
*/
|
*/
|
||||||
const CreateCoursePage: React.FC<IPageProps> = () => {
|
const CreateCoursePage: React.FC<IPageProps> = () => {
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<PageHeading
|
<Page.Heading
|
||||||
headingPrefix={<PrevPageButton prevPage='/courses' />}
|
headingPrefix={<PrevPageButton prevPage='/courses' />}
|
||||||
heading='Course Creation'
|
heading='Course Creation'
|
||||||
description='Fill in the required fields to create a new course'
|
description='Fill in the required fields to create a new course'
|
||||||
/>
|
/>
|
||||||
<PageContent>
|
<Page.Content>
|
||||||
<CreateCourseForm />
|
<CreateCourseForm />
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import DeleteCourseDialog from '@educt/components/Dialogs/DeleteCourseDialog';
|
|||||||
import PrevPageButton from '@educt/components/PrevPageButton';
|
import PrevPageButton from '@educt/components/PrevPageButton';
|
||||||
import { DeleteButton } from '@educt/components/Buttons';
|
import { DeleteButton } from '@educt/components/Buttons';
|
||||||
import { StatusButton } from './components';
|
import { StatusButton } from './components';
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { StudentTableList, StudentTableRow } from './components/StudentTableList';
|
import { StudentTableList, StudentTableRow } from './components/StudentTableList';
|
||||||
import LessonList from './components/LessonList';
|
import LessonList from './components/LessonList';
|
||||||
|
|
||||||
@ -43,13 +43,13 @@ const EditCoursePage: React.FC<IPageProps> = () => {
|
|||||||
if (!course || isLoading) return <LoadingPage />;
|
if (!course || isLoading) return <LoadingPage />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>Editing {course.title}</title>
|
<title>Editing {course.title}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
<DeleteCourseDialog course={course} onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />
|
<DeleteCourseDialog course={course} onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />
|
||||||
<PageHeading
|
<Page.Heading
|
||||||
headingPrefix={<PrevPageButton prevPage='/courses' />}
|
headingPrefix={<PrevPageButton prevPage='/courses' />}
|
||||||
heading='Course editor'
|
heading='Course editor'
|
||||||
description={course.title}
|
description={course.title}
|
||||||
@ -58,8 +58,8 @@ const EditCoursePage: React.FC<IPageProps> = () => {
|
|||||||
<StatusButton courseId={course.id} currentStatus={course.status} />
|
<StatusButton courseId={course.id} currentStatus={course.status} />
|
||||||
<DeleteButton onClick={onOpenDeleteDialog} ml='2' />
|
<DeleteButton onClick={onOpenDeleteDialog} ml='2' />
|
||||||
</Flex>
|
</Flex>
|
||||||
</PageHeading>
|
</Page.Heading>
|
||||||
<PageContent>
|
<Page.Content>
|
||||||
<Box>
|
<Box>
|
||||||
<Tabs mt='8'>
|
<Tabs mt='8'>
|
||||||
<TabList>
|
<TabList>
|
||||||
@ -91,8 +91,8 @@ const EditCoursePage: React.FC<IPageProps> = () => {
|
|||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import {
|
|||||||
import ReactPlayer from 'react-player';
|
import ReactPlayer from 'react-player';
|
||||||
import { MdOutlineCircle, MdCheckCircle, MdOutlineFilePresent, MdSkipNext, MdSkipPrevious } from 'react-icons/md';
|
import { MdOutlineCircle, MdCheckCircle, MdOutlineFilePresent, MdSkipNext, MdSkipPrevious } from 'react-icons/md';
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
@ -138,17 +138,17 @@ const LessonPage: React.FC<IPageProps> = () => {
|
|||||||
if (me === null) return <LoadingPage />;
|
if (me === null) return <LoadingPage />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{lesson.title}</title>
|
<title>{lesson.title}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<PageHeading heading={lesson.title}>
|
<Page.Heading heading={lesson.title}>
|
||||||
<Flex mt='3' alignItems={'center'} justifyContent={'space-between'}>
|
<Flex mt='3' alignItems={'center'} justifyContent={'space-between'}>
|
||||||
<Text fontSize={'sm'}>~{moment.duration(lesson.duration, 'minutes').humanize()}</Text>
|
<Text fontSize={'sm'}>~{moment.duration(lesson.duration, 'minutes').humanize()}</Text>
|
||||||
{(me.isAdmin || me.isTeacher) && <EditButton onClick={handleEditLesson} />}
|
{(me.isAdmin || me.isTeacher) && <EditButton onClick={handleEditLesson} />}
|
||||||
</Flex>
|
</Flex>
|
||||||
</PageHeading>
|
</Page.Heading>
|
||||||
<PageContent>
|
<Page.Content>
|
||||||
<Grid templateColumns={{ base: '1fr', xl: '3fr 1fr' }}>
|
<Grid templateColumns={{ base: '1fr', xl: '3fr 1fr' }}>
|
||||||
<GridItem>
|
<GridItem>
|
||||||
<Box>
|
<Box>
|
||||||
@ -270,8 +270,8 @@ const LessonPage: React.FC<IPageProps> = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
</Grid>
|
</Grid>
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IPageProps } from '@educt/interfaces';
|
import { IPageProps } from '@educt/interfaces';
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { Redirect, useParams } from 'react-router-dom';
|
import { Redirect, useParams } from 'react-router-dom';
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
import { useFetchLesson } from '@educt/hooks/queries/lesson/useFetchLesson';
|
|
||||||
import EditLessonForm from '@educt/components/Forms/LessonForm/EditLessonForm';
|
import EditLessonForm from '@educt/components/Forms/LessonForm/EditLessonForm';
|
||||||
|
import { useFetchLesson } from '@educt/hooks/queries/lesson/useFetchLesson';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create Lesson page
|
* Create Lesson page
|
||||||
@ -20,12 +20,12 @@ const EditLessonPage: React.FC<IPageProps> = () => {
|
|||||||
if (!lesson || isLoading) return <LoadingPage />;
|
if (!lesson || isLoading) return <LoadingPage />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<PageHeading heading='Lesson Editor' description={lesson.description} />
|
<Page.Heading heading='Lesson Editor' description={lesson.description} />
|
||||||
<PageContent>
|
<Page.Content>
|
||||||
<EditLessonForm lesson={lesson} />
|
<EditLessonForm lesson={lesson} />
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export const CourseCard: React.FC<BoxProps> = props => <Card py='5' px='6' {...p
|
|||||||
export const CourseCardHeading: React.FC<BoxProps> = props => (
|
export const CourseCardHeading: React.FC<BoxProps> = props => (
|
||||||
<Box borderLeftWidth={'5px'} borderColor={'blue.500'} pl='2' {...props} />
|
<Box borderLeftWidth={'5px'} borderColor={'blue.500'} pl='2' {...props} />
|
||||||
);
|
);
|
||||||
export const CourseCardTitle: React.FC<TextProps> = props => <Text fontSize={'2xl'} fontWeight={'bold'} {...props} />;
|
export const CourseCardTitle: React.FC<TextProps> = props => <Text fontSize={'2xl'} fontWeight={'medium'} {...props} />;
|
||||||
export const CourseCardCategory: React.FC = props => (
|
export const CourseCardCategory: React.FC = props => (
|
||||||
<Flex mt='2'>
|
<Flex mt='2'>
|
||||||
<Tag size='sm' borderRadius='full' variant={'solid'}>
|
<Tag size='sm' borderRadius='full' variant={'solid'}>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { Flex, Box, Text, Link, SimpleGrid, Divider } from '@chakra-ui/react';
|
import { Flex, Box, Text, Link, SimpleGrid, Divider } from '@chakra-ui/react';
|
||||||
import { Link as ReactRouterLink } from 'react-router-dom';
|
import { Link as ReactRouterLink } from 'react-router-dom';
|
||||||
import { IPageProps } from '@educt/interfaces';
|
import { IPageProps } from '@educt/interfaces';
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { Stat, StatContent, StatIcon, StatLabel, StatNumber } from '@educt/components/Stat';
|
import { Stat, StatContent, StatIcon, StatLabel, StatNumber } from '@educt/components/Stat';
|
||||||
import { MdOutlineCollectionsBookmark, MdOutlineGroup, MdOutlineMessage, MdOutlineVideoLibrary } from 'react-icons/md';
|
import { MdOutlineCollectionsBookmark, MdOutlineGroup, MdOutlineMessage, MdOutlineVideoLibrary } from 'react-icons/md';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
@ -21,9 +21,13 @@ import {
|
|||||||
CourseCardList,
|
CourseCardList,
|
||||||
} from './components';
|
} from './components';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import { useFetchCourses, useFetchStat } from '@educt/hooks/queries';
|
import { useFetchCourses, useFetchStat } from '@educt/hooks/queries';
|
||||||
import { useSocketEvent } from '@educt/hooks/useSocketEvent';
|
import { useSocketEvent } from '@educt/hooks/useSocketEvent';
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main page
|
* Main page
|
||||||
@ -52,9 +56,9 @@ const MainPage: React.FC<IPageProps> = () => {
|
|||||||
if (me === null || courses === null || stat === null) return <LoadingPage />;
|
if (me === null || courses === null || stat === null) return <LoadingPage />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<PageHeading heading={`👋 Hello ${me.first_name},`} description="This what we've got today for you." />
|
<Page.Heading heading={`👋 Hello ${me.first_name},`} description="This what we've got today for you." />
|
||||||
<PageContent>
|
<Page.Content>
|
||||||
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing='6'>
|
<SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing='6'>
|
||||||
<Stat>
|
<Stat>
|
||||||
<StatLabel>Online</StatLabel>
|
<StatLabel>Online</StatLabel>
|
||||||
@ -121,8 +125,8 @@ const MainPage: React.FC<IPageProps> = () => {
|
|||||||
</CourseCardList>
|
</CourseCardList>
|
||||||
</Box>
|
</Box>
|
||||||
<Divider />
|
<Divider />
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Heading, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Grid } from '@chakra-ui/react';
|
import { Box, Heading } from '@chakra-ui/react';
|
||||||
|
import { Page } from '@educt/components/PageElements';
|
||||||
|
|
||||||
import { IPageProps } from '@educt/interfaces';
|
import { IPageProps } from '@educt/interfaces';
|
||||||
|
|
||||||
@ -8,9 +9,9 @@ import { IPageProps } from '@educt/interfaces';
|
|||||||
*/
|
*/
|
||||||
const MessagesPage: React.FC<IPageProps> = () => {
|
const MessagesPage: React.FC<IPageProps> = () => {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Page>
|
||||||
<Heading as='h1'>Messages page</Heading>
|
<Page.Heading heading='Messages' />
|
||||||
</Box>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -69,16 +69,6 @@ export const ProfileDescription: React.FC<IProfileDescriptionProps> = props => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ProfileSignOutButton: React.FC<ButtonProps> = () => {
|
|
||||||
const { logout } = useLogout();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button onClick={logout} size='sm' w={{ base: 'full', sm: '150px' }}>
|
|
||||||
Sign Out
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ProfileLoading: React.FC<BoxProps> = props => {
|
export const ProfileLoading: React.FC<BoxProps> = props => {
|
||||||
return (
|
return (
|
||||||
<Box maxW='900px' {...props}>
|
<Box maxW='900px' {...props}>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
|
||||||
Tabs,
|
Tabs,
|
||||||
TabList,
|
TabList,
|
||||||
Tab,
|
Tab,
|
||||||
@ -23,8 +22,9 @@ import { IPageProps } from '@educt/interfaces';
|
|||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
*/
|
*/
|
||||||
import { PageHeading, PageContent, PageWrapper, PageFooter } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { ProfileAvatar, ProfileBaseInfo, ProfileDescription, ProfileLoading, ProfileSignOutButton } from './components';
|
import { ProfileLoading } from './components';
|
||||||
|
import { UserProfile } from '@educt/components/UserProfile';
|
||||||
import { CourseList } from './components/CourseList';
|
import { CourseList } from './components/CourseList';
|
||||||
import UpdateUserContactsForm from '@educt/pages/profile/components/UpdateUserContactsForm';
|
import UpdateUserContactsForm from '@educt/pages/profile/components/UpdateUserContactsForm';
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ import UpdateUserContactsForm from '@educt/pages/profile/components/UpdateUserCo
|
|||||||
*/
|
*/
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profile page
|
* Profile page
|
||||||
@ -46,13 +47,17 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
|||||||
if (me === null) return <ProfileLoading />;
|
if (me === null) return <ProfileLoading />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper maxW='900px'>
|
<Page maxW={'900px'}>
|
||||||
<PageHeading heading='My account' description='Update you contacts information or password here.' />
|
<Page.Heading heading='My account' description='Update you contacts information or password here.' />
|
||||||
<ProfileBaseInfo mt='20'>
|
<Page.Content>
|
||||||
<ProfileAvatar name={me.fullname} />
|
<UserProfile mt='14'>
|
||||||
<ProfileDescription fullname={me.fullname} email={me.email} roles={me.roles} />
|
<UserProfile.Avatar name={me.fullname} />
|
||||||
</ProfileBaseInfo>
|
<UserProfile.Info>
|
||||||
<PageContent>
|
<UserProfile.Heading fullname={me.fullname} roles={me.roles} />
|
||||||
|
<UserProfile.Details registered={me.created_at} lastLogin={me.last_login} />
|
||||||
|
<UserProfile.About about={me.about} />
|
||||||
|
</UserProfile.Info>
|
||||||
|
</UserProfile>
|
||||||
<Tabs mt='7'>
|
<Tabs mt='7'>
|
||||||
<TabList>
|
<TabList>
|
||||||
<Tab>Account information</Tab>
|
<Tab>Account information</Tab>
|
||||||
@ -116,12 +121,9 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
|||||||
)}
|
)}
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
<PageFooter textAlign='center'>
|
</Page>
|
||||||
<ProfileSignOutButton />
|
|
||||||
</PageFooter>
|
|
||||||
</PageWrapper>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProfilePage;
|
export default observer(ProfilePage);
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import { Redirect } from 'react-router-dom';
|
import { Redirect } from 'react-router-dom';
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
import { PageContent, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { UserProfile } from '@educt/components/UserProfile';
|
import { UserProfile } from '@educt/components/UserProfile';
|
||||||
import type { IPageProps } from '@educt/interfaces';
|
import type { IPageProps } from '@educt/interfaces';
|
||||||
|
|
||||||
@ -10,18 +11,27 @@ import type { IPageProps } from '@educt/interfaces';
|
|||||||
*/
|
*/
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useFetchUser } from '@educt/hooks/queries/user/useFetchUser';
|
import { useFetchUser } from '@educt/hooks/queries/user/useFetchUser';
|
||||||
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
|
||||||
const UserPage: React.FC<IPageProps> = () => {
|
const UserPage: React.FC<IPageProps> = () => {
|
||||||
const { id } = useParams<{ id: string }>();
|
const params = useParams<{ id: string }>();
|
||||||
const { data: user, error, isLoading } = useFetchUser(id);
|
const {
|
||||||
|
userStore: { me },
|
||||||
|
} = useRootStore();
|
||||||
|
const { data: user, error, isLoading } = useFetchUser(params.id);
|
||||||
|
|
||||||
if (error?.response?.status === 404) return <Redirect to='/404' />;
|
if (error?.response?.status === 404) return <Redirect to='/404' />;
|
||||||
|
|
||||||
if (user === null || isLoading) return <LoadingPage />;
|
if (user === null || me === null || isLoading) return <LoadingPage />;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to profile page
|
||||||
|
*/
|
||||||
|
if (me.id === params.id) return <Redirect to='/profile' />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper maxW='800px'>
|
<Page>
|
||||||
<PageContent>
|
<Page.Content maxW={'900px'}>
|
||||||
<UserProfile>
|
<UserProfile>
|
||||||
<UserProfile.Avatar name={user.fullname} isOnline>
|
<UserProfile.Avatar name={user.fullname} isOnline>
|
||||||
<UserProfile.ChatButton userid={user.id} />
|
<UserProfile.ChatButton userid={user.id} />
|
||||||
@ -32,9 +42,9 @@ const UserPage: React.FC<IPageProps> = () => {
|
|||||||
<UserProfile.About about={user.about} />
|
<UserProfile.About about={user.about} />
|
||||||
</UserProfile.Info>
|
</UserProfile.Info>
|
||||||
</UserProfile>
|
</UserProfile>
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UserPage;
|
export default observer(UserPage);
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import type { IPageProps } from '@educt/interfaces';
|
|||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
*/
|
*/
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { UserList, UserItem } from './components/UserList';
|
import { UserList, UserItem } from './components/UserList';
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
|
|
||||||
@ -35,12 +35,12 @@ const UsersPage: React.FC<IPageProps> = () => {
|
|||||||
return (
|
return (
|
||||||
// TODO: delete page context for better performance
|
// TODO: delete page context for better performance
|
||||||
<UsersPageContextProvider>
|
<UsersPageContextProvider>
|
||||||
<PageWrapper>
|
<Page>
|
||||||
<PageHeading heading='User management' description='You can add or delete users on this page.' />
|
<Page.Heading heading='User management' description='You can add or delete users on this page.' />
|
||||||
<PageContent mt='5'>
|
<Page.Content mt='5'>
|
||||||
<UserList render={UserItem} limit={6} />
|
<UserList render={UserItem} limit={6} />
|
||||||
</PageContent>
|
</Page.Content>
|
||||||
</PageWrapper>
|
</Page>
|
||||||
</UsersPageContextProvider>
|
</UsersPageContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -62,10 +62,25 @@ export default class UserStore {
|
|||||||
public async loadCurrentUserData() {
|
public async loadCurrentUserData() {
|
||||||
const result = await UserServiceInstance.fetchMe();
|
const result = await UserServiceInstance.fetchMe();
|
||||||
const {
|
const {
|
||||||
data: { id, first_name, last_name, fullname, email, roles, likes, contacts, courses, created_at, updated_at },
|
data: {
|
||||||
|
id,
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
fullname,
|
||||||
|
about,
|
||||||
|
last_login,
|
||||||
|
email,
|
||||||
|
roles,
|
||||||
|
likes,
|
||||||
|
contacts,
|
||||||
|
courses,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
},
|
||||||
} = result;
|
} = result;
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
|
// TODO Fix this
|
||||||
this.me = {
|
this.me = {
|
||||||
id,
|
id,
|
||||||
first_name,
|
first_name,
|
||||||
@ -73,6 +88,8 @@ export default class UserStore {
|
|||||||
email,
|
email,
|
||||||
roles,
|
roles,
|
||||||
fullname,
|
fullname,
|
||||||
|
about,
|
||||||
|
last_login,
|
||||||
likes,
|
likes,
|
||||||
contacts,
|
contacts,
|
||||||
courses,
|
courses,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user