refactor: page

This commit is contained in:
Sergey Yarkov 2022-02-02 19:03:44 +03:00
parent 7f3eabe3e4
commit a14ed5eff9
22 changed files with 167 additions and 131 deletions

View File

@ -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}

View File

@ -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 };

View File

@ -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>

View File

@ -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 {

View File

@ -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;

View File

@ -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>
</> </>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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'}>

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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}>

View File

@ -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);

View File

@ -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);

View File

@ -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>
); );
}; };

View File

@ -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,