From 9d67c14fee5b76492234b21324c41771b94463fb Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Sat, 18 Dec 2021 00:17:56 +0300 Subject: [PATCH] feat: rendering course page --- src/helpers/index.ts | 7 +- src/pages/404/404.tsx | 2 +- src/pages/auth/auth.tsx | 2 +- src/pages/course/course.tsx | 150 ++++++++------------------- src/pages/course/index.tsx | 195 ++++++++++++++++++++++++++++++++++++ 5 files changed, 246 insertions(+), 110 deletions(-) create mode 100644 src/pages/course/index.tsx diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 8b65582..9cf8a9e 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,5 +1,5 @@ import { UserRoleEnum } from '@educt/enums'; -import { IUserRole } from '@educt/interfaces'; +import { ICourse, IUserRole, UserCourseType } from '@educt/interfaces'; export function userHasRoles(userRoles: IUserRole[], roles: UserRoleEnum[]): boolean { for (let i = 0; i < roles.length; i += 1) { @@ -97,3 +97,8 @@ export function arrayMove(array: T[], from: number, to: number) { export function getDirtyFields(dirtyFields: { [key: string]: boolean | undefined }, data: T) { return Object.fromEntries(Object.keys(dirtyFields).map(k => [k, data[k as keyof T]])); } + +export function userHasCourse(findingCourse: ICourse, userCourses?: Array | undefined) { + if (!userCourses) return true; + return userCourses.some(course => course.id === findingCourse.id); +} diff --git a/src/pages/404/404.tsx b/src/pages/404/404.tsx index 4dc3dc3..2d67e41 100644 --- a/src/pages/404/404.tsx +++ b/src/pages/404/404.tsx @@ -1,7 +1,7 @@ import React from 'react'; import Helmet from 'react-helmet'; import { Link as ReactRouterLink } from 'react-router-dom'; -import { Flex, Heading, Text, Link, Icon } from '@chakra-ui/react'; +import { Heading, Text, Link, Icon } from '@chakra-ui/react'; import { MdError } from 'react-icons/md'; /** diff --git a/src/pages/auth/auth.tsx b/src/pages/auth/auth.tsx index 44b7759..1a28914 100644 --- a/src/pages/auth/auth.tsx +++ b/src/pages/auth/auth.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Helmet from 'react-helmet'; import { Redirect } from 'react-router-dom'; import { observer } from 'mobx-react'; -import { Box, Heading } from '@chakra-ui/react'; +import { Box } from '@chakra-ui/react'; /** * Types diff --git a/src/pages/course/course.tsx b/src/pages/course/course.tsx index bf4d9c5..c99b48c 100644 --- a/src/pages/course/course.tsx +++ b/src/pages/course/course.tsx @@ -1,26 +1,9 @@ import React from 'react'; -import { Redirect, useHistory } from 'react-router-dom'; -import { - Flex, - Box, - Text, - Tag, - Heading, - Icon, - Button, - Grid, - GridItem, - Image, - Divider, - AvatarGroup, - LinkBox, - LinkOverlay, - Avatar, - useColorModeValue, -} from '@chakra-ui/react'; -import { MdThumbUpOffAlt, MdPlayCircleOutline, MdOutlineGroup, MdOutlineVideoLibrary } from 'react-icons/md'; -import { Tabs, TabList, TabPanels, Tab, TabPanel, Stack } from '@chakra-ui/react'; -import { Link as ReactRouterLink } from 'react-router-dom'; +import * as helpres from '@educt/helpers'; +import { Redirect } from 'react-router-dom'; +import { Flex, Box, Text, Heading, Button, Grid, GridItem, Divider, AvatarGroup, Avatar } from '@chakra-ui/react'; +import { MdPlayCircleOutline } from 'react-icons/md'; +import { Tabs, TabList, TabPanels, Tab, TabPanel } from '@chakra-ui/react'; /** * Types @@ -30,78 +13,67 @@ import { IPageProps } from '@educt/interfaces'; /** * Components */ +import { CourseBackgroundImage, CourseHeader, CourseHeading, CourseInfo, LessonItem, LessonList } from '.'; import { PageWrapper, PageContent } from '@educt/components/PageElements'; +import LoadingPage from '@educt/components/LoadingPage'; /** * Hooks */ -import { useParams } from 'react-router-dom'; +import { useHistory, useParams } from 'react-router-dom'; import { useFetchCourse } from '@educt/hooks/queries'; -import LoadingPage from '@educt/components/LoadingPage'; -import moment from 'moment'; +import { useRootStore } from '@educt/hooks/useRootStore'; +import { observer } from 'mobx-react'; const CoursePage: React.FC = () => { + const { + userStore: { me }, + } = useRootStore(); const params = useParams<{ id: string }>(); const { error, data: course, isLoading } = useFetchCourse(params.id); const history = useHistory(); - const color = useColorModeValue('gray.50', 'gray.700'); if (error?.response?.status === 404) { return ; } - if (!course || isLoading) return ; + if (!me || !course || isLoading) return ; - const onWatchCourse = () => history.push(`/lesson/${course.lessons[0].id}`); + const isCourseAvailable = helpres.userHasCourse(course, me.courses); + const isEmptyLessons = course.lessons.length === 0; + const isEmptyStudents = course.students.length === 0; + + const handleWatchCourse = () => history.push(`/lesson/${course.lessons[0].id}`); return ( - - - - {course.title} - - - {course.teacher.fullname} •  - - {course.category.title} - - - - - - - - {course.lessons_count} - - - - {course.students_count} - - - - - + + + + + Last update: {new Date(course.updated_at).toLocaleDateString()} - - + @@ -117,7 +89,7 @@ const CoursePage: React.FC = () => { {course.description} - {course.students.length !== 0 ? ( + {!isEmptyStudents ? ( Students: @@ -152,7 +124,9 @@ const CoursePage: React.FC = () => { sunt corporis deserunt impedit natus consequuntur? + + @@ -162,46 +136,8 @@ const CoursePage: React.FC = () => { {course.lessons_count} lessons - {course.lessons.length !== 0 ? ( - - {course.lessons.map(lesson => { - return ( - - - - - {lesson.title} - - - {moment.duration(lesson.duration).asMinutes()}min - - ); - })} - + {!isEmptyLessons ? ( + ) : ( @@ -218,4 +154,4 @@ const CoursePage: React.FC = () => { ); }; -export default CoursePage; +export default observer(CoursePage); diff --git a/src/pages/course/index.tsx b/src/pages/course/index.tsx new file mode 100644 index 0000000..4ee2913 --- /dev/null +++ b/src/pages/course/index.tsx @@ -0,0 +1,195 @@ +import React from 'react'; +import moment from 'moment'; +import * as helpers from '@educt/helpers'; +import { Link as ReactRouterLink } from 'react-router-dom'; +import { + Box, + BoxProps, + Flex, + FlexProps, + Button, + Icon, + Image, + Text, + Heading, + Tag, + Stack, + ImageProps, + LinkBox, + LinkOverlay, + LinkBoxProps, + StackProps, +} from '@chakra-ui/react'; +import { + MdThumbUpOffAlt, + MdPlayCircleOutline, + MdOutlineGroup, + MdOutlineVideoLibrary, + MdOutlineLock, +} from 'react-icons/md'; + +/** + * Types + */ +import type { ICourse, ILesson } from '@educt/interfaces'; + +/** + * Hooks + */ +import { useColorModeValue } from '@chakra-ui/react'; +import { useRootStore } from '@educt/hooks/useRootStore'; + +export const CourseHeader: React.FC = props => ( + + {props.children} + +); + +interface ICourseHeadingProps extends BoxProps { + title: string; + teacher: ICourse['teacher']; + category: ICourse['category']; +} +export const CourseHeading: React.FC = props => { + const { title, teacher, category, ...boxProps } = props; + + return ( + + + {title} + + + {teacher.fullname} •  + + {category.title} + + + + ); +}; + +interface ICourseInfoProps extends BoxProps { + id: string; + lessonsCount: string; + studentsCount: string; + likesCount: string; +} +export const CourseInfo: React.FC = props => { + const { id, lessonsCount, studentsCount, likesCount, ...boxProps } = props; + + const handleLikeCourse = (id: string) => () => { + // TODO set like request + console.log(`like course: ${id}`); + }; + + return ( + + + + + {lessonsCount} + + + + + {studentsCount} + + + + + + ); +}; + +export const CourseBackgroundImage: React.FC = props => { + return ( + + ); +}; + +interface ILessonListProps extends StackProps { + course: ICourse; + render: React.FC; +} +export const LessonList: React.FC = props => { + const { course, render: Item, ...stackProps } = props; + const { + userStore: { me }, + } = useRootStore(); + const bg = useColorModeValue('gray.50', 'gray.700'); + + if (!me) return null; + + const isCourseAvailable = helpers.userHasCourse(course, me.courses); + + return ( + + {course.lessons.map(lesson => ( + + ))} + + ); +}; + +interface ILessonItemProps extends LinkBoxProps { + id: string; + title: string; + duration: string; + isDisabled?: boolean | undefined; +} +export const LessonItem: React.FC = props => { + const { id, title, duration, isDisabled, ...linkBoxProps } = props; + return ( + + + + {!isDisabled ? ( + + {title} + + ) : ( + + {title} + + )} + + {moment.duration(duration).asMinutes()}min + + ); +};