diff --git a/src/dashboards/Corporate/index.tsx b/src/dashboards/Corporate/index.tsx index 9fa3187c..f353d055 100644 --- a/src/dashboards/Corporate/index.tsx +++ b/src/dashboards/Corporate/index.tsx @@ -1,400 +1,546 @@ /* eslint-disable @next/next/no-img-element */ import Modal from "@/components/Modal"; import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser"; -import useUsers, {userHashStudent, userHashTeacher, userHashCorporate} from "@/hooks/useUsers"; -import {CorporateUser, Group, MasterCorporateUser, Stat, User} from "@/interfaces/user"; -import UserList from "@/pages/(admin)/Lists/UserList"; -import {dateSorter} from "@/utils"; -import moment from "moment"; -import {useEffect, useMemo, useState} from "react"; +import useUsers, { + userHashStudent, + userHashTeacher, + userHashCorporate, +} from "@/hooks/useUsers"; import { - BsArrowLeft, - BsClipboard2Data, - BsClipboard2DataFill, - BsClock, - BsGlobeCentralSouthAsia, - BsPaperclip, - BsPerson, - BsPersonAdd, - BsPersonFill, - BsPersonFillGear, - BsPersonGear, - BsPencilSquare, - BsPersonBadge, - BsPersonCheck, - BsPeople, - BsArrowRepeat, - BsPlus, - BsEnvelopePaper, + CorporateUser, + Group, + MasterCorporateUser, + Stat, + User, +} from "@/interfaces/user"; +import UserList from "@/pages/(admin)/Lists/UserList"; +import { dateSorter } from "@/utils"; +import moment from "moment"; +import { useEffect, useMemo, useState } from "react"; +import { + BsArrowLeft, + BsClipboard2Data, + BsClipboard2DataFill, + BsClock, + BsGlobeCentralSouthAsia, + BsPaperclip, + BsPerson, + BsPersonAdd, + BsPersonFill, + BsPersonFillGear, + BsPersonGear, + BsPencilSquare, + BsPersonBadge, + BsPersonCheck, + BsPeople, + BsArrowRepeat, + BsPlus, + BsEnvelopePaper, + BsDatabase, } from "react-icons/bs"; import UserCard from "@/components/UserCard"; import useGroups from "@/hooks/useGroups"; -import {averageLevelCalculator, calculateAverageLevel, calculateBandScore} from "@/utils/score"; -import {MODULE_ARRAY} from "@/utils/moduleUtils"; -import {Module} from "@/interfaces"; -import {groupByExam} from "@/utils/stats"; +import { + averageLevelCalculator, + calculateAverageLevel, + calculateBandScore, +} from "@/utils/score"; +import { MODULE_ARRAY } from "@/utils/moduleUtils"; +import { Module } from "@/interfaces"; +import { groupByExam } from "@/utils/stats"; import IconCard from "../IconCard"; import GroupList from "@/pages/(admin)/Lists/GroupList"; import useFilterStore from "@/stores/listFilterStore"; -import {useRouter} from "next/router"; +import { useRouter } from "next/router"; import useCodes from "@/hooks/useCodes"; -import {getUserCorporate} from "@/utils/groups"; +import { getUserCorporate } from "@/utils/groups"; import useAssignments from "@/hooks/useAssignments"; -import {Assignment} from "@/interfaces/results"; +import { Assignment } from "@/interfaces/results"; import AssignmentView from "../AssignmentView"; import AssignmentCreator from "../AssignmentCreator"; import clsx from "clsx"; import AssignmentCard from "../AssignmentCard"; -import {createColumnHelper} from "@tanstack/react-table"; +import { createColumnHelper } from "@tanstack/react-table"; import Checkbox from "@/components/Low/Checkbox"; import List from "@/components/List"; -import {getUserCompanyName} from "@/resources/user"; -import {futureAssignmentFilter, pastAssignmentFilter, archivedAssignmentFilter, activeAssignmentFilter} from "@/utils/assignments"; +import { getUserCompanyName } from "@/resources/user"; +import { + futureAssignmentFilter, + pastAssignmentFilter, + archivedAssignmentFilter, + activeAssignmentFilter, +} from "@/utils/assignments"; import useUserBalance from "@/hooks/useUserBalance"; import AssignmentsPage from "../views/AssignmentsPage"; import StudentPerformancePage from "./StudentPerformancePage"; import MasterStatistical from "../MasterCorporate/MasterStatistical"; interface Props { - user: CorporateUser; - linkedCorporate?: CorporateUser | MasterCorporateUser; + user: CorporateUser; + linkedCorporate?: CorporateUser | MasterCorporateUser; } const studentHash = { - type: "student", - orderBy: "registrationDate", - size: 25, + type: "student", + orderBy: "registrationDate", + size: 25, }; const teacherHash = { - type: "teacher", - orderBy: "registrationDate", - size: 25, + type: "teacher", + orderBy: "registrationDate", + size: 25, }; -export default function CorporateDashboard({user, linkedCorporate}: Props) { - const [selectedUser, setSelectedUser] = useState(); - const [showModal, setShowModal] = useState(false); +export default function CorporateDashboard({ user, linkedCorporate }: Props) { + const [selectedUser, setSelectedUser] = useState(); + const [showModal, setShowModal] = useState(false); - const {data: stats} = useFilterRecordsByUser(); - const {groups} = useGroups({admin: user.id}); - const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id}); - const {balance} = useUserBalance(); + const { data: stats } = useFilterRecordsByUser(); + const { groups } = useGroups({ admin: user.id }); + const { + assignments, + isLoading: isAssignmentsLoading, + reload: reloadAssignments, + } = useAssignments({ corporate: user.id }); + const { balance } = useUserBalance(); - const {users: students, total: totalStudents, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(studentHash); - const {users: teachers, total: totalTeachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers(teacherHash); + const { + users: students, + total: totalStudents, + reload: reloadStudents, + isLoading: isStudentsLoading, + } = useUsers(studentHash); + const { + users: teachers, + total: totalTeachers, + reload: reloadTeachers, + isLoading: isTeachersLoading, + } = useUsers(teacherHash); - const appendUserFilters = useFilterStore((state) => state.appendUserFilter); - const router = useRouter(); + const appendUserFilters = useFilterStore((state) => state.appendUserFilter); + const router = useRouter(); - const assignmentsGroups = useMemo(() => groups.filter((x) => x.admin === user.id || x.participants.includes(user.id)), [groups, user.id]); + const assignmentsGroups = useMemo( + () => + groups.filter( + (x) => x.admin === user.id || x.participants.includes(user.id) + ), + [groups, user.id] + ); - const assignmentsUsers = useMemo( - () => - [...teachers, ...students].filter((x) => - !!selectedUser - ? groups - .filter((g) => g.admin === selectedUser.id) - .flatMap((g) => g.participants) - .includes(x.id) || false - : groups.flatMap((g) => g.participants).includes(x.id), - ), - [groups, teachers, students, selectedUser], - ); + const assignmentsUsers = useMemo( + () => + [...teachers, ...students].filter((x) => + !!selectedUser + ? groups + .filter((g) => g.admin === selectedUser.id) + .flatMap((g) => g.participants) + .includes(x.id) || false + : groups.flatMap((g) => g.participants).includes(x.id) + ), + [groups, teachers, students, selectedUser] + ); - useEffect(() => { - setShowModal(!!selectedUser && router.asPath === "/#"); - }, [selectedUser, router.asPath]); + useEffect(() => { + setShowModal(!!selectedUser && router.asPath === "/#"); + }, [selectedUser, router.asPath]); - const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id); + const getStatsByStudent = (user: User) => + stats.filter((s) => s.user === user.id); - const UserDisplay = (displayUser: User) => ( -
setSelectedUser(displayUser)} - className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300"> - {displayUser.name} -
- {displayUser.name} - {displayUser.email} -
-
- ); + const UserDisplay = (displayUser: User) => ( +
setSelectedUser(displayUser)} + className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300" + > + {displayUser.name} +
+ {displayUser.name} + {displayUser.email} +
+
+ ); - const GroupsList = () => { - const filter = (x: Group) => x.admin === user.id || x.participants.includes(user.id); + // this workaround will allow us toreuse the master statistical due to master corporate restraints + // while still being able to use the corporate user + const groupedByNameCorporateIds = useMemo( + () => ({ + [user.corporateInformation?.companyInformation?.name || user.name]: [ + user.id, + ], + }), + [user] + ); + const teachersAndStudents = useMemo( + () => [...students, ...teachers], + [students, teachers] + ); + const MasterStatisticalPage = () => { + return ( + <> +
+
router.push("/")} + className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300" + > + + Back +
+

