Added filtering and pagination for the assignment creator

This commit is contained in:
Tiago Ribeiro
2024-09-08 23:24:27 +01:00
parent 6fd2e64e04
commit 9f0ba418e5

View File

@@ -21,6 +21,7 @@ import Checkbox from "@/components/Low/Checkbox";
import {InstructorGender, Variant} from "@/interfaces/exam"; import {InstructorGender, Variant} from "@/interfaces/exam";
import Select from "@/components/Low/Select"; import Select from "@/components/Low/Select";
import useExams from "@/hooks/useExams"; import useExams from "@/hooks/useExams";
import {useListSearch} from "@/hooks/useListSearch";
interface Props { interface Props {
isCreating: boolean; isCreating: boolean;
@@ -31,7 +32,12 @@ interface Props {
cancelCreation: () => void; cancelCreation: () => void;
} }
const SIZE = 12;
export default function AssignmentCreator({isCreating, assignment, user, groups, users, cancelCreation}: Props) { export default function AssignmentCreator({isCreating, assignment, user, groups, users, cancelCreation}: Props) {
const [studentsPage, setStudentsPage] = useState(0);
const [teachersPage, setTeachersPage] = useState(0);
const [selectedModules, setSelectedModules] = useState<Module[]>(assignment?.exams.map((e) => e.module) || []); const [selectedModules, setSelectedModules] = useState<Module[]>(assignment?.exams.map((e) => e.module) || []);
const [assignees, setAssignees] = useState<string[]>(assignment?.assignees || []); const [assignees, setAssignees] = useState<string[]>(assignment?.assignees || []);
const [teachers, setTeachers] = useState<string[]>(!!assignment ? assignment.teachers || [] : [...(user.type === "teacher" ? [user.id] : [])]); const [teachers, setTeachers] = useState<string[]>(!!assignment ? assignment.teachers || [] : [...(user.type === "teacher" ? [user.id] : [])]);
@@ -69,6 +75,29 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
const userStudents = useMemo(() => users.filter((x) => x.type === "student"), [users]); const userStudents = useMemo(() => users.filter((x) => x.type === "student"), [users]);
const userTeachers = useMemo(() => users.filter((x) => x.type === "teacher"), [users]); const userTeachers = useMemo(() => users.filter((x) => x.type === "teacher"), [users]);
const {rows: filteredStudentsRows, renderSearch: renderStudentSearch, text: studentText} = useListSearch([["name"], ["email"]], userStudents);
const {rows: filteredTeachersRows, renderSearch: renderTeacherSearch, text: teacherText} = useListSearch([["name"], ["email"]], userTeachers);
useEffect(() => setStudentsPage(0), [studentText]);
const studentRows = useMemo(
() =>
filteredStudentsRows.slice(
studentsPage * SIZE,
(studentsPage + 1) * SIZE > filteredStudentsRows.length ? filteredStudentsRows.length : (studentsPage + 1) * SIZE,
),
[filteredStudentsRows, studentsPage],
);
useEffect(() => setTeachersPage(0), [teacherText]);
const teacherRows = useMemo(
() =>
filteredTeachersRows.slice(
teachersPage * SIZE,
(teachersPage + 1) * SIZE > filteredTeachersRows.length ? filteredTeachersRows.length : (teachersPage + 1) * SIZE,
),
[filteredTeachersRows, teachersPage],
);
useEffect(() => { useEffect(() => {
setExamIDs((prev) => prev.filter((x) => selectedModules.includes(x.module))); setExamIDs((prev) => prev.filter((x) => selectedModules.includes(x.module)));
}, [selectedModules]); }, [selectedModules]);
@@ -347,9 +376,9 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
</div> </div>
)} )}
<section className="w-full flex flex-col gap-3"> <section className="w-full flex flex-col gap-4">
<span className="font-semibold">Assignees ({assignees.length} selected)</span> <span className="font-semibold">Assignees ({assignees.length} selected)</span>
<div className="flex gap-4 overflow-x-scroll scrollbar-hide"> <div className="grid grid-cols-5 gap-4">
{groups.map((g) => ( {groups.map((g) => (
<button <button
key={g.id} key={g.id}
@@ -371,8 +400,11 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
</button> </button>
))} ))}
</div> </div>
{renderStudentSearch()}
<div className="flex flex-wrap -md:justify-center gap-4"> <div className="flex flex-wrap -md:justify-center gap-4">
{userStudents.map((user) => ( {studentRows.map((user) => (
<div <div
onClick={() => toggleAssignee(user)} onClick={() => toggleAssignee(user)}
className={clsx( className={clsx(
@@ -402,12 +434,32 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
</div> </div>
))} ))}
</div> </div>
<div className="w-full flex gap-2 justify-between items-center">
<div className="flex items-center gap-4 w-fit">
<Button className="w-[200px] h-fit" disabled={studentsPage === 0} onClick={() => setStudentsPage((prev) => prev - 1)}>
Previous Page
</Button>
</div>
<div className="flex items-center gap-4 w-fit">
<span className="opacity-80">
{studentsPage * SIZE + 1} -{" "}
{(studentsPage + 1) * SIZE > filteredStudentsRows.length ? filteredStudentsRows.length : (studentsPage + 1) * SIZE} /{" "}
{filteredStudentsRows.length}
</span>
<Button
className="w-[200px]"
disabled={(studentsPage + 1) * SIZE >= filteredStudentsRows.length}
onClick={() => setStudentsPage((prev) => prev + 1)}>
Next Page
</Button>
</div>
</div>
</section> </section>
{user.type !== "teacher" && ( {user.type !== "teacher" && (
<section className="w-full flex flex-col gap-3"> <section className="w-full flex flex-col gap-3">
<span className="font-semibold">Teachers ({teachers.length} selected)</span> <span className="font-semibold">Teachers ({teachers.length} selected)</span>
<div className="flex gap-4 overflow-x-scroll scrollbar-hide"> <div className="grid grid-cols-5 gap-4">
{groups.map((g) => ( {groups.map((g) => (
<button <button
key={g.id} key={g.id}
@@ -429,8 +481,11 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
</button> </button>
))} ))}
</div> </div>
{renderTeacherSearch()}
<div className="flex flex-wrap -md:justify-center gap-4"> <div className="flex flex-wrap -md:justify-center gap-4">
{userTeachers.map((user) => ( {teacherRows.map((user) => (
<div <div
onClick={() => toggleTeacher(user)} onClick={() => toggleTeacher(user)}
className={clsx( className={clsx(
@@ -453,6 +508,29 @@ export default function AssignmentCreator({isCreating, assignment, user, groups,
</div> </div>
))} ))}
</div> </div>
<div className="w-full flex gap-2 justify-between items-center">
<div className="flex items-center gap-4 w-fit">
<Button className="w-[200px] h-fit" disabled={teachersPage === 0} onClick={() => setTeachersPage((prev) => prev - 1)}>
Previous Page
</Button>
</div>
<div className="flex items-center gap-4 w-fit">
<span className="opacity-80">
{teachersPage * SIZE + 1} -{" "}
{(teachersPage + 1) * SIZE > filteredTeachersRows.length
? filteredTeachersRows.length
: (teachersPage + 1) * SIZE}{" "}
/ {filteredTeachersRows.length}
</span>
<Button
className="w-[200px]"
disabled={(teachersPage + 1) * SIZE >= filteredTeachersRows.length}
onClick={() => setTeachersPage((prev) => prev + 1)}>
Next Page
</Button>
</div>
</div>
</section> </section>
)} )}