mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 18:29:15 +03:00
refactor: layout
This commit is contained in:
parent
4d0fef3fb1
commit
82c9021389
@ -1,4 +1,4 @@
|
|||||||
import React, { Suspense, useEffect } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Redirect } from 'react-router';
|
import { Redirect } from 'react-router';
|
||||||
import { useErrorHandler } from 'react-error-boundary';
|
import { useErrorHandler } from 'react-error-boundary';
|
||||||
@ -20,6 +20,7 @@ import NavDesktop from '@educt/components/Nav/Desktop';
|
|||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
|
||||||
type LayoutPropsType = { roles: UserRoleEnum[] | undefined };
|
type LayoutPropsType = { roles: UserRoleEnum[] | undefined };
|
||||||
@ -31,6 +32,9 @@ const Layout: React.FC<LayoutPropsType> = ({ children, roles }) => {
|
|||||||
const { userStore } = useRootStore();
|
const { userStore } = useRootStore();
|
||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch authorized user handler
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
userStore.loadCurrentUserData().catch(error => handleError(error));
|
userStore.loadCurrentUserData().catch(error => handleError(error));
|
||||||
}, [handleError, userStore]);
|
}, [handleError, userStore]);
|
||||||
@ -38,8 +42,8 @@ const Layout: React.FC<LayoutPropsType> = ({ children, roles }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
<Flex minH='100vh'>
|
|
||||||
<NavDesktop />
|
<NavDesktop />
|
||||||
|
<Flex minH='100vh' justifyContent='center'>
|
||||||
<Box
|
<Box
|
||||||
as='main'
|
as='main'
|
||||||
flex='1 0'
|
flex='1 0'
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React from 'react';
|
|||||||
|
|
||||||
const LoadingPage: React.FC = () => {
|
const LoadingPage: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Flex justifyContent='center' mt='40' maxW='950px'>
|
<Flex justifyContent='center' mt='40'>
|
||||||
<Box mt='auto' mb='auto' textAlign='center'>
|
<Box mt='auto' mb='auto' textAlign='center'>
|
||||||
<Spinner thickness='4px' speed='0.55s' emptyColor='gray.200' color='blue.500' size='xl' />
|
<Spinner thickness='4px' speed='0.55s' emptyColor='gray.200' color='blue.500' size='xl' />
|
||||||
<Text mt='2' color='gray.500' userSelect='none'>
|
<Text mt='2' color='gray.500' userSelect='none'>
|
||||||
|
|||||||
@ -1,10 +1,18 @@
|
|||||||
import config from '@educt/config';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Flex, Box, useColorMode } from '@chakra-ui/react';
|
import { Flex, Box, useColorMode } from '@chakra-ui/react';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import config from '@educt/config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components
|
||||||
|
*/
|
||||||
import NavList from '../NavList';
|
import NavList from '../NavList';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Nav component
|
* Nav component
|
||||||
|
|||||||
@ -1,16 +1,25 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { MdExitToApp } from 'react-icons/md';
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { IconButton, Skeleton } from '@chakra-ui/react';
|
||||||
import { Avatar } from '@chakra-ui/avatar';
|
import { Avatar } from '@chakra-ui/avatar';
|
||||||
import { Flex, Box, Text } from '@chakra-ui/layout';
|
import { Flex, Box, Text } from '@chakra-ui/layout';
|
||||||
import { Drawer, DrawerOverlay, DrawerContent, DrawerBody } from '@chakra-ui/modal';
|
import { Drawer, DrawerOverlay, DrawerContent, DrawerBody } from '@chakra-ui/modal';
|
||||||
|
import { MdExitToApp } from 'react-icons/md';
|
||||||
import { HamburgerIcon } from '@chakra-ui/icons';
|
import { HamburgerIcon } from '@chakra-ui/icons';
|
||||||
import { useDisclosure, IconButton, useColorMode, Skeleton } from '@chakra-ui/react';
|
|
||||||
import NavList from '../NavList';
|
|
||||||
import config from '@educt/config';
|
import config from '@educt/config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components
|
||||||
|
*/
|
||||||
|
import NavList from '../NavList';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
import useLogoutQuery from '@educt/hooks/useLogoutQuery';
|
import useLogoutQuery from '@educt/hooks/useLogoutQuery';
|
||||||
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
import { useColorMode, useDisclosure } from '@chakra-ui/react';
|
||||||
|
|
||||||
const NavMobile: React.FC = () => {
|
const NavMobile: React.FC = () => {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
import { userContainRoles } from '@educt/helpers';
|
||||||
import { Box, useColorMode } from '@chakra-ui/react';
|
import { Box, useColorMode } from '@chakra-ui/react';
|
||||||
import type { LinkType } from '@educt/types';
|
import type { LinkType } from '@educt/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
import { userContainRoles } from '@educt/helpers';
|
|
||||||
|
|
||||||
type NavLinkPropsType = {
|
type NavLinkPropsType = {
|
||||||
link: LinkType;
|
link: LinkType;
|
||||||
|
|||||||
@ -1,9 +1,21 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
import { LinkType } from '@educt/types';
|
import { LinkType } from '@educt/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components
|
||||||
|
*/
|
||||||
import NavLink from './NavLink';
|
import NavLink from './NavLink';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
type NavListPropsType = {
|
type NavListPropsType = {
|
||||||
links: LinkType[];
|
links: LinkType[];
|
||||||
onCloseDrawer?: () => void;
|
onCloseDrawer?: () => void;
|
||||||
|
|||||||
@ -14,6 +14,9 @@ import AuthForm from './components/AuthForm';
|
|||||||
const AuthPage: React.FC<IPageProps> = () => {
|
const AuthPage: React.FC<IPageProps> = () => {
|
||||||
const { authStore } = useRootStore();
|
const { authStore } = useRootStore();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User already authorized
|
||||||
|
*/
|
||||||
if (authStore.isLoggedIn) {
|
if (authStore.isLoggedIn) {
|
||||||
return <Redirect to='/' />;
|
return <Redirect to='/' />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MdAccountCircle, MdSchool, MdVpnKey } from 'react-icons/md';
|
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
@ -12,9 +11,23 @@ import {
|
|||||||
Flex,
|
Flex,
|
||||||
Box,
|
Box,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
import { MdAccountCircle, MdSchool, MdVpnKey } from 'react-icons/md';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
|
import type { SubmitHandler } from 'react-hook-form';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
import useLoginQuery from '@educt/hooks/useLoginQuery';
|
import useLoginQuery from '@educt/hooks/useLoginQuery';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schema
|
||||||
|
*/
|
||||||
import LoginSchema from './AuthForm.validator';
|
import LoginSchema from './AuthForm.validator';
|
||||||
|
|
||||||
type AuthInputType = {
|
type AuthInputType = {
|
||||||
@ -33,6 +46,9 @@ const AuthForm: React.FC = () => {
|
|||||||
});
|
});
|
||||||
const { login, loading } = useLoginQuery();
|
const { login, loading } = useLoginQuery();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login handler
|
||||||
|
*/
|
||||||
const onSubmit: SubmitHandler<AuthInputType> = data => {
|
const onSubmit: SubmitHandler<AuthInputType> = data => {
|
||||||
login(data.login, data.password).finally(() => reset());
|
login(data.login, data.password).finally(() => reset());
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useContext } from 'react';
|
import React from 'react';
|
||||||
import { Tag, TagLabel } from '@chakra-ui/react';
|
import { Tag, TagLabel } from '@chakra-ui/react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,6 +11,11 @@ import { ICategory } from '@educt/interfaces';
|
|||||||
*/
|
*/
|
||||||
import { CoursesPageContext } from '@educt/contexts';
|
import { CoursesPageContext } from '@educt/contexts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
type CategoryItemProps = {
|
type CategoryItemProps = {
|
||||||
category: ICategory;
|
category: ICategory;
|
||||||
};
|
};
|
||||||
@ -21,9 +26,7 @@ const CategoryItem: React.FC<CategoryItemProps> = ({ category }) => {
|
|||||||
/**
|
/**
|
||||||
* Handle click on change category
|
* Handle click on change category
|
||||||
*/
|
*/
|
||||||
const onCategoryChange = () => {
|
const onCategoryChange = () => setSelectedCategory(category);
|
||||||
setSelectedCategory(category);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
|
|||||||
@ -1,15 +1,28 @@
|
|||||||
import React, { useContext, useEffect } from 'react';
|
import React from 'react';
|
||||||
import { Box, Flex, Tag, TagLabel } from '@chakra-ui/react';
|
import { Box, Flex, Tag, TagLabel } from '@chakra-ui/react';
|
||||||
import { CoursesPageContext } from '@educt/contexts';
|
|
||||||
import CategoryItem from './CategoryItem';
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
import { ICategory } from '@educt/interfaces';
|
import { ICategory } from '@educt/interfaces';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components
|
||||||
|
*/
|
||||||
|
import CategoryItem from './CategoryItem';
|
||||||
import CategoryListLoading from './CategoryListLoading';
|
import CategoryListLoading from './CategoryListLoading';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contexts
|
||||||
|
*/
|
||||||
|
import { CoursesPageContext } from '@educt/contexts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useErrorHandler } from 'react-error-boundary';
|
import { useContext, useEffect } from 'react';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
import { useErrorHandler } from 'react-error-boundary';
|
||||||
|
|
||||||
type CategoryListPropsType = {
|
type CategoryListPropsType = {
|
||||||
categories: ICategory[] | null;
|
categories: ICategory[] | null;
|
||||||
@ -21,6 +34,9 @@ const CategoryList: React.FC<CategoryListPropsType> = ({ categories, isLoading }
|
|||||||
const { selectedCategory, setSelectedCategory } = useContext(CoursesPageContext);
|
const { selectedCategory, setSelectedCategory } = useContext(CoursesPageContext);
|
||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch handler
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
categoryStore.loadCategories().catch(error => handleError(error));
|
categoryStore.loadCategories().catch(error => handleError(error));
|
||||||
}, [categoryStore, handleError]);
|
}, [categoryStore, handleError]);
|
||||||
@ -42,9 +58,10 @@ const CategoryList: React.FC<CategoryListPropsType> = ({ categories, isLoading }
|
|||||||
>
|
>
|
||||||
<TagLabel>All categories</TagLabel>
|
<TagLabel>All categories</TagLabel>
|
||||||
</Tag>
|
</Tag>
|
||||||
{categories.map((category, i) => {
|
{/* Render items */}
|
||||||
return <CategoryItem key={category.id} category={category} />;
|
{categories.map((category, i) => (
|
||||||
})}
|
<CategoryItem key={category.id} category={category} />
|
||||||
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,20 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { observer } from 'mobx-react';
|
||||||
Flex,
|
|
||||||
Box,
|
|
||||||
Heading,
|
|
||||||
Text,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
Badge,
|
|
||||||
MenuItem,
|
|
||||||
MenuList,
|
|
||||||
Image,
|
|
||||||
Link,
|
|
||||||
IconButton,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { Link as ReactRouterLink } from 'react-router-dom';
|
import { Link as ReactRouterLink } from 'react-router-dom';
|
||||||
import { Icon } from '@chakra-ui/react';
|
import { Badge, Image, Link, IconButton, Icon } from '@chakra-ui/react';
|
||||||
|
import { Flex, Box, Heading, Text } from '@chakra-ui/layout';
|
||||||
|
import { Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/menu';
|
||||||
import { MdNote, MdMoreHoriz, MdGroup, MdThumbUp, MdVideoLibrary } from 'react-icons/md';
|
import { MdNote, MdMoreHoriz, MdGroup, MdThumbUp, MdVideoLibrary } from 'react-icons/md';
|
||||||
import { CheckIcon, DeleteIcon, EditIcon } from '@chakra-ui/icons';
|
import { CheckIcon, DeleteIcon, EditIcon } from '@chakra-ui/icons';
|
||||||
|
|
||||||
@ -22,15 +11,18 @@ import { CheckIcon, DeleteIcon, EditIcon } from '@chakra-ui/icons';
|
|||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
import { ICourse } from '@educt/interfaces';
|
import { ICourse } from '@educt/interfaces';
|
||||||
|
import { CourseStatusEnum } from '@educt/enums';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components
|
||||||
|
*/
|
||||||
|
import CourseStatusBadge from '@educt/components/CourseStatusBadge';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useColorMode } from '@chakra-ui/color-mode';
|
import { useColorMode } from '@chakra-ui/color-mode';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
import CourseStatusBadge from '@educt/components/CourseStatusBadge';
|
|
||||||
import { CourseStatusEnum } from '@educt/enums';
|
|
||||||
import { observer } from 'mobx-react';
|
|
||||||
|
|
||||||
type CourseItemPropsType = {
|
type CourseItemPropsType = {
|
||||||
course: Omit<ICourse, 'teacher' | 'students' | 'lessons'>;
|
course: Omit<ICourse, 'teacher' | 'students' | 'lessons'>;
|
||||||
@ -51,6 +43,7 @@ const CourseItem: React.FC<CourseItemPropsType> = ({ course, onDelete, onSetStat
|
|||||||
{(me.isAdmin || me.isTeacher) && (
|
{(me.isAdmin || me.isTeacher) && (
|
||||||
<Flex justifyContent='flex-end'>
|
<Flex justifyContent='flex-end'>
|
||||||
<Box position='absolute' zIndex='1' padding='10px'>
|
<Box position='absolute' zIndex='1' padding='10px'>
|
||||||
|
{/* Actions */}
|
||||||
<Menu isLazy>
|
<Menu isLazy>
|
||||||
<MenuButton
|
<MenuButton
|
||||||
as={IconButton}
|
as={IconButton}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import React, { useContext, useEffect } from 'react';
|
import React from 'react';
|
||||||
import { Box, Grid, Text } from '@chakra-ui/react';
|
import { Box, Grid, Text } from '@chakra-ui/react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
import { ICourse } from '@educt/interfaces';
|
import { ICourse } from '@educt/interfaces';
|
||||||
|
import { CourseStatusEnum } from '@educt/enums';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
@ -16,6 +17,7 @@ import DeleteCourseDialog from '../DeleteCourseDialog';
|
|||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
|
import { useContext, useEffect } from 'react';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
import { useDisclosure } from '@chakra-ui/hooks';
|
import { useDisclosure } from '@chakra-ui/hooks';
|
||||||
import { useToast } from '@chakra-ui/toast';
|
import { useToast } from '@chakra-ui/toast';
|
||||||
@ -25,7 +27,6 @@ import { useErrorHandler } from 'react-error-boundary';
|
|||||||
* Contexts
|
* Contexts
|
||||||
*/
|
*/
|
||||||
import { CoursesPageContext } from '@educt/contexts';
|
import { CoursesPageContext } from '@educt/contexts';
|
||||||
import { CourseStatusEnum } from '@educt/enums';
|
|
||||||
|
|
||||||
type CourseListPropsType = {
|
type CourseListPropsType = {
|
||||||
courses: Omit<ICourse, 'teacher' | 'students' | 'lessons'>[] | null;
|
courses: Omit<ICourse, 'teacher' | 'students' | 'lessons'>[] | null;
|
||||||
@ -42,6 +43,9 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
|
|||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch handler
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (me !== null) {
|
if (me !== null) {
|
||||||
courseStore.loadCourses({
|
courseStore.loadCourses({
|
||||||
@ -84,18 +88,8 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses, isLoading }) => {
|
|||||||
<>
|
<>
|
||||||
{deletingCourse && <DeleteCourseDialog onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />}
|
{deletingCourse && <DeleteCourseDialog onClose={onCloseDeleteDialog} isOpen={isOpenDeleteDialog} />}
|
||||||
{courses.length !== 0 ? (
|
{courses.length !== 0 ? (
|
||||||
<Grid
|
<Grid templateColumns='repeat(auto-fill, minmax(400px, 1fr))' gap='6'>
|
||||||
templateColumns='repeat(3, minmax(0, 1fr))'
|
{/* Render items */}
|
||||||
gap='6'
|
|
||||||
sx={{
|
|
||||||
'@media (max-width: 1280px)': {
|
|
||||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
||||||
},
|
|
||||||
'@media (max-width: 768px)': {
|
|
||||||
gridTemplateColumns: 'repeat(1, 1fr)',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{courses.map(course => (
|
{courses.map(course => (
|
||||||
<CourseItem key={course.id} course={course} onDelete={onDeleteCourse} onSetStatus={onSetStatus} />
|
<CourseItem key={course.id} course={course} onDelete={onDeleteCourse} onSetStatus={onSetStatus} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useContext } from 'react';
|
import React from 'react';
|
||||||
import { Tabs, TabList, Tab } from '@chakra-ui/react';
|
import { Tabs, TabList, Tab } from '@chakra-ui/react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,6 +11,11 @@ import { CourseStatusEnum } from '@educt/enums';
|
|||||||
*/
|
*/
|
||||||
import { CoursesPageContext } from '@educt/contexts';
|
import { CoursesPageContext } from '@educt/contexts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
type CourseStatusTabsProps = {};
|
type CourseStatusTabsProps = {};
|
||||||
|
|
||||||
const CourseStatusTabs: React.FC<CourseStatusTabsProps> = () => {
|
const CourseStatusTabs: React.FC<CourseStatusTabsProps> = () => {
|
||||||
|
|||||||
@ -16,12 +16,16 @@ import CategoryList from './components/CategoryList';
|
|||||||
import CourseStatusTabs from './components/CourseStatusTabs';
|
import CourseStatusTabs from './components/CourseStatusTabs';
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Providers
|
||||||
|
*/
|
||||||
|
import { CoursesPageContextProvider } from '@educt/providers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
import { CoursesPageContextProvider } from '@educt/providers';
|
import { useHistory } from 'react-router';
|
||||||
import { useHistory, useLocation } from 'react-router';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Courses page
|
* Courses page
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Link, Box, Flex } from '@chakra-ui/layout';
|
|
||||||
import { Link as ReactRouterLink } from 'react-router-dom';
|
import { Link as ReactRouterLink } from 'react-router-dom';
|
||||||
import { MdVideoLibrary, MdGroup, MdThumbUp } from 'react-icons/md';
|
|
||||||
import { UserCourseType } from '@educt/interfaces';
|
|
||||||
import { Tag, TagLabel } from '@chakra-ui/react';
|
import { Tag, TagLabel } from '@chakra-ui/react';
|
||||||
|
import { Text, Link, Box, Flex } from '@chakra-ui/layout';
|
||||||
|
import { MdVideoLibrary, MdGroup, MdThumbUp } from 'react-icons/md';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
|
import { UserCourseType } from '@educt/interfaces';
|
||||||
|
|
||||||
type CourseItemPropsType = {
|
type CourseItemPropsType = {
|
||||||
course: UserCourseType;
|
course: UserCourseType;
|
||||||
@ -11,15 +15,20 @@ type CourseItemPropsType = {
|
|||||||
|
|
||||||
const CourseItem: React.FC<CourseItemPropsType> = ({ course }) => {
|
const CourseItem: React.FC<CourseItemPropsType> = ({ course }) => {
|
||||||
return (
|
return (
|
||||||
<Box borderWidth='1px' borderRadius='md' p='10px' boxShadow='sm'>
|
<Flex flexDir='column' borderWidth='1px' borderRadius='lg' p='10px' boxShadow='sm' mb='3'>
|
||||||
<Flex justifyContent='space-between' alignItems='center'>
|
<Tag colorScheme='purple' borderRadius='full' alignSelf='flex-start'>
|
||||||
<Link as={ReactRouterLink} to={`course/${course.id}`} fontWeight='bold' fontSize='2xl'>
|
|
||||||
{course.title}
|
|
||||||
</Link>
|
|
||||||
<Link as={ReactRouterLink} to={`category/${course.category.id}`}>
|
|
||||||
<Tag colorScheme='purple' borderRadius='full'>
|
|
||||||
<TagLabel>{course.category.title}</TagLabel>
|
<TagLabel>{course.category.title}</TagLabel>
|
||||||
</Tag>
|
</Tag>
|
||||||
|
<Flex justifyContent='space-between' alignItems='center'>
|
||||||
|
<Link
|
||||||
|
as={ReactRouterLink}
|
||||||
|
to={`course/${course.id}`}
|
||||||
|
overflowWrap='anywhere'
|
||||||
|
fontWeight='bold'
|
||||||
|
fontSize='xl'
|
||||||
|
mr='5'
|
||||||
|
>
|
||||||
|
{course.title}
|
||||||
</Link>
|
</Link>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text>{course.description}</Text>
|
<Text>{course.description}</Text>
|
||||||
@ -37,7 +46,7 @@ const CourseItem: React.FC<CourseItemPropsType> = ({ course }) => {
|
|||||||
{course.likes_count} likes
|
{course.likes_count} likes
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Box, Text, Flex } from '@chakra-ui/layout';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
import { IMe } from '@educt/interfaces';
|
import { IMe } from '@educt/interfaces';
|
||||||
import { Box, Text, Stack, StackDivider } from '@chakra-ui/layout';
|
|
||||||
|
/**
|
||||||
|
* Components
|
||||||
|
*/
|
||||||
import CourseItem from './CourseItem';
|
import CourseItem from './CourseItem';
|
||||||
|
|
||||||
type CourseListPropsType = {
|
type CourseListPropsType = {
|
||||||
courses: IMe['courses'];
|
courses: IMe['courses'];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the course list on profile page for STUDENT role
|
|
||||||
* in "Available courses" tab
|
|
||||||
*/
|
|
||||||
const CourseList: React.FC<CourseListPropsType> = ({ courses }) => {
|
const CourseList: React.FC<CourseListPropsType> = ({ courses }) => {
|
||||||
if (courses === undefined) {
|
if (courses === undefined) {
|
||||||
return null;
|
return null;
|
||||||
@ -27,11 +31,11 @@ const CourseList: React.FC<CourseListPropsType> = ({ courses }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing='8' divider={<StackDivider />}>
|
<Flex flexDirection='column'>
|
||||||
{courses.map(course => (
|
{courses.map(course => (
|
||||||
<CourseItem course={course} key={course.id} />
|
<CourseItem course={course} key={course.id} />
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -90,11 +90,8 @@ const UpdateUserContactsForm: React.FC<UpdateUserContactsFormPropsType> = ({ con
|
|||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<Box boxShadow='sm' borderRadius='sm' borderWidth='1px' padding='20px'>
|
<Box boxShadow='sm' borderRadius='lg' borderWidth='1px' padding='20px'>
|
||||||
<Heading as='h3' size='md'>
|
<Stack spacing='5px'>
|
||||||
My Contacts
|
|
||||||
</Heading>
|
|
||||||
<Stack spacing='5px' margin='20px 0'>
|
|
||||||
<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>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -1,8 +1,21 @@
|
|||||||
import React, { useContext } from 'react';
|
import React from 'react';
|
||||||
import { Box, Heading, Stack, Flex, StackDivider, Text, Button } from '@chakra-ui/react';
|
import { Box, Heading, Stack, Flex, StackDivider, Text, Button } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
import { IUser } from '@educt/interfaces';
|
import { IUser } from '@educt/interfaces';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contexts
|
||||||
|
*/
|
||||||
import { ProfilePageContext } from '@educt/contexts';
|
import { ProfilePageContext } from '@educt/contexts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
type UserAccountInfoPropsType = {
|
type UserAccountInfoPropsType = {
|
||||||
user: IUser;
|
user: IUser;
|
||||||
};
|
};
|
||||||
@ -11,11 +24,8 @@ const UserAccountInfo: React.FC<UserAccountInfoPropsType> = ({ user }) => {
|
|||||||
const { setStatusPageView } = useContext(ProfilePageContext);
|
const { setStatusPageView } = useContext(ProfilePageContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box borderRadius='md' borderWidth='1px' padding='20px' boxShadow='sm'>
|
<Box borderRadius='lg' borderWidth='1px' padding='20px' boxShadow='sm'>
|
||||||
<Heading as='h3' size='md'>
|
<Stack spacing='10px' divider={<StackDivider />}>
|
||||||
Account information
|
|
||||||
</Heading>
|
|
||||||
<Stack spacing='10px' mt='20px' divider={<StackDivider />}>
|
|
||||||
<Flex>
|
<Flex>
|
||||||
<Box>
|
<Box>
|
||||||
<Text as='small' color='gray.500'>
|
<Text as='small' color='gray.500'>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Heading, Flex, Avatar, Text, Tabs, TabList, Tab, TabPanels, TabPanel } from '@chakra-ui/react';
|
import { Box, Heading, Flex, Avatar, Text, Tabs, TabList, Tab, TabPanels, TabPanel, Button } from '@chakra-ui/react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
@ -12,6 +12,7 @@ import CourseList from '@educt/pages/profile/components/CourseList';
|
|||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
|
import useLogoutQuery from '@educt/hooks/useLogoutQuery';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
import { useColorMode } from '@chakra-ui/color-mode';
|
import { useColorMode } from '@chakra-ui/color-mode';
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ const MainContainer: React.FC = () => {
|
|||||||
userStore: { me },
|
userStore: { me },
|
||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
const { colorMode } = useColorMode();
|
const { colorMode } = useColorMode();
|
||||||
|
const { logout } = useLogoutQuery();
|
||||||
|
|
||||||
if (me === null) return null;
|
if (me === null) return null;
|
||||||
|
|
||||||
@ -29,47 +31,17 @@ const MainContainer: React.FC = () => {
|
|||||||
<Text mt='2'>Update you contacts information or password here.</Text>
|
<Text mt='2'>Update you contacts information or password here.</Text>
|
||||||
<Flex
|
<Flex
|
||||||
alignItems='center'
|
alignItems='center'
|
||||||
|
flexDir={{ base: 'column', md: 'row' }}
|
||||||
|
textAlign={{ base: 'center', md: 'left' }}
|
||||||
mt='20'
|
mt='20'
|
||||||
sx={{
|
|
||||||
'@media (max-width: 768px)': {
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
textAlign: 'center',
|
|
||||||
mt: '10',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Avatar border={`4px solid ${colorMode === 'dark' ? '#2D3748' : '#E2E8F0'}`} size='2xl' name={me.fullname} />
|
<Avatar border={`4px solid ${colorMode === 'dark' ? '#2D3748' : '#E2E8F0'}`} size='2xl' name={me.fullname} />
|
||||||
</Box>
|
</Box>
|
||||||
<Flex
|
<Flex ml={{ md: '50' }} mt={{ base: '2', md: '0' }} flexDirection='column'>
|
||||||
ml='50'
|
|
||||||
flexDirection='column'
|
|
||||||
sx={{
|
|
||||||
'@media (max-width: 768px)': {
|
|
||||||
ml: '0',
|
|
||||||
mt: '5',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Heading as='h2'>{me.fullname}</Heading>
|
<Heading as='h2'>{me.fullname}</Heading>
|
||||||
<Flex
|
<Flex alignItems='center' flexDir={{ base: 'column', md: 'row' }}>
|
||||||
alignItems='center'
|
<Text mr='10px' mb={{ base: '2', md: '0' }}>
|
||||||
sx={{
|
|
||||||
'@media (max-width: 768px)': {
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
mr='10px'
|
|
||||||
sx={{
|
|
||||||
'@media (max-width: 768px)': {
|
|
||||||
mr: '0',
|
|
||||||
mb: '5px',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{me.email}
|
{me.email}
|
||||||
</Text>
|
</Text>
|
||||||
<UserBadge roles={me.roles} />
|
<UserBadge roles={me.roles} />
|
||||||
@ -83,7 +55,7 @@ const MainContainer: React.FC = () => {
|
|||||||
<Tab>My contacts</Tab>
|
<Tab>My contacts</Tab>
|
||||||
{me.isStudent && <Tab>Available courses</Tab>}
|
{me.isStudent && <Tab>Available courses</Tab>}
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanels>
|
<TabPanels mt='2'>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<UserAccountInfo user={me} />
|
<UserAccountInfo user={me} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
@ -98,6 +70,11 @@ const MainContainer: React.FC = () => {
|
|||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box textAlign='center' mt='3'>
|
||||||
|
<Button onClick={logout} w={{ base: 'full', sm: '150px' }}>
|
||||||
|
Sign Out
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,6 +22,7 @@ import ConfirmEmailContainer from './containers/ConfirmEmailContainer';
|
|||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
|
import { useEffect } from 'react';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
|
|||||||
@ -121,7 +121,7 @@ const UserList: React.FC<UserListPropsType> = ({ users, pagination }) => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Flex mt='auto' mb='6rem' flexDirection={{ base: 'column', sm: 'column', md: 'row' }}>
|
<Flex margin='2rem 0' flexDirection={{ base: 'column', sm: 'column', md: 'row' }}>
|
||||||
<Box textAlign={{ base: 'center', sm: 'center', md: 'left' }} mb={{ base: '2', md: '0' }}>
|
<Box textAlign={{ base: 'center', sm: 'center', md: 'left' }} mb={{ base: '2', md: '0' }}>
|
||||||
Page <b>{pagination.current_page}</b> of {pagesCount}
|
Page <b>{pagination.current_page}</b> of {pagesCount}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user