Master Statistical

+
+ + + ); + }; - return ( - <> -
-
router.push("/")} - className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> - - Back -
-

Groups ({groups.filter(filter).length})

-
+ const GroupsList = () => { + const filter = (x: Group) => + x.admin === user.id || x.participants.includes(user.id); - - - ); - }; + return ( + <> +
+
router.push("/")} + className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300" + > + + Back +
+

+ Groups ({groups.filter(filter).length}) +

+
- const averageLevelCalculator = (studentStats: Stat[]) => { - const formattedStats = studentStats - .map((s) => ({ - focus: students.find((u) => u.id === s.user)?.focus, - score: s.score, - module: s.module, - })) - .filter((f) => !!f.focus); - const bandScores = formattedStats.map((s) => ({ - module: s.module, - level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!), - })); + + + ); + }; - const levels: {[key in Module]: number} = { - reading: 0, - listening: 0, - writing: 0, - speaking: 0, - level: 0, - }; - bandScores.forEach((b) => (levels[b.module] += b.level)); + const averageLevelCalculator = (studentStats: Stat[]) => { + const formattedStats = studentStats + .map((s) => ({ + focus: students.find((u) => u.id === s.user)?.focus, + score: s.score, + module: s.module, + })) + .filter((f) => !!f.focus); + const bandScores = formattedStats.map((s) => ({ + module: s.module, + level: calculateBandScore( + s.score.correct, + s.score.total, + s.module, + s.focus! + ), + })); - return calculateAverageLevel(levels); - }; + const levels: { [key in Module]: number } = { + reading: 0, + listening: 0, + writing: 0, + speaking: 0, + level: 0, + }; + bandScores.forEach((b) => (levels[b.module] += b.level)); - if (router.asPath === "/#students") - return ( - ( -
-
router.push("/")} - className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> - - Back -
-

Students ({total})

-
- )} - /> - ); + return calculateAverageLevel(levels); + }; - if (router.asPath === "/#teachers") - return ( - ( -
-
router.push("/")} - className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> - - Back -
-

Teachers ({total})

-
- )} - /> - ); + if (router.asPath === "/#students") + return ( + ( +
+
router.push("/")} + className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300" + > + + Back +
+

Students ({total})

+
+ )} + /> + ); - if (router.asPath === "/#groups") return ; - if (router.asPath === "/#studentsPerformance") return ; + if (router.asPath === "/#teachers") + return ( + ( +
+
router.push("/")} + className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300" + > + + Back +
+

Teachers ({total})

+
+ )} + /> + ); - if (router.asPath === "/#assignments") - return ( - router.push("/")} - /> - ); + if (router.asPath === "/#groups") return ; + if (router.asPath === "/#studentsPerformance") + return ; - return ( - <> - setSelectedUser(undefined)}> - <> - {selectedUser && ( -
- { - setSelectedUser(undefined); - if (shouldReload && selectedUser!.type === "student") reloadStudents(); - if (shouldReload && selectedUser!.type === "teacher") reloadTeachers(); - }} - onViewStudents={ - selectedUser.type === "corporate" || selectedUser.type === "teacher" - ? () => { - appendUserFilters({ - id: "view-students", - filter: (x: User) => x.type === "student", - }); - appendUserFilters({ - id: "belongs-to-admin", - filter: (x: User) => - groups - .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id)) - .flatMap((g) => g.participants) - .includes(x.id), - }); + if (router.asPath === "/#assignments") + return ( + router.push("/")} + /> + ); - router.push("/list/users"); - } - : undefined - } - onViewTeachers={ - selectedUser.type === "corporate" || selectedUser.type === "student" - ? () => { - appendUserFilters({ - id: "view-teachers", - filter: (x: User) => x.type === "teacher", - }); - appendUserFilters({ - id: "belongs-to-admin", - filter: (x: User) => - groups - .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id)) - .flatMap((g) => g.participants) - .includes(x.id), - }); + if (router.asPath === "/#statistical") return ; - router.push("/list/users"); - } - : undefined - } - user={selectedUser} - /> -
- )} - -
+ return ( + <> + setSelectedUser(undefined)}> + <> + {selectedUser && ( +
+ { + setSelectedUser(undefined); + if (shouldReload && selectedUser!.type === "student") + reloadStudents(); + if (shouldReload && selectedUser!.type === "teacher") + reloadTeachers(); + }} + onViewStudents={ + selectedUser.type === "corporate" || + selectedUser.type === "teacher" + ? () => { + appendUserFilters({ + id: "view-students", + filter: (x: User) => x.type === "student", + }); + appendUserFilters({ + id: "belongs-to-admin", + filter: (x: User) => + groups + .filter( + (g) => + g.admin === selectedUser.id || + g.participants.includes(selectedUser.id) + ) + .flatMap((g) => g.participants) + .includes(x.id), + }); - <> - {!!linkedCorporate && ( -
- Linked to: {linkedCorporate?.corporateInformation?.companyInformation.name || linkedCorporate.name} -
- )} -
- router.push("/#students")} - isLoading={isStudentsLoading} - Icon={BsPersonFill} - label="Students" - value={totalStudents} - color="purple" - /> - router.push("/#teachers")} - isLoading={isTeachersLoading} - Icon={BsPencilSquare} - label="Teachers" - value={totalTeachers} - color="purple" - /> - groups.flatMap((g) => g.participants).includes(s.user)).length} - color="purple" - /> - groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)} - color="purple" - /> - router.push("/#groups")} Icon={BsPeople} label="Groups" value={groups.length} color="purple" /> - - - router.push("/#studentsPerformance")} - /> - -
+ router.push("/list/users"); + } + : undefined + } + onViewTeachers={ + selectedUser.type === "corporate" || + selectedUser.type === "student" + ? () => { + appendUserFilters({ + id: "view-teachers", + filter: (x: User) => x.type === "teacher", + }); + appendUserFilters({ + id: "belongs-to-admin", + filter: (x: User) => + groups + .filter( + (g) => + g.admin === selectedUser.id || + g.participants.includes(selectedUser.id) + ) + .flatMap((g) => g.participants) + .includes(x.id), + }); -
-
- Latest students -
- {students - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Latest teachers -
- {teachers - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Highest level students -
- {students - .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels)) - .map((x) => ( - - ))} -
-
-
- Highest exam count students -
- {students - .sort( - (a, b) => - Object.keys(groupByExam(getStatsByStudent(b))).length - Object.keys(groupByExam(getStatsByStudent(a))).length, - ) - .map((x) => ( - - ))} -
-
-
- - - ); + router.push("/list/users"); + } + : undefined + } + user={selectedUser} + /> +
+ )} + +
+ + <> + {!!linkedCorporate && ( +
+ Linked to:{" "} + + {linkedCorporate?.corporateInformation?.companyInformation.name || + linkedCorporate.name} + +
+ )} +
+ router.push("/#students")} + isLoading={isStudentsLoading} + Icon={BsPersonFill} + label="Students" + value={totalStudents} + color="purple" + /> + router.push("/#teachers")} + isLoading={isTeachersLoading} + Icon={BsPencilSquare} + label="Teachers" + value={totalTeachers} + color="purple" + /> + + groups.flatMap((g) => g.participants).includes(s.user) + ).length + } + color="purple" + /> + + groups.flatMap((g) => g.participants).includes(s.user) + ) + ).toFixed(1)} + color="purple" + /> + router.push("/#groups")} + Icon={BsPeople} + label="Groups" + value={groups.length} + color="purple" + /> + + + router.push("/#studentsPerformance")} + /> + router.push("/#statistical")} + /> + +
+ +
+
+ Latest students +
+ {students + .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) + .map((x) => ( + + ))} +
+
+
+ Latest teachers +
+ {teachers + .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) + .map((x) => ( + + ))} +
+
+
+ Highest level students +
+ {students + .sort( + (a, b) => + calculateAverageLevel(b.levels) - + calculateAverageLevel(a.levels) + ) + .map((x) => ( + + ))} +
+
+
+ Highest exam count students +
+ {students + .sort( + (a, b) => + Object.keys(groupByExam(getStatsByStudent(b))).length - + Object.keys(groupByExam(getStatsByStudent(a))).length + ) + .map((x) => ( + + ))} +
+
+
+ + + ); }