mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
refactor: removed edit course page store
This commit is contained in:
parent
5d70c629ea
commit
21779419c9
@ -19,15 +19,17 @@ type DeleteLessonDialogPropsType = {
|
|||||||
lesson: Pick<ILesson, 'id' | 'title'>;
|
lesson: Pick<ILesson, 'id' | 'title'>;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
onConfirmed?: (id: string) => void | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DeleteLessonDialog: React.FC<DeleteLessonDialogPropsType> = ({ lesson, isOpen, onClose }) => {
|
const DeleteLessonDialog: React.FC<DeleteLessonDialogPropsType> = ({ lesson, isOpen, onConfirmed, onClose }) => {
|
||||||
const { deleteLesson, isLoading } = useDeleteLesson();
|
const { deleteLesson, isLoading } = useDeleteLesson();
|
||||||
|
|
||||||
const handleDelete = async (isConfirmed: boolean) => {
|
const handleDelete = async (isConfirmed: boolean) => {
|
||||||
if (isConfirmed) {
|
if (isConfirmed) {
|
||||||
try {
|
try {
|
||||||
await deleteLesson(lesson.id);
|
await deleteLesson(lesson.id);
|
||||||
|
onConfirmed && onConfirmed(lesson.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,14 +22,14 @@ import { BeatLoader } from 'react-spinners';
|
|||||||
import { Virtuoso } from 'react-virtuoso';
|
import { Virtuoso } from 'react-virtuoso';
|
||||||
import { FetchUsersParamsType } from '@educt/types';
|
import { FetchUsersParamsType } from '@educt/types';
|
||||||
|
|
||||||
type SelectUsersInputPropsType = {
|
type SelectStudentsInputPropsType = {
|
||||||
placeholder?: string | undefined;
|
placeholder?: string | undefined;
|
||||||
searchParams?: FetchUsersParamsType | undefined;
|
searchParams?: FetchUsersParamsType | undefined;
|
||||||
onSelect: (selected: Array<IUser>) => void;
|
onSelect: (selected: Array<IUser>) => void;
|
||||||
currentStudents: ICourse['students'];
|
currentStudents: ICourse['students'];
|
||||||
};
|
};
|
||||||
|
|
||||||
const SelectUsersInput: React.FC<SelectUsersInputPropsType> = ({
|
const SelectStudentsInput: React.FC<SelectStudentsInputPropsType> = ({
|
||||||
placeholder,
|
placeholder,
|
||||||
searchParams: params,
|
searchParams: params,
|
||||||
onSelect,
|
onSelect,
|
||||||
@ -151,4 +151,4 @@ const SelectUsersInput: React.FC<SelectUsersInputPropsType> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectUsersInput;
|
export default SelectStudentsInput;
|
||||||
|
|||||||
@ -2,24 +2,20 @@
|
|||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
import { ICourse } from '@educt/interfaces';
|
import { ICourse } from '@educt/interfaces';
|
||||||
|
import { CourseServiceInstance } from '@educt/services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import useAsync from '../../useAsync';
|
import useAsync from '../../useAsync';
|
||||||
import { useRootStore } from '../../useRootStore';
|
|
||||||
|
|
||||||
type CourseResultDataType = Omit<ICourse, 'students_count' | 'likes_count' | 'lessons_count'>;
|
type CourseResultDataType = Omit<ICourse, 'students_count' | 'likes_count' | 'lessons_count'>;
|
||||||
|
|
||||||
const useFetchCourse = (id: string) => {
|
const useFetchCourse = (id: string) => {
|
||||||
const {
|
|
||||||
pageStore: { editCourseStore },
|
|
||||||
} = useRootStore();
|
|
||||||
|
|
||||||
const fetch = async (id: string) => {
|
const fetch = async (id: string) => {
|
||||||
const result = await editCourseStore.loadCourseById(id);
|
const { data } = await CourseServiceInstance.fetchById(id);
|
||||||
return result.data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { execute: fetchCourseById, ...state } = useAsync<CourseResultDataType, Parameters<typeof fetch>>(fetch);
|
const { execute: fetchCourseById, ...state } = useAsync<CourseResultDataType, Parameters<typeof fetch>>(fetch);
|
||||||
|
|||||||
@ -1,22 +1,19 @@
|
|||||||
import { useToast } from '@chakra-ui/toast';
|
import { useToast } from '@chakra-ui/toast';
|
||||||
import { ILesson } from '@educt/interfaces';
|
import { ILesson } from '@educt/interfaces';
|
||||||
|
import { LessonServiceInstance } from '@educt/services';
|
||||||
import { useErrorHandler } from 'react-error-boundary';
|
import { useErrorHandler } from 'react-error-boundary';
|
||||||
import useAsync from '../../useAsync';
|
import useAsync from '../../useAsync';
|
||||||
import { useRootStore } from '../../useRootStore';
|
|
||||||
|
|
||||||
type CreatedLessonDataType = ILesson;
|
type CreatedLessonDataType = ILesson;
|
||||||
|
|
||||||
const useDeleteLesson = () => {
|
const useDeleteLesson = () => {
|
||||||
const {
|
|
||||||
pageStore: { editCourseStore },
|
|
||||||
} = useRootStore();
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
const deleteLesson = async (id: string) => {
|
const deleteLesson = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
const result = await editCourseStore.deleteLessonById(id);
|
const result = await LessonServiceInstance.deleteLesson(id);
|
||||||
toast({ title: 'Lesson deleted', status: 'info' });
|
toast({ title: 'Lesson deleted.', status: 'info' });
|
||||||
return result.data;
|
return result.data;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
|
||||||
import { ItemProps, Virtuoso } from 'react-virtuoso';
|
import { ItemProps, Virtuoso } from 'react-virtuoso';
|
||||||
import { DragDropContext, Draggable, DraggableProvided, Droppable, DropResult } from 'react-beautiful-dnd';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import * as helpers from '@educt/helpers';
|
||||||
|
import { DragDropContext, Draggable, DraggableProvided, Droppable, DropResult } from 'react-beautiful-dnd';
|
||||||
import { Flex, Box, Text, IconButton, Icon, Button } from '@chakra-ui/react';
|
import { Flex, Box, Text, IconButton, Icon, Button } from '@chakra-ui/react';
|
||||||
import { DeleteIcon, DragHandleIcon } from '@chakra-ui/icons';
|
import { DeleteIcon, DragHandleIcon } from '@chakra-ui/icons';
|
||||||
|
import { MdTimer, MdAttachment } from 'react-icons/md';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
@ -19,12 +20,11 @@ import DeleteLessonDialog from '@educt/components/Dialogs/DeleteLessonDialog';
|
|||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
|
import { useState } from 'react';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import { useColorMode } from '@chakra-ui/react';
|
import { useColorMode } from '@chakra-ui/react';
|
||||||
import { MdTimer, MdAttachment } from 'react-icons/md';
|
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
|
||||||
import useDidMountEffect from '@educt/hooks/useDidMountEffect';
|
|
||||||
import { useDisclosure } from '@chakra-ui/hooks';
|
import { useDisclosure } from '@chakra-ui/hooks';
|
||||||
|
import useDidMountEffect from '@educt/hooks/useDidMountEffect';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contexts
|
* Contexts
|
||||||
@ -55,19 +55,16 @@ const CreateLessonButton: React.FC<{ id: string }> = ({ id }) => {
|
|||||||
|
|
||||||
const LessonList: React.FC<LessonListPropsType> = ({ course }) => {
|
const LessonList: React.FC<LessonListPropsType> = ({ course }) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const {
|
const { onOpen: onOpenDelDialog, onClose: onCloseDelDialog, isOpen: isOpenDelDialog } = useDisclosure();
|
||||||
pageStore: { editCourseStore },
|
const [lessons, setLessons] = useState<Array<ILesson>>(course.lessons);
|
||||||
} = useRootStore();
|
|
||||||
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
|
|
||||||
const [deleting, setDeleting] = useState<Pick<ILesson, 'id' | 'title'> | null>(null);
|
const [deleting, setDeleting] = useState<Pick<ILesson, 'id' | 'title'> | null>(null);
|
||||||
const handleError = useErrorHandler();
|
const handleError = useErrorHandler();
|
||||||
const lessons = course.lessons;
|
|
||||||
|
|
||||||
const handleCreateLesson = (): void => history.push('/lessons/create');
|
const handleCreateLesson = (): void => history.push('/lessons/create');
|
||||||
const handleEditLesson = (id: string): void => history.push(`/lessons/edit/${id}`);
|
const handleEditLesson = (id: string): void => history.push(`/lessons/edit/${id}`);
|
||||||
const handleDeleteLesson = (lesson: ILesson): void => {
|
const handleDeleteLesson = (lesson: ILesson): void => {
|
||||||
setDeleting({ id: lesson.id, title: lesson.title });
|
setDeleting({ id: lesson.id, title: lesson.title });
|
||||||
onOpenDeleteDialog();
|
onOpenDelDialog();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeOrder = async (ids: string[]) => {
|
const handleChangeOrder = async (ids: string[]) => {
|
||||||
@ -84,9 +81,11 @@ const LessonList: React.FC<LessonListPropsType> = ({ course }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
editCourseStore.reorderLessons(result.source.index, result.destination.index);
|
setLessons(helpers.arrayMove(lessons, result.source.index, result.destination.index));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onDeleted = (id: string) => setLessons(prev => prev.filter(l => l.id !== id));
|
||||||
|
|
||||||
const LessonItem = React.useMemo(() => {
|
const LessonItem = React.useMemo(() => {
|
||||||
return ({
|
return ({
|
||||||
provided,
|
provided,
|
||||||
@ -199,7 +198,12 @@ const LessonList: React.FC<LessonListPropsType> = ({ course }) => {
|
|||||||
{lessons.length !== 0 ? (
|
{lessons.length !== 0 ? (
|
||||||
<>
|
<>
|
||||||
{deleting && (
|
{deleting && (
|
||||||
<DeleteLessonDialog lesson={deleting} isOpen={isOpenDeleteDialog} onClose={onCloseDeleteDialog} />
|
<DeleteLessonDialog
|
||||||
|
lesson={deleting}
|
||||||
|
isOpen={isOpenDelDialog}
|
||||||
|
onClose={onCloseDelDialog}
|
||||||
|
onConfirmed={onDeleted}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<Flex mt='2' mb='3' padding='0 20px' alignItems='center' justifyContent='space-between'>
|
<Flex mt='2' mb='3' padding='0 20px' alignItems='center' justifyContent='space-between'>
|
||||||
<Text fontSize='sm' color='gray.500'>
|
<Text fontSize='sm' color='gray.500'>
|
||||||
@ -255,4 +259,4 @@ const LessonList: React.FC<LessonListPropsType> = ({ course }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default observer(LessonList);
|
export default LessonList;
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Table, Tbody, Button, Input, InputGroup, InputLeftElement, Flex, Box, Text } from '@chakra-ui/react';
|
import { Table, Tbody, Input, InputGroup, InputLeftElement, Flex, Box, Text } from '@chakra-ui/react';
|
||||||
import { MdSearch } from 'react-icons/md';
|
import { MdSearch } from 'react-icons/md';
|
||||||
import AddStudentsModal from '@educt/components/Modals/AddStudentsModal';
|
import AddStudentsModal from '@educt/components/Modals/AddStudentsModal';
|
||||||
import { AddButton } from '@educt/components/Buttons';
|
import { AddButton } from '@educt/components/Buttons';
|
||||||
import BulkActionsMenu from './BulkActionsMenu';
|
import BulkActionsMenu from './BulkActionsMenu';
|
||||||
import StudentTableHead from './StudentTableHead';
|
import StudentTableHead from './StudentTableHead';
|
||||||
import { AddIcon } from '@chakra-ui/icons';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
import type { StudentTableRowPropsType } from './StudentTableRow';
|
import type { StudentTableRowPropsType } from './StudentTableRow';
|
||||||
import type { ICourse } from '@educt/interfaces';
|
import type { ICourse, IUser } from '@educt/interfaces';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
@ -28,9 +27,8 @@ type StudentTableListPropsType = {
|
|||||||
|
|
||||||
const StudentTableList: React.FC<StudentTableListPropsType> = props => {
|
const StudentTableList: React.FC<StudentTableListPropsType> = props => {
|
||||||
const { render: Row, course } = props;
|
const { render: Row, course } = props;
|
||||||
const { students } = course;
|
|
||||||
|
|
||||||
const [rows, setRows] = useState<ICourse['students']>(students);
|
const [rows, setRows] = useState<ICourse['students']>(props.course.students);
|
||||||
const [selected, setSelected] = useState<ICourse['students']>([]);
|
const [selected, setSelected] = useState<ICourse['students']>([]);
|
||||||
const [search, setSearch] = useState<string>('');
|
const [search, setSearch] = useState<string>('');
|
||||||
const {
|
const {
|
||||||
@ -50,20 +48,21 @@ const StudentTableList: React.FC<StudentTableListPropsType> = props => {
|
|||||||
*/
|
*/
|
||||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(e.target.value);
|
setSearch(e.target.value);
|
||||||
setRows([
|
setRows(
|
||||||
...students.filter(
|
props.course.students.filter(
|
||||||
s => s.fullname.toLowerCase().includes(e.target.value) || s.email.toLowerCase().includes(e.target.value)
|
s => s.fullname.toLowerCase().includes(e.target.value) || s.email.toLowerCase().includes(e.target.value)
|
||||||
),
|
)
|
||||||
]);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete student from state after removing
|
|
||||||
*/
|
|
||||||
const onRemoved = (removedStudents: ICourse['students']) => {
|
const onRemoved = (removedStudents: ICourse['students']) => {
|
||||||
setSelected(prev => prev.filter(s => removedStudents.every(r => s.id !== r.id)));
|
setSelected(prev => prev.filter(s => removedStudents.every(r => s.id !== r.id)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onAdded = (users: IUser[]) => {
|
||||||
|
setRows(prev => [...prev, ...users]);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select all students in a table
|
* Select all students in a table
|
||||||
*/
|
*/
|
||||||
@ -133,7 +132,7 @@ const StudentTableList: React.FC<StudentTableListPropsType> = props => {
|
|||||||
onClose={onCloseAddStudentModal}
|
onClose={onCloseAddStudentModal}
|
||||||
course={course}
|
course={course}
|
||||||
currentStudents={rows}
|
currentStudents={rows}
|
||||||
onAdded={users => setRows(prev => [...prev, ...users])}
|
onAdded={onAdded}
|
||||||
/>
|
/>
|
||||||
<Flex justifyContent='space-between' flexDir={{ base: 'column', lg: 'row' }}>
|
<Flex justifyContent='space-between' flexDir={{ base: 'column', lg: 'row' }}>
|
||||||
<Flex mb='2' alignItems='center'>
|
<Flex mb='2' alignItems='center'>
|
||||||
@ -164,7 +163,7 @@ const StudentTableList: React.FC<StudentTableListPropsType> = props => {
|
|||||||
<AddButton onClick={onOpenAddStudentModal} />
|
<AddButton onClick={onOpenAddStudentModal} />
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Table overflow='hidden' borderRadius='lg' mt='2'>
|
<Table borderRadius='lg' mt='2'>
|
||||||
<StudentTableHead onSelectAll={handleSelectAll} />
|
<StudentTableHead onSelectAll={handleSelectAll} />
|
||||||
<Tbody>
|
<Tbody>
|
||||||
{rows.map(student => (
|
{rows.map(student => (
|
||||||
@ -184,7 +183,9 @@ const StudentTableList: React.FC<StudentTableListPropsType> = props => {
|
|||||||
</Table>
|
</Table>
|
||||||
{isEmptyRows && (
|
{isEmptyRows && (
|
||||||
<Box textAlign='center' m='6'>
|
<Box textAlign='center' m='6'>
|
||||||
<Text color='gray.500'>Cannot find any user.</Text>
|
<Text color='gray.500' userSelect='none'>
|
||||||
|
Cannot find any user.
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Text mt='4' fontSize='sm' color='gray.500'>
|
<Text mt='4' fontSize='sm' color='gray.500'>
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import LessonList from './components/LessonList';
|
|||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
|
||||||
import { useFetchCourse } from '@educt/hooks/queries';
|
import { useFetchCourse } from '@educt/hooks/queries';
|
||||||
import { useDisclosure } from '@chakra-ui/hooks';
|
import { useDisclosure } from '@chakra-ui/hooks';
|
||||||
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
|
||||||
@ -33,12 +32,7 @@ import { StatusButton } from './components';
|
|||||||
*/
|
*/
|
||||||
const EditCoursePage: React.FC<IPageProps> = () => {
|
const EditCoursePage: React.FC<IPageProps> = () => {
|
||||||
const params = useParams<{ id: string }>();
|
const params = useParams<{ id: string }>();
|
||||||
const {
|
const { error, data: course, isLoading } = useFetchCourse(params.id);
|
||||||
pageStore: {
|
|
||||||
editCourseStore: { course },
|
|
||||||
},
|
|
||||||
} = useRootStore();
|
|
||||||
const { error, isLoading } = useFetchCourse(params.id);
|
|
||||||
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
|
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,13 +62,12 @@ const EditCoursePage: React.FC<IPageProps> = () => {
|
|||||||
</PageHeading>
|
</PageHeading>
|
||||||
<PageContent>
|
<PageContent>
|
||||||
<Box>
|
<Box>
|
||||||
<Tabs isLazy mt='8'>
|
<Tabs mt='8'>
|
||||||
<TabList>
|
<TabList>
|
||||||
<Tab>Info</Tab>
|
<Tab>Info</Tab>
|
||||||
<Tab>Lessons</Tab>
|
<Tab>Lessons</Tab>
|
||||||
<Tab>Students</Tab>
|
<Tab>Students</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<TabPanels>
|
<TabPanels>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
@ -76,63 +76,61 @@ const UserList: React.FC<UserListPropsType> = ({ render: Item, limit }) => {
|
|||||||
<Box>
|
<Box>
|
||||||
{editing && <EditUserForm user={editing} isOpen={isOpenEditModal} onClose={onCloseEditModal} />}
|
{editing && <EditUserForm user={editing} isOpen={isOpenEditModal} onClose={onCloseEditModal} />}
|
||||||
{deleting && <DeleteUserDialog user={deleting} isOpen={isOpenDeleteDialog} onClose={onCloseDeleteDialog} />}
|
{deleting && <DeleteUserDialog user={deleting} isOpen={isOpenDeleteDialog} onClose={onCloseDeleteDialog} />}
|
||||||
{users.length !== 0 ? (
|
|
||||||
<>
|
<Flex justifyContent='space-between' sx={{ gap: '10px' }} flexWrap='wrap'>
|
||||||
<Flex justifyContent='space-between' sx={{ gap: '10px' }} flexWrap='wrap'>
|
<UserSearch />
|
||||||
<UserSearch />
|
<CreateUserForm />
|
||||||
<CreateUserForm />
|
</Flex>
|
||||||
</Flex>
|
<Box>
|
||||||
<Box>
|
<Flex mt='5' p='0 10px' fontWeight='bold' alignItems='center' justifyContent='space-between'>
|
||||||
<Flex mt='5' p='0 10px' fontWeight='bold' alignItems='center' justifyContent='space-between'>
|
<Text>Total: ({pagination.total})</Text>
|
||||||
<Text>Total: ({pagination.total})</Text>
|
<Text>Actions</Text>
|
||||||
<Text>Actions</Text>
|
</Flex>
|
||||||
</Flex>
|
{!isLoading ? (
|
||||||
{!isLoading ? (
|
<Stack mt='4' spacing='2'>
|
||||||
<Stack mt='4' spacing='2'>
|
{users.map(user => (
|
||||||
{users.map(user => (
|
<Item
|
||||||
<Item
|
key={user.id}
|
||||||
key={user.id}
|
user={user}
|
||||||
user={user}
|
onEdit={user => handleEditUser(user)}
|
||||||
onEdit={user => handleEditUser(user)}
|
onDelete={user => handleDeleteUser({ id: user.id, fullname: user.fullname })}
|
||||||
onDelete={user => handleDeleteUser({ id: user.id, fullname: user.fullname })}
|
/>
|
||||||
/>
|
))}
|
||||||
))}
|
</Stack>
|
||||||
</Stack>
|
) : (
|
||||||
) : (
|
<LoadingList />
|
||||||
<LoadingList />
|
)}
|
||||||
)}
|
</Box>
|
||||||
</Box>
|
{users.length === 0 && (
|
||||||
<Flex margin='2rem 0' flexDirection={{ base: 'column', sm: 'column', md: 'row' }}>
|
<Box textAlign='center' userSelect='none'>
|
||||||
<Box textAlign={{ base: 'center', sm: 'center', md: 'left' }} mb={{ base: '2', md: '0' }}>
|
|
||||||
<Text fontSize='sm'>
|
|
||||||
Page <b>{pagination.current_page}</b> of {pagesCount}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
<Flex ml='auto' mr='auto' justifyContent='center' flexWrap='wrap' alignItems='center' sx={{ gap: '30px' }}>
|
|
||||||
<Button
|
|
||||||
onClick={() => setSearchingPage(prevP => prevP - 1)}
|
|
||||||
disabled={pagination.current_page <= 1}
|
|
||||||
variant='link'
|
|
||||||
leftIcon={<ChevronLeftIcon />}
|
|
||||||
>
|
|
||||||
Previous page
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => setSearchingPage(prevP => prevP + 1)}
|
|
||||||
disabled={pagination.current_page === pagesCount}
|
|
||||||
variant='link'
|
|
||||||
rightIcon={<ChevronRightIcon />}
|
|
||||||
>
|
|
||||||
Next page
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Box textAlign='center' mt='10' userSelect='none'>
|
|
||||||
<Text color='gray.500'>Cannot find any users.</Text>
|
<Text color='gray.500'>Cannot find any users.</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
<Flex margin='2rem 0' flexDirection={{ base: 'column', sm: 'column', md: 'row' }}>
|
||||||
|
<Box textAlign={{ base: 'center', sm: 'center', md: 'left' }} mb={{ base: '2', md: '0' }}>
|
||||||
|
<Text fontSize='sm'>
|
||||||
|
Page <b>{pagination.current_page}</b> of {pagesCount}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Flex ml='auto' mr='auto' justifyContent='center' flexWrap='wrap' alignItems='center' sx={{ gap: '30px' }}>
|
||||||
|
<Button
|
||||||
|
onClick={() => setSearchingPage(prevP => prevP - 1)}
|
||||||
|
disabled={pagination.current_page <= 1}
|
||||||
|
variant='link'
|
||||||
|
leftIcon={<ChevronLeftIcon />}
|
||||||
|
>
|
||||||
|
Previous page
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => setSearchingPage(prevP => prevP + 1)}
|
||||||
|
disabled={pagination.current_page === pagesCount}
|
||||||
|
variant='link'
|
||||||
|
rightIcon={<ChevronRightIcon />}
|
||||||
|
>
|
||||||
|
Next page
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { makeAutoObservable } from 'mobx';
|
import { makeAutoObservable } from 'mobx';
|
||||||
import CoursesStore from './pages/CoursesStore';
|
import CoursesStore from './pages/CoursesStore';
|
||||||
import EditCourseStore from './pages/EditCourseStore';
|
|
||||||
import RootStore from './RootStore';
|
import RootStore from './RootStore';
|
||||||
|
|
||||||
export default class PageStore {
|
export default class PageStore {
|
||||||
@ -8,8 +7,6 @@ export default class PageStore {
|
|||||||
|
|
||||||
public coursesStore: CoursesStore;
|
public coursesStore: CoursesStore;
|
||||||
|
|
||||||
public editCourseStore: EditCourseStore;
|
|
||||||
|
|
||||||
constructor(root: RootStore) {
|
constructor(root: RootStore) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
|
|
||||||
@ -17,7 +14,6 @@ export default class PageStore {
|
|||||||
* Pages
|
* Pages
|
||||||
*/
|
*/
|
||||||
this.coursesStore = new CoursesStore(this);
|
this.coursesStore = new CoursesStore(this);
|
||||||
this.editCourseStore = new EditCourseStore(this);
|
|
||||||
|
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
import { makeAutoObservable, runInAction } from 'mobx';
|
|
||||||
import * as helpers from '@educt/helpers';
|
|
||||||
import { ICourse } from '@educt/interfaces';
|
|
||||||
import PageStore from '../PageStore';
|
|
||||||
|
|
||||||
import { CourseServiceInstance, LessonServiceInstance } from '@educt/services';
|
|
||||||
|
|
||||||
export default class EditCourseStore {
|
|
||||||
public pageStore: PageStore;
|
|
||||||
|
|
||||||
public course: Omit<ICourse, 'students_count' | 'likes_count' | 'lessons_count'> | null = null;
|
|
||||||
|
|
||||||
constructor(pageStore: PageStore) {
|
|
||||||
this.pageStore = pageStore;
|
|
||||||
|
|
||||||
makeAutoObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load course by id into store
|
|
||||||
*
|
|
||||||
* @param id Course id
|
|
||||||
* @returns Course response
|
|
||||||
*/
|
|
||||||
public async loadCourseById(id: string) {
|
|
||||||
const result = await CourseServiceInstance.fetchById(id);
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
this.course = result.data;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO update stor on detaching, attaching, deleting students
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove lesson from store
|
|
||||||
*
|
|
||||||
* @param id Lesson id
|
|
||||||
* @returns Deleted lesson response
|
|
||||||
*/
|
|
||||||
public async deleteLessonById(id: string) {
|
|
||||||
const result = await LessonServiceInstance.deleteLesson(id);
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
if (this.course) {
|
|
||||||
this.course.lessons = this.course.lessons.filter(lesson => lesson.id !== result.data.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reorder lessons in store list on drag
|
|
||||||
*
|
|
||||||
* @param oldIndex
|
|
||||||
* @param newIndex
|
|
||||||
*/
|
|
||||||
public reorderLessons(oldIndex: number, newIndex: number): void {
|
|
||||||
if (this.course) {
|
|
||||||
this.course.lessons = helpers.arrayMove(this.course.lessons, oldIndex, newIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user