Added filtering and pagination for the assignment creator
This commit is contained in:
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user