mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: some refactor & added draggable lesson
This commit is contained in:
parent
aa5bf76cc7
commit
cc5581bbc4
5
package-lock.json
generated
5
package-lock.json
generated
@ -2101,6 +2101,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"react-movable": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-movable/-/react-movable-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-dDDYm3CRnDy8YLXMyyaR2MbcQiTwhPOP+dfl3fZukiI6mN1flVatcjSozT7HXjVk2yHwBC67ZOWGVAmjY6F/dA=="
|
||||||
|
},
|
||||||
"react-refresh": {
|
"react-refresh": {
|
||||||
"version": "0.10.0",
|
"version": "0.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.10.0.tgz",
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-hook-form": "^7.12.2",
|
"react-hook-form": "^7.12.2",
|
||||||
"react-icons": "^3.11.0",
|
"react-icons": "^3.11.0",
|
||||||
|
"react-movable": "^3.0.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-spinners": "^0.11.0",
|
"react-spinners": "^0.11.0",
|
||||||
"validator": "^13.6.0",
|
"validator": "^13.6.0",
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button } from '@chakra-ui/react';
|
||||||
|
import { AiOutlineEye, AiOutlineEyeInvisible } from 'react-icons/ai';
|
||||||
|
import { CourseStatusEnum } from '@educt/enums';
|
||||||
|
import useSetCourseStatusQuery from '@educt/hooks/useSetCourseStatusQuery';
|
||||||
|
|
||||||
|
type SetCourseStatusButtonPropsType = {
|
||||||
|
courseId: string;
|
||||||
|
currentStatus: CourseStatusEnum;
|
||||||
|
loadingText?: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SetCourseStatusButton: React.FC<SetCourseStatusButtonPropsType> = ({
|
||||||
|
courseId,
|
||||||
|
currentStatus,
|
||||||
|
loadingText = 'Wait a second...',
|
||||||
|
}) => {
|
||||||
|
const [status, setStatus] = useState<keyof typeof CourseStatusEnum>(currentStatus);
|
||||||
|
const { setCourseStatus, isLoading } = useSetCourseStatusQuery();
|
||||||
|
|
||||||
|
const handleChangeStatus = async (newStatus: CourseStatusEnum) => {
|
||||||
|
await setCourseStatus(courseId, newStatus);
|
||||||
|
setStatus(newStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Components: { [k in keyof typeof CourseStatusEnum]: any } = {
|
||||||
|
PUBLISHED: (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleChangeStatus(CourseStatusEnum.DRAFT)}
|
||||||
|
isLoading={isLoading}
|
||||||
|
loadingText={loadingText}
|
||||||
|
leftIcon={<AiOutlineEyeInvisible />}
|
||||||
|
>
|
||||||
|
Mark as Draft
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
DRAFT: (
|
||||||
|
<Button
|
||||||
|
onClick={() => handleChangeStatus(CourseStatusEnum.PUBLISHED)}
|
||||||
|
isLoading={isLoading}
|
||||||
|
loadingText={loadingText}
|
||||||
|
leftIcon={<AiOutlineEye />}
|
||||||
|
>
|
||||||
|
Publish course
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Components[status];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SetCourseStatusButton;
|
||||||
3
src/components/SetCourseStatusButton/index.ts
Normal file
3
src/components/SetCourseStatusButton/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import SetCourseStatusButton from './SetCourseStatusButton';
|
||||||
|
|
||||||
|
export default SetCourseStatusButton;
|
||||||
49
src/hooks/useSetCourseStatusQuery.ts
Normal file
49
src/hooks/useSetCourseStatusQuery.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { CourseStatusEnum } from '@educt/enums';
|
||||||
|
import { useErrorHandler } from 'react-error-boundary';
|
||||||
|
import useAsync from './useAsync';
|
||||||
|
import { useRootStore } from './useRootStore';
|
||||||
|
import { useToast } from '@chakra-ui/toast';
|
||||||
|
|
||||||
|
type QueryResponseDataType = {} | undefined;
|
||||||
|
export type SetCourseStatusFnType = (
|
||||||
|
id: string,
|
||||||
|
newStatus: CourseStatusEnum
|
||||||
|
) => Promise<QueryResponseDataType | undefined>;
|
||||||
|
|
||||||
|
const useSetCourseStatusQuery = () => {
|
||||||
|
const { courseStore } = useRootStore();
|
||||||
|
const toast = useToast();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
|
const setCourseStatus = async (id: string, newStatus: CourseStatusEnum) => {
|
||||||
|
try {
|
||||||
|
const result = await courseStore.setCourseStatus(id, newStatus);
|
||||||
|
switch (newStatus) {
|
||||||
|
case CourseStatusEnum.PUBLISHED:
|
||||||
|
toast({ title: 'Course has been published.', status: 'success' });
|
||||||
|
break;
|
||||||
|
case CourseStatusEnum.DRAFT:
|
||||||
|
toast({ title: 'Course has been marked as draft.', status: 'info' });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result.data;
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.response) {
|
||||||
|
toast({ title: error.message, status: 'error' });
|
||||||
|
} else {
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { execute, ...state } = useAsync<QueryResponseDataType, Parameters<SetCourseStatusFnType>>(setCourseStatus);
|
||||||
|
|
||||||
|
return { setCourseStatus: execute, ...state };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSetCourseStatusQuery;
|
||||||
@ -50,6 +50,7 @@ export interface ICategory {
|
|||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
color?: ColorType | undefined | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export type CategoryItemPropsType = {
|
|||||||
|
|
||||||
const CategoryItem: React.FC<CategoryItemPropsType> = ({ category }) => {
|
const CategoryItem: React.FC<CategoryItemPropsType> = ({ category }) => {
|
||||||
const { selectedCategory, setSelectedCategory } = useContext(CoursesPageContext);
|
const { selectedCategory, setSelectedCategory } = useContext(CoursesPageContext);
|
||||||
|
const isSelected = selectedCategory?.id === category.id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle click on change category
|
* Handle click on change category
|
||||||
@ -31,10 +32,12 @@ const CategoryItem: React.FC<CategoryItemPropsType> = ({ category }) => {
|
|||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
borderRadius='full'
|
borderRadius='full'
|
||||||
variant={selectedCategory?.id === category.id ? 'solid' : 'outline'}
|
variant={isSelected ? 'solid' : 'outline'}
|
||||||
colorScheme='blue'
|
boxShadow={`inset 0 0 0px 1px ${category.color?.hex}`}
|
||||||
|
color={isSelected ? 'white' : category.color?.hex}
|
||||||
cursor='pointer'
|
cursor='pointer'
|
||||||
transition='all .1s'
|
transition='all .1s'
|
||||||
|
bg={isSelected ? `${category.color?.hex}` : 'none'}
|
||||||
_hover={{ opacity: '.8' }}
|
_hover={{ opacity: '.8' }}
|
||||||
onClick={onCategoryChange}
|
onClick={onCategoryChange}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { DeleteIcon, EditIcon } from '@chakra-ui/icons';
|
|||||||
*/
|
*/
|
||||||
import { ICourse } from '@educt/interfaces';
|
import { ICourse } from '@educt/interfaces';
|
||||||
import { CourseStatusEnum } from '@educt/enums';
|
import { CourseStatusEnum } from '@educt/enums';
|
||||||
|
import { SetCourseStatusFnType } from '@educt/hooks/useSetCourseStatusQuery';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Components
|
* Components
|
||||||
@ -29,7 +30,7 @@ import { useRootStore } from '@educt/hooks/useRootStore';
|
|||||||
export type CourseItemPropsType = {
|
export type CourseItemPropsType = {
|
||||||
course: Omit<ICourse, 'teacher' | 'students' | 'lessons'>;
|
course: Omit<ICourse, 'teacher' | 'students' | 'lessons'>;
|
||||||
onDelete: (course: Pick<ICourse, 'id' | 'title'>) => void;
|
onDelete: (course: Pick<ICourse, 'id' | 'title'>) => void;
|
||||||
onSetStatus: (id: string, status: CourseStatusEnum) => Promise<void>;
|
onSetStatus: SetCourseStatusFnType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CourseItem: React.FC<CourseItemPropsType> = ({ course, onDelete, onSetStatus }) => {
|
const CourseItem: React.FC<CourseItemPropsType> = ({ course, onDelete, onSetStatus }) => {
|
||||||
|
|||||||
@ -20,13 +20,12 @@ import LoadingList from '@educt/components/LoadingList';
|
|||||||
import { useContext, useEffect } from 'react';
|
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 { useErrorHandler } from 'react-error-boundary';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contexts
|
* Contexts
|
||||||
*/
|
*/
|
||||||
import { CoursesPageContext } from '@educt/contexts';
|
import { CoursesPageContext } from '@educt/contexts';
|
||||||
|
import useSetCourseStatusQuery from '@educt/hooks/useSetCourseStatusQuery';
|
||||||
|
|
||||||
type CourseListPropsType = {
|
type CourseListPropsType = {
|
||||||
render: React.FC<CourseItemPropsType>;
|
render: React.FC<CourseItemPropsType>;
|
||||||
@ -39,8 +38,7 @@ const CourseList: React.FC<CourseListPropsType> = ({ render: Item }) => {
|
|||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
const { selectedCategory, courseStatus, deletingCourse, setDeletingCourse } = useContext(CoursesPageContext);
|
const { selectedCategory, courseStatus, deletingCourse, setDeletingCourse } = useContext(CoursesPageContext);
|
||||||
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
|
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
|
||||||
const handleError = useErrorHandler();
|
const { setCourseStatus } = useSetCourseStatusQuery();
|
||||||
const toast = useToast();
|
|
||||||
const { courses, isLoading } = courseStore;
|
const { courses, isLoading } = courseStore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,22 +53,6 @@ const CourseList: React.FC<CourseListPropsType> = ({ render: Item }) => {
|
|||||||
}
|
}
|
||||||
}, [courseStore, selectedCategory, courseStatus]);
|
}, [courseStore, selectedCategory, courseStatus]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle update course status
|
|
||||||
*/
|
|
||||||
const onSetStatus = async (id: string, status: CourseStatusEnum) => {
|
|
||||||
try {
|
|
||||||
await courseStore.setCourseStatus(id, status);
|
|
||||||
toast({ title: 'Status updated.', status: 'info' });
|
|
||||||
} catch (error: any) {
|
|
||||||
if (error.response) {
|
|
||||||
toast({ title: error.message, status: 'error' });
|
|
||||||
} else {
|
|
||||||
handleError(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (courses === null || isLoading) return <LoadingList />;
|
if (courses === null || isLoading) return <LoadingList />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -88,7 +70,7 @@ const CourseList: React.FC<CourseListPropsType> = ({ render: Item }) => {
|
|||||||
setDeletingCourse(course);
|
setDeletingCourse(course);
|
||||||
onOpenDeleteDialog();
|
onOpenDeleteDialog();
|
||||||
}}
|
}}
|
||||||
onSetStatus={onSetStatus}
|
onSetStatus={setCourseStatus}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -1,91 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import moment from 'moment';
|
|
||||||
import { Flex, Box, Text, Icon, Button, IconButton } from '@chakra-ui/react';
|
|
||||||
import { DeleteIcon } from '@chakra-ui/icons';
|
|
||||||
import { MdTimer, MdAttachment } from 'react-icons/md';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Types
|
|
||||||
*/
|
|
||||||
import type { ILesson } from '@educt/interfaces';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hooks
|
|
||||||
*/
|
|
||||||
import { useHistory } from 'react-router';
|
|
||||||
import { useColorModeValue } from '@chakra-ui/color-mode';
|
|
||||||
|
|
||||||
export type LessonItemPropsType = {
|
|
||||||
lesson: ILesson;
|
|
||||||
};
|
|
||||||
|
|
||||||
const LessonItem: React.FC<LessonItemPropsType> = ({ lesson }) => {
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const handleEditLesson = (): void => history.push(`/lessons/edit/${lesson.id}`);
|
|
||||||
const handleDeleteLesson = (): void => undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box key={lesson.title} p='3' borderBottomWidth='1px'>
|
|
||||||
<Flex flexDirection={{ base: 'column', md: 'row' }} justifyContent='space-between'>
|
|
||||||
<Flex
|
|
||||||
flexDirection={{ base: 'column', md: 'row' }}
|
|
||||||
textAlign={{ base: 'center', md: 'left' }}
|
|
||||||
alignItems='center'
|
|
||||||
mr={{ md: '4' }}
|
|
||||||
>
|
|
||||||
<Box mr='3'>
|
|
||||||
<Box bg={lesson.color?.hex || 'gray.100'} minH='50px' minW='50px' borderRadius='md' />
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fontSize='lg' fontWeight='medium' mt={{ base: '3', md: '0' }}>
|
|
||||||
<Text as='span' color='gray.500' mr='1' fontSize='md' fontWeight='normal'>
|
|
||||||
1.
|
|
||||||
</Text>
|
|
||||||
{lesson.title}
|
|
||||||
</Text>
|
|
||||||
<Text mt='2' color='gray.500'>
|
|
||||||
Sergey Yarkov
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
<Box>
|
|
||||||
<Flex flexDirection='column' alignItems={{ base: 'center' }} mt={{ base: '4' }}>
|
|
||||||
<Flex flexDirection={{ base: 'column', md: 'row' }}>
|
|
||||||
<Button onClick={handleEditLesson} size='md' p='0 60px' mr='1'>
|
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
<IconButton
|
|
||||||
onClick={handleDeleteLesson}
|
|
||||||
aria-label='Delete lesson'
|
|
||||||
variant='ghost'
|
|
||||||
colorScheme='red'
|
|
||||||
icon={<DeleteIcon />}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent='flex-end' mt={{ base: '2' }}>
|
|
||||||
<Text as='span' color='gray.500' mr='3'>
|
|
||||||
<Text as='span' verticalAlign='middle' mr='1'>
|
|
||||||
<Icon as={MdTimer} />
|
|
||||||
</Text>
|
|
||||||
<Text as='small' verticalAlign='middle'>
|
|
||||||
~{moment.duration(lesson.duration, 'minutes').humanize()}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
<Text as='span' color='gray.500'>
|
|
||||||
<Text as='span' verticalAlign='middle' mr='1'>
|
|
||||||
<Icon as={MdAttachment} />
|
|
||||||
</Text>
|
|
||||||
<Text as='small' verticalAlign='middle'>
|
|
||||||
4 attachments
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LessonItem;
|
|
||||||
@ -1,19 +1,24 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Box, Stack, Text, Button, StackDivider } from '@chakra-ui/react';
|
import moment from 'moment';
|
||||||
|
import { List, arrayMove } from 'react-movable';
|
||||||
|
import { Flex, Box, Stack, Text, IconButton, Icon, Button } from '@chakra-ui/react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
import type { LessonItemPropsType } from './LessonItem';
|
import type { ICourse, ILesson } from '@educt/interfaces';
|
||||||
import type { ICourse } from '@educt/interfaces';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
|
|
||||||
|
import { useColorMode } from '@chakra-ui/react';
|
||||||
|
import { DeleteIcon } from '@chakra-ui/icons';
|
||||||
|
import { MdTimer, MdAttachment } from 'react-icons/md';
|
||||||
|
import { DragHandleIcon } from '@chakra-ui/icons';
|
||||||
|
|
||||||
type LessonListPropsType = {
|
type LessonListPropsType = {
|
||||||
render: React.FC<LessonItemPropsType>;
|
|
||||||
course: Omit<ICourse, 'students_count' | 'likes_count' | 'lessons_count'>;
|
course: Omit<ICourse, 'students_count' | 'likes_count' | 'lessons_count'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,16 +36,103 @@ const CreateLessonButton: React.FC<{ id: string }> = ({ id }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const LessonList: React.FC<LessonListPropsType> = ({ render: Item, course }) => {
|
const LessonList: React.FC<LessonListPropsType> = ({ course }) => {
|
||||||
|
const history = useHistory();
|
||||||
|
const [lessons, setLessons] = useState<ILesson[]>(course.lessons);
|
||||||
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
|
const handleEditLesson = (id: string): void => history.push(`/lessons/edit/${id}`);
|
||||||
|
const handleDeleteLesson = (id: string): void => undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{course.lessons.length !== 0 ? (
|
{course.lessons.length !== 0 ? (
|
||||||
<Box>
|
<Box>
|
||||||
<Stack mt='4'>
|
<List
|
||||||
{course.lessons.map(lesson => (
|
transitionDuration={100}
|
||||||
<Item key={lesson.id} lesson={lesson} />
|
lockVertically
|
||||||
))}
|
values={lessons}
|
||||||
</Stack>
|
onChange={({ oldIndex, newIndex }) => setLessons(arrayMove(lessons, oldIndex, newIndex))}
|
||||||
|
renderList={({ children, props }) => <Stack {...props}>{children}</Stack>}
|
||||||
|
renderItem={({ value: lesson, props, isDragged, index }) => (
|
||||||
|
<Box
|
||||||
|
{...props}
|
||||||
|
zIndex='1'
|
||||||
|
bg={colorMode === 'dark' ? 'gray.700' : 'gray.50'}
|
||||||
|
borderRadius='lg'
|
||||||
|
position='relative'
|
||||||
|
p='3'
|
||||||
|
borderBottomWidth='1px'
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
data-movable-handle
|
||||||
|
cursor={isDragged ? 'grabbing' : 'grab'}
|
||||||
|
position='absolute'
|
||||||
|
bottom='50%'
|
||||||
|
left='-20px'
|
||||||
|
>
|
||||||
|
<DragHandleIcon />
|
||||||
|
</Box>
|
||||||
|
<Flex flexDirection={{ base: 'column', md: 'row' }} justifyContent='space-between'>
|
||||||
|
<Flex
|
||||||
|
flexDirection={{ base: 'column', md: 'row' }}
|
||||||
|
textAlign={{ base: 'center', md: 'left' }}
|
||||||
|
alignItems='center'
|
||||||
|
mr={{ md: '4' }}
|
||||||
|
>
|
||||||
|
<Box mr='3'>
|
||||||
|
<Box bg={lesson.color?.hex || 'gray.100'} minH='50px' minW='50px' borderRadius='md' />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fontSize='lg' fontWeight='medium' mt={{ base: '3', md: '0' }}>
|
||||||
|
<Text as='span' color='gray.500' mr='1' fontSize='md' fontWeight='normal'>
|
||||||
|
{index}.
|
||||||
|
</Text>
|
||||||
|
{lesson.title}
|
||||||
|
</Text>
|
||||||
|
<Text mt='2' color='gray.500'>
|
||||||
|
Sergey Yarkov
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
<Box>
|
||||||
|
<Flex flexDirection='column' alignItems={{ base: 'center' }} mt={{ base: '4' }}>
|
||||||
|
<Flex flexDirection={{ base: 'column', md: 'row' }}>
|
||||||
|
<Button onClick={() => handleEditLesson(lesson.id)} size='md' p='0 60px' mr='1'>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => handleDeleteLesson(lesson.id)}
|
||||||
|
aria-label='Delete lesson'
|
||||||
|
variant='ghost'
|
||||||
|
colorScheme='red'
|
||||||
|
icon={<DeleteIcon />}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Flex justifyContent='flex-end' mt={{ base: '2' }}>
|
||||||
|
<Text as='span' color='gray.500' mr='3'>
|
||||||
|
<Text as='span' verticalAlign='middle' mr='1'>
|
||||||
|
<Icon as={MdTimer} />
|
||||||
|
</Text>
|
||||||
|
<Text as='small' verticalAlign='middle'>
|
||||||
|
~{moment.duration(lesson.duration, 'minutes').humanize()}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
<Text as='span' color='gray.500'>
|
||||||
|
<Text as='span' verticalAlign='middle' mr='1'>
|
||||||
|
<Icon as={MdAttachment} />
|
||||||
|
</Text>
|
||||||
|
<Text as='small' verticalAlign='middle'>
|
||||||
|
4 attachments
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<CreateLessonButton id={course.id} />
|
<CreateLessonButton id={course.id} />
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import LessonList from './LessonList';
|
import LessonList from './LessonList';
|
||||||
import LessonItem from './LessonItem';
|
|
||||||
|
|
||||||
export { LessonList, LessonItem };
|
export default LessonList;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import { Redirect } from 'react-router-dom';
|
import { Redirect } from 'react-router-dom';
|
||||||
import { Box, Heading, Text, Tab, TabList, Flex, Tabs, TabPanels, TabPanel } from '@chakra-ui/react';
|
import { Box, Heading, Text, Tab, TabList, Flex, Tabs, TabPanels, TabPanel } from '@chakra-ui/react';
|
||||||
|
|
||||||
@ -13,7 +14,8 @@ import type { IPageProps } from '@educt/interfaces';
|
|||||||
*/
|
*/
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
import { EditCourseForm } from '@educt/components/Forms/CourseForm';
|
import { EditCourseForm } from '@educt/components/Forms/CourseForm';
|
||||||
import { LessonList, LessonItem } from './components/LessonList';
|
import SetCourseStatusButton from '@educt/components/SetCourseStatusButton';
|
||||||
|
import LessonList from './components/LessonList';
|
||||||
import { StudentList, StudentItem } from './components/StudentList';
|
import { StudentList, StudentItem } from './components/StudentList';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,12 +46,14 @@ const EditCoursePage: React.FC<IPageProps> = () => {
|
|||||||
<Helmet>
|
<Helmet>
|
||||||
<title>Editing {course.title}</title>
|
<title>Editing {course.title}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<Flex>
|
<Flex justifyContent='space-between' alignItems='center' flexWrap='wrap'>
|
||||||
<Box>
|
<Box mr='3'>
|
||||||
<Heading as='h1'>Course editor</Heading>
|
<Heading as='h1'>Course editor</Heading>
|
||||||
<Text mt='2'>{course.title}</Text>
|
<Text mt='2'>{course.title}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box></Box>
|
<Box ml='auto' alignSelf='flex-end' justifySelf='flex-end' mt={{ base: '7', md: '0' }}>
|
||||||
|
<SetCourseStatusButton courseId={course.id} currentStatus={course.status} />
|
||||||
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Box>
|
<Box>
|
||||||
<Tabs isLazy mt='8'>
|
<Tabs isLazy mt='8'>
|
||||||
@ -74,7 +78,7 @@ const EditCoursePage: React.FC<IPageProps> = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<LessonList render={LessonItem} course={course} />
|
<LessonList course={course} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<StudentList render={StudentItem} students={course.students} />
|
<StudentList render={StudentItem} students={course.students} />
|
||||||
@ -86,4 +90,4 @@ const EditCoursePage: React.FC<IPageProps> = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EditCoursePage;
|
export default observer(EditCoursePage);
|
||||||
|
|||||||
@ -30,7 +30,13 @@ const CourseItem: React.FC<CourseItemPropsType> = ({ course }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Tag variant='subtle' colorScheme='green' borderRadius='full' alignSelf='flex-start'>
|
<Tag
|
||||||
|
color='white'
|
||||||
|
boxShadow={`inset 0 0 0px 1px ${course.category.color?.hex}`}
|
||||||
|
bg={course.category.color?.hex}
|
||||||
|
borderRadius='full'
|
||||||
|
alignSelf='flex-start'
|
||||||
|
>
|
||||||
<TagLabel>{course.category.title}</TagLabel>
|
<TagLabel>{course.category.title}</TagLabel>
|
||||||
</Tag>
|
</Tag>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user