From 093247899afeac59cf8243c7a6a84b5ee6f9fb4e Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Sun, 5 Dec 2021 23:11:27 +0300 Subject: [PATCH] feat: added students table list --- .../AddStudentsModal/AddSudentsModal.tsx | 51 ++++++++ .../Modals/AddStudentsModal/index.ts | 3 + src/interfaces/index.ts | 6 +- .../components/StudentList/StudentItem.tsx | 16 --- .../components/StudentList/StudentList.tsx | 36 ------ .../edit/components/StudentList/index.ts | 4 - .../StudentTableList/BulkActionsMenu.tsx | 45 +++++++ .../StudentTableList/Cells/index.tsx | 68 ++++++++++ .../StudentTableList/StudentTableHead.tsx | 44 +++++++ .../StudentTableList/StudentTableList.tsx | 119 ++++++++++++++++++ .../StudentTableList/StudentTableRow.tsx | 27 ++++ .../edit/components/StudentTableList/index.ts | 4 + src/pages/courses/edit/edit.tsx | 6 +- src/stores/UIStore.ts | 2 +- 14 files changed, 369 insertions(+), 62 deletions(-) create mode 100644 src/components/Modals/AddStudentsModal/AddSudentsModal.tsx create mode 100644 src/components/Modals/AddStudentsModal/index.ts delete mode 100644 src/pages/courses/edit/components/StudentList/StudentItem.tsx delete mode 100644 src/pages/courses/edit/components/StudentList/StudentList.tsx delete mode 100644 src/pages/courses/edit/components/StudentList/index.ts create mode 100644 src/pages/courses/edit/components/StudentTableList/BulkActionsMenu.tsx create mode 100644 src/pages/courses/edit/components/StudentTableList/Cells/index.tsx create mode 100644 src/pages/courses/edit/components/StudentTableList/StudentTableHead.tsx create mode 100644 src/pages/courses/edit/components/StudentTableList/StudentTableList.tsx create mode 100644 src/pages/courses/edit/components/StudentTableList/StudentTableRow.tsx create mode 100644 src/pages/courses/edit/components/StudentTableList/index.ts diff --git a/src/components/Modals/AddStudentsModal/AddSudentsModal.tsx b/src/components/Modals/AddStudentsModal/AddSudentsModal.tsx new file mode 100644 index 0000000..5887933 --- /dev/null +++ b/src/components/Modals/AddStudentsModal/AddSudentsModal.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Button } from '@chakra-ui/button'; +import { Text, Flex } from '@chakra-ui/react'; +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + ModalCloseButton, +} from '@chakra-ui/react'; +import { useDisclosure, Divider } from '@chakra-ui/react'; +import { MdGroup } from 'react-icons/md'; + +type AddStudentsModalPropsType = {}; + +const AddStudentsModal: React.FC = () => { + const { isOpen, onOpen, onClose } = useDisclosure(); + + return ( + <> + + + + + + + + Add students + + + + + + + + + + + + + + ); +}; + +export default AddStudentsModal; diff --git a/src/components/Modals/AddStudentsModal/index.ts b/src/components/Modals/AddStudentsModal/index.ts new file mode 100644 index 0000000..2dc8b48 --- /dev/null +++ b/src/components/Modals/AddStudentsModal/index.ts @@ -0,0 +1,3 @@ +import AddStudentsModal from './AddSudentsModal'; + +export default AddStudentsModal; diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 9b2f49f..8dd813c 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -23,10 +23,10 @@ export interface ICourse { title: string; description: string; status: CourseStatusEnum; - teacher: Pick; + teacher: Pick; category: ICategory; lessons: ILesson[]; - students: Array>; + students: Array>; color: ColorType | null; students_count: string; likes_count: string; @@ -92,6 +92,8 @@ export interface IUser { email: string; roles: IUserRole[]; contacts: IUserContacts | null; + created_at: string; + updated_at: string; } /** diff --git a/src/pages/courses/edit/components/StudentList/StudentItem.tsx b/src/pages/courses/edit/components/StudentList/StudentItem.tsx deleted file mode 100644 index 96083f1..0000000 --- a/src/pages/courses/edit/components/StudentList/StudentItem.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; - -/** - * Types - */ -import { IUser } from '@educt/interfaces'; - -export type StudentItemPropsType = { - student: Pick; -}; - -const StudentItem: React.FC = ({ student }) => { - return
{student.fullname}
; -}; - -export default StudentItem; diff --git a/src/pages/courses/edit/components/StudentList/StudentList.tsx b/src/pages/courses/edit/components/StudentList/StudentList.tsx deleted file mode 100644 index 916dc57..0000000 --- a/src/pages/courses/edit/components/StudentList/StudentList.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { Stack, Box, Text, Button } from '@chakra-ui/react'; - -/** - * Types - */ -import { StudentItemPropsType } from './StudentItem'; -import { ICourse } from '@educt/interfaces'; - -type StudentListPropsType = { - render: React.FC; - students: ICourse['students']; -}; - -const StudentList: React.FC = ({ render: Item, students }) => { - return ( - - {students.length !== 0 ? ( - - {students.map(student => ( - - ))} - - ) : ( - - There are no students in the course yet - - - )} - - ); -}; - -export default StudentList; diff --git a/src/pages/courses/edit/components/StudentList/index.ts b/src/pages/courses/edit/components/StudentList/index.ts deleted file mode 100644 index afb24b8..0000000 --- a/src/pages/courses/edit/components/StudentList/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import StudentList from './StudentList'; -import StudentItem from './StudentItem'; - -export { StudentList, StudentItem }; diff --git a/src/pages/courses/edit/components/StudentTableList/BulkActionsMenu.tsx b/src/pages/courses/edit/components/StudentTableList/BulkActionsMenu.tsx new file mode 100644 index 0000000..4542cf5 --- /dev/null +++ b/src/pages/courses/edit/components/StudentTableList/BulkActionsMenu.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { Button, Menu, MenuButton, MenuList, MenuItem } from '@chakra-ui/react'; +import { ChevronDownIcon, DeleteIcon } from '@chakra-ui/icons'; +import { MdRemove, MdImportExport } from 'react-icons/md'; + +import type { ICourse } from '@educt/interfaces'; + +type BulkActionsMenuPropsType = { + selected: ICourse['students']; + rows: ICourse['students']; + setRows: React.Dispatch>; +}; + +const BulkActionsMenu: React.FC = ({ selected, rows, setRows }) => { + const handleDetach = async () => { + console.log( + `Detaching from course: `, + selected.map(s => s.id) + ); + setRows(rows.filter(r => selected.every(s => s.id !== r.id))); + }; + const handleDelete = async () => {}; + const handleExport = async () => {}; + + return ( + + }> + Bulk Actions + + + }> + Remove from course + + } isDisabled> + Export + + } color='red' isDisabled> + Delete + + + + ); +}; + +export default BulkActionsMenu; diff --git a/src/pages/courses/edit/components/StudentTableList/Cells/index.tsx b/src/pages/courses/edit/components/StudentTableList/Cells/index.tsx new file mode 100644 index 0000000..fbc067d --- /dev/null +++ b/src/pages/courses/edit/components/StudentTableList/Cells/index.tsx @@ -0,0 +1,68 @@ +import React, { memo } from 'react'; +import { Link as ReactRouterLink } from 'react-router-dom'; +import { Flex, Box, Avatar, Td, Checkbox, Link, Text, IconButton } from '@chakra-ui/react'; +import { Menu, MenuList, MenuButton, MenuItem } from '@chakra-ui/react'; +import { MdEdit, MdMoreHoriz, MdRemove } from 'react-icons/md'; +import { DeleteIcon } from '@chakra-ui/icons'; + +/** + * Types + */ +import type { StudentTableRowPropsType } from '../StudentTableRow'; + +/** + * Hooks + */ +import { useColorModeValue } from '@chakra-ui/react'; +import { IUser } from '@educt/interfaces'; + +type CheckBoxCellPropsType = Pick; +export const CheckBoxCell: React.FC = ({ isSelected, onSelect, student }) => ( + + onSelect(e, student)} /> + +); + +type InfoCellPropsType = Pick; +export const InfoCell: React.FC = memo(({ id, fullname, email }) => ( + + + + + + {fullname} + + + {email} + + + + +)); + +type RegisteredCellPropsType = { registered: string }; +export const RegisteredCell: React.FC = memo(({ registered }) => ( + {new Date(registered).toLocaleDateString()} +)); + +type ActionsCellPropsType = Pick; +export const ActionsCell: React.FC = memo(({ id }) => ( + + + } variant='ghost' /> + + }>Edit + }>Remove from course + } color='red.400'> + Delete + + + + +)); diff --git a/src/pages/courses/edit/components/StudentTableList/StudentTableHead.tsx b/src/pages/courses/edit/components/StudentTableList/StudentTableHead.tsx new file mode 100644 index 0000000..0483830 --- /dev/null +++ b/src/pages/courses/edit/components/StudentTableList/StudentTableHead.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Icon, Flex, Checkbox, Text } from '@chakra-ui/react'; +import { Thead, Tr, Th } from '@chakra-ui/react'; +import { MdSettings, MdSupervisorAccount } from 'react-icons/md'; + +/** + * Hooks + */ +import { useColorModeValue } from '@chakra-ui/react'; + +type StudentTableHeadPropsType = { + onSelectAll?: (e: React.ChangeEvent) => void; +}; + +const StudentTableHead: React.FC = ({ onSelectAll }) => { + return ( + + + + + + + + + + Name + + + + Registered + + + + + Actions + + + + + + ); +}; + +export default StudentTableHead; diff --git a/src/pages/courses/edit/components/StudentTableList/StudentTableList.tsx b/src/pages/courses/edit/components/StudentTableList/StudentTableList.tsx new file mode 100644 index 0000000..5289beb --- /dev/null +++ b/src/pages/courses/edit/components/StudentTableList/StudentTableList.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { Table, Tbody, Button, Menu, MenuButton, MenuList, MenuItem } from '@chakra-ui/react'; +import { Input, InputGroup, InputLeftElement } from '@chakra-ui/react'; +import { Flex, Box, Text } from '@chakra-ui/react'; +import { DeleteIcon, ChevronDownIcon } from '@chakra-ui/icons'; +import { MdSearch, MdImportExport, MdRemove } from 'react-icons/md'; + +import AddStudentsModal from '@educt/components/Modals/AddStudentsModal'; +import StudentTableHead from './StudentTableHead'; + +/** + * Types + */ +import type { StudentTableRowPropsType } from './StudentTableRow'; +import type { ICourse } from '@educt/interfaces'; + +/** + * Hooks + */ +import { useCallback, useState } from 'react'; +import BulkActionsMenu from './BulkActionsMenu'; + +type StudentTableListPropsType = { + render: React.FC; + students: ICourse['students']; +}; + +const StudentTableList: React.FC = ({ render: Row, students }) => { + const [rows, setRows] = useState(students); + const [selected, setSelected] = useState([]); + const [search, setSearch] = useState(''); + + /** + * Search for a student by input + */ + const handleSearch = (e: React.ChangeEvent) => { + setSearch(e.target.value); + setRows([ + ...students.filter( + s => s.fullname.toLowerCase().includes(e.target.value) || s.email.toLowerCase().includes(e.target.value) + ), + ]); + }; + + /** + * Select all students in a table + */ + const handleSelectAll = (e: React.ChangeEvent) => { + if (e.target.checked) { + setSelected(rows); + return; + } + + setSelected([]); + }; + + /** + * Select a student with checkbox + */ + const handleSelect = useCallback((e: React.ChangeEvent, student: ICourse['students'][number]) => { + if (e.target.checked) { + setSelected(prev => [...prev, student]); + return; + } + + setSelected(prev => prev.filter(s => s.id !== student.id)); + }, []); + + if (rows.length === 0) { + return ( + + There are no students in the course yet + + + ); + } + + return ( + + + + + } /> + + + + + + {selected.length !== 0 && ( + <> + + You have selected {selected.length} student(s). + + + + )} + + + + + + + {rows.map(student => ( + s.id === student.id)} + onSelect={handleSelect} + /> + ))} + +
+
+ ); +}; + +export default StudentTableList; diff --git a/src/pages/courses/edit/components/StudentTableList/StudentTableRow.tsx b/src/pages/courses/edit/components/StudentTableList/StudentTableRow.tsx new file mode 100644 index 0000000..9468705 --- /dev/null +++ b/src/pages/courses/edit/components/StudentTableList/StudentTableRow.tsx @@ -0,0 +1,27 @@ +import React, { memo } from 'react'; +import { Tr } from '@chakra-ui/react'; + +/** + * Cells + */ +import { CheckBoxCell, InfoCell, RegisteredCell, ActionsCell } from './Cells'; +import { ICourse } from '@educt/interfaces'; + +export type StudentTableRowPropsType = { + isSelected: boolean; + student: ICourse['students'][number]; + onSelect: (e: React.ChangeEvent, student: ICourse['students'][number]) => void; +}; + +const StudentTableItem: React.FC = ({ student, isSelected, onSelect }) => { + return ( + + + + + + + ); +}; + +export default memo(StudentTableItem); diff --git a/src/pages/courses/edit/components/StudentTableList/index.ts b/src/pages/courses/edit/components/StudentTableList/index.ts new file mode 100644 index 0000000..deba552 --- /dev/null +++ b/src/pages/courses/edit/components/StudentTableList/index.ts @@ -0,0 +1,4 @@ +import StudentTableList from './StudentTableList'; +import StudentTableRow from './StudentTableRow'; + +export { StudentTableList, StudentTableRow }; diff --git a/src/pages/courses/edit/edit.tsx b/src/pages/courses/edit/edit.tsx index 878f833..d6e02b6 100644 --- a/src/pages/courses/edit/edit.tsx +++ b/src/pages/courses/edit/edit.tsx @@ -15,9 +15,9 @@ import type { IPageProps } from '@educt/interfaces'; import LoadingPage from '@educt/components/LoadingPage'; import { EditCourseForm } from '@educt/components/Forms/CourseForm'; import SetCourseStatusButton from '@educt/components/SetCourseStatusButton'; -import LessonList from './components/LessonList'; -import { StudentList, StudentItem } from './components/StudentList'; import DeleteCourseDialog from '@educt/components/Dialogs/DeleteCourseDialog'; +import { StudentTableList, StudentTableRow } from './components/StudentTableList'; +import LessonList from './components/LessonList'; /** * Hooks @@ -96,7 +96,7 @@ const EditCoursePage: React.FC = () => { - + diff --git a/src/stores/UIStore.ts b/src/stores/UIStore.ts index 9605cdf..d3022fc 100644 --- a/src/stores/UIStore.ts +++ b/src/stores/UIStore.ts @@ -35,7 +35,7 @@ export default class UIStore { } public setLocation(location: string): void { - console.log(`[history]: location changed to "${location}"`); this.location = location; + console.info(`[history]: location changed to "${location}"`); } }