diff --git a/src/components/ConfirmDialog/ConfirmDialog.tsx b/src/components/ConfirmDialog/ConfirmDialog.tsx new file mode 100644 index 0000000..156b5e1 --- /dev/null +++ b/src/components/ConfirmDialog/ConfirmDialog.tsx @@ -0,0 +1,63 @@ +import React, { useState } from 'react'; +import { + AlertDialog, + AlertDialogBody, + AlertDialogOverlay, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + Button, +} from '@chakra-ui/react'; + +import { useRef } from 'react'; + +export interface IConfirmDialogProps { + title?: string; + message?: string; + confirmMessage?: string; + isOpen: boolean; + isLoading?: boolean; + onClose: (isConfirmed: boolean) => void; +} + +const ConfirmDialog: React.FC = ({ + title, + message, + confirmMessage, + isOpen, + isLoading, + onClose, +}) => { + const cancelRef = useRef(null); + + return ( + onClose(false)}> + + + + {title || 'Confirm that action'} + + + {message || 'Would you like to perform this action?'} + + + + + + + + + ); +}; + +export default ConfirmDialog; diff --git a/src/contexts/index.ts b/src/contexts/index.ts index 4a1b5e4..6174192 100644 --- a/src/contexts/index.ts +++ b/src/contexts/index.ts @@ -14,8 +14,8 @@ export const ChangeEmailPageContext = createContext( export const UsersPageContext = createContext({ searchingRole: undefined, setSearchingRole: () => {}, - loading: false, - setLoading: () => {}, + searchingPage: 1, + setSearchingPage: () => {}, search: undefined, setSearch: () => {}, editingUser: undefined, diff --git a/src/pages/courses/components/CourseList/CourseItem.tsx b/src/pages/courses/components/CourseList/CourseItem.tsx index 74cf85e..f352282 100644 --- a/src/pages/courses/components/CourseList/CourseItem.tsx +++ b/src/pages/courses/components/CourseList/CourseItem.tsx @@ -92,6 +92,7 @@ const CourseItem: React.FC = ({ course, onDelete, onSetStat to={`/course/${course.id}`} as={ReactRouterLink} display='block' + h='full' textDecoration='none' _visited={{ textDecoration: 'none' }} _hover={{ textDecoration: 'none' }} diff --git a/src/pages/courses/components/CourseList/CourseList.tsx b/src/pages/courses/components/CourseList/CourseList.tsx index 702b6a0..62432ac 100644 --- a/src/pages/courses/components/CourseList/CourseList.tsx +++ b/src/pages/courses/components/CourseList/CourseList.tsx @@ -56,15 +56,6 @@ const CourseList: React.FC = ({ render: Item }) => { } }, [courseStore, selectedCategory, courseStatus]); - /** - * Handle delete course - * Set deleting course to context and open dialog - */ - const onDeleteCourse = (course: Pick) => { - setDeletingCourse(course); - onOpenDeleteDialog(); - }; - /** * Handle update course status */ @@ -81,29 +72,38 @@ const CourseList: React.FC = ({ render: Item }) => { } }; - /** - * Loading - */ if (courses === null || isLoading) return ; - /** - * No courses - */ - if (courses.length === 0) - return ( - - There are no courses in this category {courseStatus && 'or with this status'}. - - ); - return ( - {deletingCourse && } - - {courses.map(course => ( - - ))} - + {deletingCourse && ( + + )} + {courses.length !== 0 ? ( + + {courses.map(course => ( + { + setDeletingCourse(course); + onOpenDeleteDialog(); + }} + onSetStatus={onSetStatus} + /> + ))} + + ) : ( + + + There are no courses  + {!selectedCategory && !courseStatus + ? 'in the system yet' + : `in this category${courseStatus ? ' or with this status' : ''}`} + . + + + )} ); }; diff --git a/src/pages/courses/components/DeleteCourseDialog/DeleteCourseDialog.tsx b/src/pages/courses/components/DeleteCourseDialog/DeleteCourseDialog.tsx index 74bd58c..d59ff3a 100644 --- a/src/pages/courses/components/DeleteCourseDialog/DeleteCourseDialog.tsx +++ b/src/pages/courses/components/DeleteCourseDialog/DeleteCourseDialog.tsx @@ -10,6 +10,11 @@ import { useToast, } from '@chakra-ui/react'; +/** + * Components + */ +import ConfirmDialog from '@educt/components/ConfirmDialog/ConfirmDialog'; + /** * Contexts */ @@ -18,87 +23,62 @@ import { CoursesPageContext } from '@educt/contexts'; /** * Hooks */ -import useIsMountedRef from '@educt/hooks/useIsMountedRef'; import { useErrorHandler } from 'react-error-boundary'; import { useRootStore } from '@educt/hooks/useRootStore'; +import { ICourse } from '@educt/interfaces'; -type DeleteCourseDialogProps = { +type DeleteCourseDialogPropsType = { + course: Pick; onClose: () => void; isOpen: boolean; }; -const DeleteCourseDialog: React.FC = ({ onClose, isOpen }) => { +const DeleteCourseDialog: React.FC = ({ onClose, isOpen, course }) => { const { courseStore } = useRootStore(); - const { deletingCourse, setDeletingCourse } = useContext(CoursesPageContext); - const isMountedRef = useIsMountedRef(); + const { setDeletingCourse } = useContext(CoursesPageContext); const [isLoading, setIsLoading] = useState(false); - const cancelRef = useRef(null); const handleError = useErrorHandler(); const toast = useToast(); /** * Delete course handler * + * @param isConfirmed Is dialog confirmed * @param id Course id */ - const onDelete = async (id: string) => { - try { - setIsLoading(true); - await courseStore.deleteCourse(id); - toast({ title: 'Course deleted.', status: 'info' }); - onClose(); - setDeletingCourse(undefined); - } catch (error: any) { - if (error.response) { - toast({ title: error.message, status: 'error' }); - } else { - handleError(error); - } - } finally { - if (isMountedRef.current) { + const deleteCourseHandler = async (isConfirmed: boolean, id: string) => { + if (isConfirmed) { + try { + setIsLoading(true); + await courseStore.deleteCourse(id); + toast({ title: 'Course deleted.', status: 'info' }); + } catch (error: any) { + if (error.response) { + toast({ title: error.message, status: 'error' }); + } else { + handleError(error); + } + } finally { setIsLoading(false); + setDeletingCourse(undefined); } } + + /** + * Close dialog + */ + onClose(); }; - if (deletingCourse === undefined) return null; - return ( - <> - - - - - Confirm deleting - - - The course with title "{deletingCourse.title}" will be removed from the system. After deleting a course, - the lessons in it will also be deleted. - - - - - - - - - + deleteCourseHandler(isConfirmed, course.id)} + title='Confirm deleting' + message={`The course with title "${course.title}" will be removed from the system. After deleting a course, the lessons in it will also be deleted.`} + confirmMessage='Delete' + /> ); }; diff --git a/src/pages/courses/create/components/CreateCourseForm.tsx b/src/pages/courses/create/components/CreateCourseForm.tsx index a96a5f2..c8c6e82 100644 --- a/src/pages/courses/create/components/CreateCourseForm.tsx +++ b/src/pages/courses/create/components/CreateCourseForm.tsx @@ -45,9 +45,9 @@ type CreateCourseInputType = { description: string; teacher_id: string; }; -type CreateCourseFormProps = {}; +type CreateCourseFormPropsType = {}; -const CreateCourseForm: React.FC = () => { +const CreateCourseForm: React.FC = () => { const { userStore: { userService }, categoryStore: { categoryService }, @@ -58,8 +58,6 @@ const CreateCourseForm: React.FC = () => { handleSubmit, formState: { errors }, control, - watch, - reset, } = useForm({ resolver: yupResolver(CreateCourseSchema), }); @@ -205,6 +203,7 @@ const CreateCourseForm: React.FC = () => { {errors.category_id?.message} + = () => { {errors.teacher_id?.message} + Description