diff --git a/src/dashboards/IconCard.tsx b/src/dashboards/IconCard.tsx index 5fd55486..afc09cee 100644 --- a/src/dashboards/IconCard.tsx +++ b/src/dashboards/IconCard.tsx @@ -1,36 +1,49 @@ import clsx from "clsx"; -import {IconType} from "react-icons"; +import { IconType } from "react-icons"; interface Props { - Icon: IconType; - label: string; - value?: string | number; - color: "purple" | "rose" | "red" | "green"; - tooltip?: string; - onClick?: () => void; + Icon: IconType; + label: string; + value?: string | number; + color: "purple" | "rose" | "red" | "green"; + tooltip?: string; + onClick?: () => void; + isSelected?: boolean; } -export default function IconCard({Icon, label, value, color, tooltip, onClick}: Props) { - const colorClasses: {[key in typeof color]: string} = { - purple: "text-mti-purple-light", - red: "text-mti-red-light", - rose: "text-mti-rose-light", - green: "text-mti-green-light", - }; +export default function IconCard({ + Icon, + label, + value, + color, + tooltip, + onClick, + isSelected, +}: Props) { + const colorClasses: { [key in typeof color]: string } = { + purple: "mti-purple-light", + red: "mti-red-light", + rose: "mti-rose-light", + green: "mti-green-light", + }; - return ( -
- - - {label} - {value} - -
- ); + return ( +
+ + + {label} + + {value} + + +
+ ); } diff --git a/src/dashboards/MasterCorporate.tsx b/src/dashboards/MasterCorporate.tsx index 4f61f02d..b5ad4275 100644 --- a/src/dashboards/MasterCorporate.tsx +++ b/src/dashboards/MasterCorporate.tsx @@ -2,838 +2,1110 @@ import Modal from "@/components/Modal"; import useStats from "@/hooks/useStats"; import useUsers 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, useState} from "react"; import { - BsArrowLeft, - BsClipboard2Data, - BsClock, - BsPaperclip, - BsPersonFill, - BsPencilSquare, - BsPersonCheck, - BsPeople, - BsBank, - BsEnvelopePaper, - BsArrowRepeat, - BsPlus, - BsPersonFillGear, - BsFilter, - BsDatabase, + 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, useState, useMemo } from "react"; +import { + BsArrowLeft, + BsClipboard2Data, + BsClock, + BsPaperclip, + BsPersonFill, + BsPencilSquare, + BsPersonCheck, + BsPeople, + BsBank, + BsEnvelopePaper, + BsArrowRepeat, + BsPlus, + BsPersonFillGear, + BsFilter, + 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 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 {createColumn, createColumnHelper} from "@tanstack/react-table"; +import { createColumn, createColumnHelper } from "@tanstack/react-table"; import List from "@/components/List"; -import {getUserCorporate} from "@/utils/groups"; -import {getCorporateUser, getUserCompanyName} from "@/resources/user"; +import { getUserCorporate } from "@/utils/groups"; +import { getCorporateUser, getUserCompanyName } from "@/resources/user"; import Checkbox from "@/components/Low/Checkbox"; -import {groupBy, uniq, uniqBy} from "lodash"; +import { groupBy, uniq, uniqBy } from "lodash"; import Select from "@/components/Low/Select"; -import {Menu, MenuButton, MenuItem, MenuItems} from "@headlessui/react"; -import {Popover, PopoverContent, PopoverTrigger} from "@/components/ui/popover"; +import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; import MasterStatistical from "./MasterStatistical"; interface Props { - user: MasterCorporateUser; + user: MasterCorporateUser; } const activeFilter = (a: Assignment) => - moment(a.endDate).isAfter(moment()) && moment(a.startDate).isBefore(moment()) && a.assignees.length > a.results.length; -const pastFilter = (a: Assignment) => (moment(a.endDate).isBefore(moment()) || a.assignees.length === a.results.length) && !a.archived; + moment(a.endDate).isAfter(moment()) && + moment(a.startDate).isBefore(moment()) && + a.assignees.length > a.results.length; +const pastFilter = (a: Assignment) => + (moment(a.endDate).isBefore(moment()) || + a.assignees.length === a.results.length) && + !a.archived; const archivedFilter = (a: Assignment) => a.archived; const futureFilter = (a: Assignment) => moment(a.startDate).isAfter(moment()); -type StudentPerformanceItem = User & {corporate?: CorporateUser; group?: Group}; -const StudentPerformanceList = ({items, stats, users, groups}: {items: StudentPerformanceItem[]; stats: Stat[]; users: User[]; groups: Group[]}) => { - const [isShowingAmount, setIsShowingAmount] = useState(false); - const [availableCorporates] = useState( - uniqBy( - items.map((x) => x.corporate), - "id", - ), - ); - const [availableGroups] = useState( - uniqBy( - items.map((x) => x.group), - "id", - ), - ); +type StudentPerformanceItem = User & { + corporate?: CorporateUser; + group?: Group; +}; +const StudentPerformanceList = ({ + items, + stats, + users, + groups, +}: { + items: StudentPerformanceItem[]; + stats: Stat[]; + users: User[]; + groups: Group[]; +}) => { + const [isShowingAmount, setIsShowingAmount] = useState(false); + const [availableCorporates] = useState( + uniqBy( + items.map((x) => x.corporate), + "id" + ) + ); + const [availableGroups] = useState( + uniqBy( + items.map((x) => x.group), + "id" + ) + ); - const [selectedCorporate, setSelectedCorporate] = useState(null); - const [selectedGroup, setSelectedGroup] = useState(null); + const [selectedCorporate, setSelectedCorporate] = useState< + CorporateUser | null | undefined + >(null); + const [selectedGroup, setSelectedGroup] = useState( + null + ); - const columnHelper = createColumnHelper(); + const columnHelper = createColumnHelper(); - const columns = [ - columnHelper.accessor("name", { - header: "Student Name", - cell: (info) => info.getValue(), - }), - columnHelper.accessor("email", { - header: "E-mail", - cell: (info) => info.getValue(), - }), - columnHelper.accessor("demographicInformation.passport_id", { - header: "ID", - cell: (info) => info.getValue() || "N/A", - }), - columnHelper.accessor("group", { - header: "Group", - cell: (info) => info.getValue()?.name || "N/A", - }), - columnHelper.accessor("corporate", { - header: "Corporate", - cell: (info) => (!!info.getValue() ? getUserCompanyName(info.getValue() as User, users, groups) : "N/A"), - }), - columnHelper.accessor("levels.reading", { - header: "Reading", - cell: (info) => - !isShowingAmount - ? calculateBandScore( - stats - .filter((x) => x.module === "reading" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.correct, 0), - stats - .filter((x) => x.module === "reading" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.total, 0), - "level", - info.row.original.focus || "academic", - ) || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "reading" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels.listening", { - header: "Listening", - cell: (info) => - !isShowingAmount - ? calculateBandScore( - stats - .filter((x) => x.module === "listening" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.correct, 0), - stats - .filter((x) => x.module === "listening" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.total, 0), - "level", - info.row.original.focus || "academic", - ) || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "listening" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels.writing", { - header: "Writing", - cell: (info) => - !isShowingAmount - ? calculateBandScore( - stats - .filter((x) => x.module === "writing" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.correct, 0), - stats - .filter((x) => x.module === "writing" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.total, 0), - "level", - info.row.original.focus || "academic", - ) || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "writing" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels.speaking", { - header: "Speaking", - cell: (info) => - !isShowingAmount - ? calculateBandScore( - stats - .filter((x) => x.module === "speaking" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.correct, 0), - stats - .filter((x) => x.module === "speaking" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.total, 0), - "level", - info.row.original.focus || "academic", - ) || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "speaking" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels.level", { - header: "Level", - cell: (info) => - !isShowingAmount - ? calculateBandScore( - stats - .filter((x) => x.module === "level" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.correct, 0), - stats - .filter((x) => x.module === "level" && x.user === info.row.original.id) - .reduce((acc, curr) => acc + curr.score.total, 0), - "level", - info.row.original.focus || "academic", - ) || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "level" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels", { - id: "overall_level", - header: "Overall", - cell: (info) => - !isShowingAmount - ? averageLevelCalculator( - users, - stats.filter((x) => x.user === info.row.original.id), - ).toFixed(1) - : `${Object.keys(groupByExam(stats.filter((x) => x.user === info.row.original.id))).length} exams`, - }), - ]; + const columns = [ + columnHelper.accessor("name", { + header: "Student Name", + cell: (info) => info.getValue(), + }), + columnHelper.accessor("email", { + header: "E-mail", + cell: (info) => info.getValue(), + }), + columnHelper.accessor("demographicInformation.passport_id", { + header: "ID", + cell: (info) => info.getValue() || "N/A", + }), + columnHelper.accessor("group", { + header: "Group", + cell: (info) => info.getValue()?.name || "N/A", + }), + columnHelper.accessor("corporate", { + header: "Corporate", + cell: (info) => + !!info.getValue() + ? getUserCompanyName(info.getValue() as User, users, groups) + : "N/A", + }), + columnHelper.accessor("levels.reading", { + header: "Reading", + cell: (info) => + !isShowingAmount + ? calculateBandScore( + stats + .filter( + (x) => + x.module === "reading" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.correct, 0), + stats + .filter( + (x) => + x.module === "reading" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.total, 0), + "level", + info.row.original.focus || "academic" + ) || 0 + : `${ + Object.keys( + groupByExam( + stats.filter( + (x) => + x.module === "reading" && x.user === info.row.original.id + ) + ) + ).length + } exams`, + }), + columnHelper.accessor("levels.listening", { + header: "Listening", + cell: (info) => + !isShowingAmount + ? calculateBandScore( + stats + .filter( + (x) => + x.module === "listening" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.correct, 0), + stats + .filter( + (x) => + x.module === "listening" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.total, 0), + "level", + info.row.original.focus || "academic" + ) || 0 + : `${ + Object.keys( + groupByExam( + stats.filter( + (x) => + x.module === "listening" && + x.user === info.row.original.id + ) + ) + ).length + } exams`, + }), + columnHelper.accessor("levels.writing", { + header: "Writing", + cell: (info) => + !isShowingAmount + ? calculateBandScore( + stats + .filter( + (x) => + x.module === "writing" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.correct, 0), + stats + .filter( + (x) => + x.module === "writing" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.total, 0), + "level", + info.row.original.focus || "academic" + ) || 0 + : `${ + Object.keys( + groupByExam( + stats.filter( + (x) => + x.module === "writing" && x.user === info.row.original.id + ) + ) + ).length + } exams`, + }), + columnHelper.accessor("levels.speaking", { + header: "Speaking", + cell: (info) => + !isShowingAmount + ? calculateBandScore( + stats + .filter( + (x) => + x.module === "speaking" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.correct, 0), + stats + .filter( + (x) => + x.module === "speaking" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.total, 0), + "level", + info.row.original.focus || "academic" + ) || 0 + : `${ + Object.keys( + groupByExam( + stats.filter( + (x) => + x.module === "speaking" && x.user === info.row.original.id + ) + ) + ).length + } exams`, + }), + columnHelper.accessor("levels.level", { + header: "Level", + cell: (info) => + !isShowingAmount + ? calculateBandScore( + stats + .filter( + (x) => x.module === "level" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.correct, 0), + stats + .filter( + (x) => x.module === "level" && x.user === info.row.original.id + ) + .reduce((acc, curr) => acc + curr.score.total, 0), + "level", + info.row.original.focus || "academic" + ) || 0 + : `${ + Object.keys( + groupByExam( + stats.filter( + (x) => + x.module === "level" && x.user === info.row.original.id + ) + ) + ).length + } exams`, + }), + columnHelper.accessor("levels", { + id: "overall_level", + header: "Overall", + cell: (info) => + !isShowingAmount + ? averageLevelCalculator( + users, + stats.filter((x) => x.user === info.row.original.id) + ).toFixed(1) + : `${ + Object.keys( + groupByExam( + stats.filter((x) => x.user === info.row.original.id) + ) + ).length + } exams`, + }), + ]; - const filterUsers = (data: StudentPerformanceItem[]) => { - console.log(data, selectedCorporate); - const filterByCorporate = (item: StudentPerformanceItem) => item.corporate?.id === selectedCorporate?.id; - const filterByGroup = (item: StudentPerformanceItem) => item.group?.id === selectedGroup?.id; + const filterUsers = (data: StudentPerformanceItem[]) => { + console.log(data, selectedCorporate); + const filterByCorporate = (item: StudentPerformanceItem) => + item.corporate?.id === selectedCorporate?.id; + const filterByGroup = (item: StudentPerformanceItem) => + item.group?.id === selectedGroup?.id; - const filters: ((item: StudentPerformanceItem) => boolean)[] = []; - if (selectedCorporate !== null) filters.push(filterByCorporate); - if (selectedGroup !== null) filters.push(filterByGroup); + const filters: ((item: StudentPerformanceItem) => boolean)[] = []; + if (selectedCorporate !== null) filters.push(filterByCorporate); + if (selectedGroup !== null) filters.push(filterByGroup); - return filters.reduce((d, f) => d.filter(f), data); - }; + return filters.reduce((d, f) => d.filter(f), data); + }; - return ( -
-
- - Show Utilization - - - -
- -
-
- -
- Filters - ({ - value: x?.id || "N/A", - label: x?.name || "N/A", - }))} - isClearable - value={ - selectedGroup === null - ? null - : { - value: selectedGroup?.id || "N/A", - label: selectedGroup?.name || "N/A", - } - } - placeholder="Select a Group..." - onChange={(value) => - !value - ? setSelectedGroup(null) - : setSelectedGroup(value.value === "N/A" ? undefined : availableGroups.find((x) => x?.id === value.value)) - } - /> -
-
-
-
- - data={filterUsers( - items.sort( - (a, b) => - averageLevelCalculator( - users, - stats.filter((x) => x.user === b.id), - ) - - averageLevelCalculator( - users, - stats.filter((x) => x.user === a.id), - ), - ), - )} - columns={columns} - /> -
- ); + return ( +
+
+ + Show Utilization + + + +
+ +
+
+ +
+ Filters + ({ + value: x?.id || "N/A", + label: x?.name || "N/A", + }))} + isClearable + value={ + selectedGroup === null + ? null + : { + value: selectedGroup?.id || "N/A", + label: selectedGroup?.name || "N/A", + } + } + placeholder="Select a Group..." + onChange={(value) => + !value + ? setSelectedGroup(null) + : setSelectedGroup( + value.value === "N/A" + ? undefined + : availableGroups.find((x) => x?.id === value.value) + ) + } + /> +
+
+
+
+ + data={filterUsers( + items.sort( + (a, b) => + averageLevelCalculator( + users, + stats.filter((x) => x.user === b.id) + ) - + averageLevelCalculator( + users, + stats.filter((x) => x.user === a.id) + ) + ) + )} + columns={columns} + /> +
+ ); }; -export default function MasterCorporateDashboard({user}: Props) { - const [page, setPage] = useState(""); - const [selectedUser, setSelectedUser] = useState(); - const [showModal, setShowModal] = useState(false); - const [selectedAssignment, setSelectedAssignment] = useState(); - const [isCreatingAssignment, setIsCreatingAssignment] = useState(false); - const [corporateAssignments, setCorporateAssignments] = useState<(Assignment & {corporate?: CorporateUser})[]>([]); +export default function MasterCorporateDashboard({ user }: Props) { + const [page, setPage] = useState(""); + const [selectedUser, setSelectedUser] = useState(); + const [showModal, setShowModal] = useState(false); + const [selectedAssignment, setSelectedAssignment] = useState(); + const [isCreatingAssignment, setIsCreatingAssignment] = useState(false); + const [corporateAssignments, setCorporateAssignments] = useState< + (Assignment & { corporate?: CorporateUser })[] + >([]); - const {stats} = useStats(); - const {users, reload} = useUsers(); - const {codes} = useCodes(user.id); - const {groups} = useGroups({admin: user.id, userType: user.type}); + const { stats } = useStats(); + const { users, reload } = useUsers(); + const { codes } = useCodes(user.id); + const { groups } = useGroups({ admin: user.id, userType: user.type }); - const masterCorporateUserGroups = [...new Set(groups.filter((u) => u.admin === user.id).flatMap((g) => g.participants))]; + const masterCorporateUserGroups = [ + ...new Set( + groups.filter((u) => u.admin === user.id).flatMap((g) => g.participants) + ), + ]; - const corporateUserGroups = [...new Set(groups.flatMap((g) => g.participants))]; + const corporateUserGroups = [ + ...new Set(groups.flatMap((g) => g.participants)), + ]; - const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id}); + const { + assignments, + isLoading: isAssignmentsLoading, + reload: reloadAssignments, + } = useAssignments({ corporate: user.id }); - const appendUserFilters = useFilterStore((state) => state.appendUserFilter); - const router = useRouter(); + const appendUserFilters = useFilterStore((state) => state.appendUserFilter); + const router = useRouter(); - useEffect(() => { - setShowModal(!!selectedUser && page === ""); - }, [selectedUser, page]); + useEffect(() => { + setShowModal(!!selectedUser && page === ""); + }, [selectedUser, page]); - useEffect(() => { - setCorporateAssignments( - assignments.filter(activeFilter).map((a) => ({ - ...a, - corporate: !!users.find((x) => x.id === a.assigner) - ? getCorporateUser(users.find((x) => x.id === a.assigner)!, users, groups) - : undefined, - })), - ); - }, [assignments, groups, users]); + useEffect(() => { + setCorporateAssignments( + assignments.filter(activeFilter).map((a) => ({ + ...a, + corporate: !!users.find((x) => x.id === a.assigner) + ? getCorporateUser( + users.find((x) => x.id === a.assigner)!, + users, + groups + ) + : undefined, + })) + ); + }, [assignments, groups, users]); - const studentFilter = (user: User) => user.type === "student" && corporateUserGroups.includes(user.id); - const teacherFilter = (user: User) => user.type === "teacher" && corporateUserGroups.includes(user.id); - const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id); + const studentFilter = (user: User) => + user.type === "student" && corporateUserGroups.includes(user.id); + const teacherFilter = (user: User) => + user.type === "teacher" && corporateUserGroups.includes(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 StudentsList = () => { - const filter = (x: User) => - x.type === "student" && (!!selectedUser ? corporateUserGroups.includes(x.id) || false : corporateUserGroups.includes(x.id)); + const StudentsList = () => { + const filter = (x: User) => + x.type === "student" && + (!!selectedUser + ? corporateUserGroups.includes(x.id) || false + : corporateUserGroups.includes(x.id)); - return ( - ( -
-
setPage("")} - 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 ( + ( +
+
setPage("")} + 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})

+
+ )} + /> + ); + }; - const TeachersList = () => { - const filter = (x: User) => - x.type === "teacher" && (!!selectedUser ? corporateUserGroups.includes(x.id) || false : corporateUserGroups.includes(x.id)); + const TeachersList = () => { + const filter = (x: User) => + x.type === "teacher" && + (!!selectedUser + ? corporateUserGroups.includes(x.id) || false + : corporateUserGroups.includes(x.id)); - return ( - ( -
-
setPage("")} - 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})

-
- )} - /> - ); - }; + return ( + ( +
+
setPage("")} + 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})

+
+ )} + /> + ); + }; - const corporateUserFilter = (x: User) => - x.type === "corporate" && (!!selectedUser ? masterCorporateUserGroups.includes(x.id) || false : masterCorporateUserGroups.includes(x.id)); + const corporateUserFilter = (x: User) => + x.type === "corporate" && + (!!selectedUser + ? masterCorporateUserGroups.includes(x.id) || false + : masterCorporateUserGroups.includes(x.id)); - const CorporateList = () => { - return ( - ( -
-
setPage("")} - className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> - - Back -
-

Corporates ({total})

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

Corporates ({total})

+
+ )} + /> + ); + }; - const GroupsList = () => { - return ( - <> -
-
setPage("")} - 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.length})

-
+ const GroupsList = () => { + return ( + <> +
+
setPage("")} + 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.length})

+
- - - ); - }; + + + ); + }; - // const AssignmentsPage = () => { - // const activeFilter = (a: Assignment) => - // moment(a.endDate).isAfter(moment()) && - // moment(a.startDate).isBefore(moment()) && - // a.assignees.length > a.results.length; - // const pastFilter = (a: Assignment) => - // (moment(a.endDate).isBefore(moment()) || - // a.assignees.length === a.results.length) && - // !a.archived; - // const archivedFilter = (a: Assignment) => a.archived; - // const futureFilter = (a: Assignment) => - // moment(a.startDate).isAfter(moment()); + // const AssignmentsPage = () => { + // const activeFilter = (a: Assignment) => + // moment(a.endDate).isAfter(moment()) && + // moment(a.startDate).isBefore(moment()) && + // a.assignees.length > a.results.length; + // const pastFilter = (a: Assignment) => + // (moment(a.endDate).isBefore(moment()) || + // a.assignees.length === a.results.length) && + // !a.archived; + // const archivedFilter = (a: Assignment) => a.archived; + // const futureFilter = (a: Assignment) => + // moment(a.startDate).isAfter(moment()); - const StudentPerformancePage = () => { - const students = users - .filter((x) => x.type === "student" && groups.flatMap((g) => g.participants).includes(x.id)) - .map((u) => ({ - ...u, - group: groups.find((x) => x.participants.includes(u.id)), - corporate: getCorporateUser(u, users, groups), - })); + const StudentPerformancePage = () => { + const students = users + .filter( + (x) => + x.type === "student" && + groups.flatMap((g) => g.participants).includes(x.id) + ) + .map((u) => ({ + ...u, + group: groups.find((x) => x.participants.includes(u.id)), + corporate: getCorporateUser(u, users, groups), + })); - return ( - <> -
-
setPage("")} - className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> - - Back -
-
- Reload - -
-
- - - ); - }; + return ( + <> +
+
setPage("")} + className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300" + > + + Back +
+
+ Reload + +
+
+ + + ); + }; - const AssignmentsPage = () => { - return ( - <> - { - setSelectedAssignment(undefined); - setIsCreatingAssignment(false); - reloadAssignments(); - }} - assignment={selectedAssignment} - /> - x.admin === user.id || x.participants.includes(user.id))} - users={users.filter( - (x) => - x.type === "student" && - (!!selectedUser - ? groups - .filter((g) => g.admin === selectedUser.id) - .flatMap((g) => g.participants) - .includes(x.id) || false - : groups.flatMap((g) => g.participants).includes(x.id)), - )} - assigner={user.id} - isCreating={isCreatingAssignment} - cancelCreation={() => { - setIsCreatingAssignment(false); - setSelectedAssignment(undefined); - reloadAssignments(); - }} - /> -
-
setPage("")} - className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> - - Back -
-
- Reload - -
-
-
- Active Assignments Status -
- - Total: {assignments.filter(activeFilter).reduce((acc, curr) => acc + curr.results.length, 0)}/ - {assignments.filter(activeFilter).reduce((acc, curr) => curr.exams.length + acc, 0)} - - {Object.keys(groupBy(corporateAssignments, (x) => x.corporate?.id)).map((x) => ( -
- {getUserCompanyName(users.find((u) => u.id === x)!, users, groups)}: - - {groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.results.length + acc, 0)}/ - {groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.exams.length + acc, 0)} - -
- ))} -
-
-
-

Active Assignments ({assignments.filter(activeFilter).length})

-
- {assignments.filter(activeFilter).map((a) => ( - setSelectedAssignment(a)} key={a.id} /> - ))} -
-
-
-

Planned Assignments ({assignments.filter(futureFilter).length})

-
-
setIsCreatingAssignment(true)} - className="w-[250px] h-[200px] flex flex-col gap-2 items-center justify-center bg-white hover:bg-mti-purple-ultralight text-mti-purple-light hover:text-mti-purple-dark border border-mti-gray-platinum hover:drop-shadow p-4 cursor-pointer rounded-xl transition ease-in-out duration-300"> - - New Assignment -
- {assignments.filter(futureFilter).map((a) => ( - { - setSelectedAssignment(a); - setIsCreatingAssignment(true); - }} - key={a.id} - /> - ))} -
-
-
-

Past Assignments ({assignments.filter(pastFilter).length})

-
- {assignments.filter(pastFilter).map((a) => ( - setSelectedAssignment(a)} - key={a.id} - allowDownload - reload={reloadAssignments} - allowArchive - allowExcelDownload - /> - ))} -
-
-
-

Archived Assignments ({assignments.filter(archivedFilter).length})

-
- {assignments.filter(archivedFilter).map((a) => ( - setSelectedAssignment(a)} - key={a.id} - allowDownload - reload={reloadAssignments} - allowUnarchive - allowExcelDownload - /> - ))} -
-
- - ); - }; + const AssignmentsPage = () => { + return ( + <> + { + setSelectedAssignment(undefined); + setIsCreatingAssignment(false); + reloadAssignments(); + }} + assignment={selectedAssignment} + /> + x.admin === user.id || x.participants.includes(user.id) + )} + users={users.filter( + (x) => + x.type === "student" && + (!!selectedUser + ? groups + .filter((g) => g.admin === selectedUser.id) + .flatMap((g) => g.participants) + .includes(x.id) || false + : groups.flatMap((g) => g.participants).includes(x.id)) + )} + assigner={user.id} + isCreating={isCreatingAssignment} + cancelCreation={() => { + setIsCreatingAssignment(false); + setSelectedAssignment(undefined); + reloadAssignments(); + }} + /> +
+
setPage("")} + className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300" + > + + Back +
+
+ Reload + +
+
+
+ Active Assignments Status +
+ + Total:{" "} + {assignments + .filter(activeFilter) + .reduce((acc, curr) => acc + curr.results.length, 0)} + / + {assignments + .filter(activeFilter) + .reduce((acc, curr) => curr.exams.length + acc, 0)} + + {Object.keys( + groupBy(corporateAssignments, (x) => x.corporate?.id) + ).map((x) => ( +
+ + {getUserCompanyName( + users.find((u) => u.id === x)!, + users, + groups + )} + :{" "} + + + {groupBy(corporateAssignments, (x) => x.corporate?.id)[ + x + ].reduce((acc, curr) => curr.results.length + acc, 0)} + / + {groupBy(corporateAssignments, (x) => x.corporate?.id)[ + x + ].reduce((acc, curr) => curr.exams.length + acc, 0)} + +
+ ))} +
+
+
+

+ Active Assignments ({assignments.filter(activeFilter).length}) +

+
+ {assignments.filter(activeFilter).map((a) => ( + setSelectedAssignment(a)} + key={a.id} + /> + ))} +
+
+
+

+ Planned Assignments ({assignments.filter(futureFilter).length}) +

+
+
setIsCreatingAssignment(true)} + className="w-[250px] h-[200px] flex flex-col gap-2 items-center justify-center bg-white hover:bg-mti-purple-ultralight text-mti-purple-light hover:text-mti-purple-dark border border-mti-gray-platinum hover:drop-shadow p-4 cursor-pointer rounded-xl transition ease-in-out duration-300" + > + + New Assignment +
+ {assignments.filter(futureFilter).map((a) => ( + { + setSelectedAssignment(a); + setIsCreatingAssignment(true); + }} + key={a.id} + /> + ))} +
+
+
+

+ Past Assignments ({assignments.filter(pastFilter).length}) +

+
+ {assignments.filter(pastFilter).map((a) => ( + setSelectedAssignment(a)} + key={a.id} + allowDownload + reload={reloadAssignments} + allowArchive + allowExcelDownload + /> + ))} +
+
+
+

+ Archived Assignments ({assignments.filter(archivedFilter).length}) +

+
+ {assignments.filter(archivedFilter).map((a) => ( + setSelectedAssignment(a)} + key={a.id} + allowDownload + reload={reloadAssignments} + allowUnarchive + allowExcelDownload + /> + ))} +
+
+ + ); + }; - const MasterStatisticalPage = () => { - return ( - <> -
-
setPage("")} - 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

-
- { - const user = users.find((u) => u.id === id) as CorporateUser; - if (user) return [...accm, user]; - return accm; - }, [])} - /> - - ); - }; + const masterCorporateUsers = useMemo( + () => + masterCorporateUserGroups.reduce((accm: CorporateUser[], id) => { + const user = users.find((u) => u.id === id) as CorporateUser; + if (user) return [...accm, user]; + return accm; + }, []), + [masterCorporateUserGroups, users] + ); - const DefaultDashboard = () => ( - <> -
- setPage("students")} - Icon={BsPersonFill} - label="Students" - value={users.filter(studentFilter).length} - color="purple" - /> - setPage("teachers")} - Icon={BsPencilSquare} - label="Teachers" - value={users.filter(teacherFilter).length} - 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" - /> - setPage("groups")} Icon={BsPeople} label="Groups" value={groups.length} color="purple" /> - - - setPage("corporate")} - /> - setPage("studentsPerformance")} - /> - setPage("statistical")} - /> - -
+ const MasterStatisticalPage = () => { + return ( + <> +
+
setPage("")} + 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

+
+ + + ); + }; -
-
- Latest students -
- {users - .filter(studentFilter) - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Latest teachers -
- {users - .filter(teacherFilter) - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Highest level students -
- {users - .filter(studentFilter) - .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels)) - .map((x) => ( - - ))} -
-
-
- Highest exam count students -
- {users - .filter(studentFilter) - .sort( - (a, b) => - Object.keys(groupByExam(getStatsByStudent(b))).length - Object.keys(groupByExam(getStatsByStudent(a))).length, - ) - .map((x) => ( - - ))} -
-
-
- - ); + const DefaultDashboard = () => ( + <> +
+ setPage("students")} + Icon={BsPersonFill} + label="Students" + value={users.filter(studentFilter).length} + color="purple" + /> + setPage("teachers")} + Icon={BsPencilSquare} + label="Teachers" + value={users.filter(teacherFilter).length} + 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" + /> + setPage("groups")} + Icon={BsPeople} + label="Groups" + value={groups.length} + color="purple" + /> + + + setPage("corporate")} + /> + setPage("studentsPerformance")} + /> + setPage("statistical")} + /> + +
- return ( - <> - setSelectedUser(undefined)}> - <> - {selectedUser && ( -
- { - setSelectedUser(undefined); - if (shouldReload) reload(); - }} - 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), - }); +
+
+ Latest students +
+ {users + .filter(studentFilter) + .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) + .map((x) => ( + + ))} +
+
+
+ Latest teachers +
+ {users + .filter(teacherFilter) + .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) + .map((x) => ( + + ))} +
+
+
+ Highest level students +
+ {users + .filter(studentFilter) + .sort( + (a, b) => + calculateAverageLevel(b.levels) - + calculateAverageLevel(a.levels) + ) + .map((x) => ( + + ))} +
+
+
+ Highest exam count students +
+ {users + .filter(studentFilter) + .sort( + (a, b) => + Object.keys(groupByExam(getStatsByStudent(b))).length - + Object.keys(groupByExam(getStatsByStudent(a))).length + ) + .map((x) => ( + + ))} +
+
+
+ + ); - 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), - }); + return ( + <> + setSelectedUser(undefined)}> + <> + {selectedUser && ( +
+ { + setSelectedUser(undefined); + if (shouldReload) reload(); + }} + 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), + }); - router.push("/list/users"); - } - : undefined - } - user={selectedUser} - /> -
- )} - -
- {page === "students" && } - {page === "teachers" && } - {page === "groups" && } - {page === "corporate" && } - {page === "assignments" && } - {page === "studentsPerformance" && } - {page === "statistical" && } - {page === "" && } - - ); + 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), + }); + + router.push("/list/users"); + } + : undefined + } + user={selectedUser} + /> +
+ )} + +
+ {page === "students" && } + {page === "teachers" && } + {page === "groups" && } + {page === "corporate" && } + {page === "assignments" && } + {page === "studentsPerformance" && } + {page === "statistical" && } + {page === "" && } + + ); } diff --git a/src/dashboards/MasterStatistical.tsx b/src/dashboards/MasterStatistical.tsx index c3f6800a..71537fb4 100644 --- a/src/dashboards/MasterStatistical.tsx +++ b/src/dashboards/MasterStatistical.tsx @@ -5,11 +5,20 @@ import IconCard from "./IconCard"; import useAssignmentsCorporates from "@/hooks/useAssignmentCorporates"; import ReactDatePicker from "react-datepicker"; import moment from "moment"; -import { groupBySession } from "@/utils/stats"; -import { Assignment, AssignmentResult } from "@/interfaces/results"; +import { Assignment, AssignmentWithCorporateId } from "@/interfaces/results"; +import { + CellContext, + createColumnHelper, + flexRender, + getCoreRowModel, + HeaderGroup, + Table, + useReactTable, +} from "@tanstack/react-table"; +import Checkbox from "@/components/Low/Checkbox"; interface Props { - corporateUsers: CorporateUser[]; + corporateUsers: User[]; users: User[]; } @@ -20,11 +29,29 @@ interface TableData { submitted: boolean; date: moment.Moment; assignment: string; + corporateId: string; } + +interface UserCount { + userCount: number; + maxUserCount: number; +} + const MasterStatistical = (props: Props) => { const { users, corporateUsers } = props; - const corporates = React.useMemo(() => corporateUsers.map((x) => x.id), [corporateUsers]); + const corporateRelevantUsers = React.useMemo( + () => corporateUsers.filter((x) => x.type !== "student") as CorporateUser[], + [corporateUsers] + ); + + const corporates = React.useMemo( + () => corporateRelevantUsers.map((x) => x.id), + [corporateRelevantUsers] + ); + + const [selectedCorporates, setSelectedCorporates] = + React.useState(corporates); const [startDate, setStartDate] = React.useState( moment("01/01/2023").toDate() ); @@ -33,75 +60,249 @@ const MasterStatistical = (props: Props) => { ); const { assignments } = useAssignmentsCorporates({ - corporates, + // corporates: [...corporates, "tYU0HTiJdjMsS8SB7XJsUdMMP892"], + corporates: selectedCorporates, startDate, endDate, }); - - const x = assignments.reduce((accmA: TableData[], a: Assignment) => { - const userResults = a.results.reduce((accmB: TableData[], r: AssignmentResult) => { - const userStats = groupBySession(r.stats); - const data = Object.keys(userStats).map((key) => ({ - user: users.find((u) => u.id === r.user)?.name || "", - correct: userStats[key].reduce((n, e) => n + e.score.correct, 0), - corporate: users.find((u) => u.id === a.assigner)?.name || "", - submitted: false, - date: moment.max(userStats[key].map((e) => moment(e.date))), - assignment: a.name, - })); - return [...accmB, ...data]; - }, []); - - return [...accmA, ...userResults]; - }, []); - return ( -
- console.log("clicked")} - /> - {corporateUsers.map((group) => ( - console.log("clicked", group)} - /> - ))} - - { - setStartDate(initialDate ?? moment("01/01/2023").toDate()); - if (finalDate) { - // basicly selecting a final day works as if I'm selecting the first - // minute of that day. this way it covers the whole day - setEndDate(moment(finalDate).endOf("day").toDate()); - return; + const tableResults = React.useMemo( + () => + assignments.reduce((accmA: TableData[], a: AssignmentWithCorporateId) => { + const userResults = a.assignees.map((assignee) => { + const userStats = + a.results.find((r) => r.user === assignee)?.stats || []; + const userName = users.find((u) => u.id === assignee)?.name || ""; + const corporate = users.find((u) => u.id === a.assigner)?.name || ""; + const commonData = { + user: userName, + corporateId: a.corporateId, + corporate, + assignment: a.name, + }; + if (userStats.length === 0) { + return { + ...commonData, + correct: 0, + submitted: false, + // date: moment(), + }; } - setEndDate(null); - }} - /> - console.log("clicked")} - Icon={BsPersonFill} - label="Consolidate Highest Student" - color="purple" - /> -
+ return { + ...commonData, + correct: userStats.reduce((n, e) => n + e.score.correct, 0), + submitted: true, + date: moment.max(userStats.map((e) => moment(e.date))), + }; + }) as TableData[]; + + return [...accmA, ...userResults]; + }, []), + [assignments, users] + ); + + const getCorporateScores = (corporateId: string): UserCount => { + const corporateAssignmentsUsers = assignments + .filter((a) => a.corporateId === corporateId) + .reduce((acc, a) => acc + a.assignees.length, 0); + + const corporateResults = tableResults.filter( + (r) => r.corporateId === corporateId + ).length; + + return { + maxUserCount: corporateAssignmentsUsers, + userCount: corporateResults, + }; + }; + + const corporateScores = corporates.reduce( + (accm, id) => ({ + ...accm, + [id]: getCorporateScores(id), + }), + {} + ) as Record; + + const consolidateScore = Object.values(corporateScores).reduce( + (acc: UserCount, { userCount, maxUserCount }: UserCount) => ({ + userCount: acc.userCount + userCount, + maxUserCount: acc.maxUserCount + maxUserCount, + }), + { userCount: 0, maxUserCount: 0 } + ); + + const getConsolidateScoreStr = (data: UserCount) => + `${data.userCount}/${data.maxUserCount}`; + + const columnHelper = createColumnHelper(); + + const defaultColumns = [ + columnHelper.accessor("user", { + header: "User", + id: "user", + cell: (info) => { + return {info.getValue()}; + }, + }), + columnHelper.accessor("corporate", { + header: "Corporate", + id: "corporate", + cell: (info) => { + return {info.getValue()}; + }, + }), + columnHelper.accessor("assignment", { + header: "Assignment", + id: "assignment", + cell: (info) => { + return {info.getValue()}; + }, + }), + columnHelper.accessor("submitted", { + header: "Submitted", + id: "submitted", + cell: (info) => { + return ( + {}}> + + + ); + }, + }), + columnHelper.accessor("correct", { + header: "Correct", + id: "correct", + cell: (info) => { + return {info.getValue()}; + }, + }), + columnHelper.accessor("date", { + header: "Date", + id: "date", + cell: (info) => { + const date = info.getValue(); + if (date) { + return {date.format("DD/MM/YYYY")}; + } + + return {""}; + }, + }), + ]; + + const table = useReactTable({ + data: tableResults, + columns: defaultColumns, + getCoreRowModel: getCoreRowModel(), + }); + + const areAllSelected = selectedCorporates.length === corporates.length; + return ( + <> +
+ { + if (areAllSelected) { + setSelectedCorporates([]); + return; + } + setSelectedCorporates(corporates); + }} + isSelected={areAllSelected} + /> + {corporateRelevantUsers.map((group) => { + const isSelected = selectedCorporates.includes(group.id); + return ( + { + if (isSelected) { + setSelectedCorporates( + selectedCorporates.filter((x) => x !== group.id) + ); + return; + } + setSelectedCorporates([...selectedCorporates, group.id]); + }} + isSelected={isSelected} + /> + ); + })} +
+
+ { + setStartDate(initialDate ?? moment("01/01/2023").toDate()); + if (finalDate) { + // basicly selecting a final day works as if I'm selecting the first + // minute of that day. this way it covers the whole day + setEndDate(moment(finalDate).endOf("day").toDate()); + return; + } + setEndDate(null); + }} + /> +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+
+ console.log("clicked")} + Icon={BsPersonFill} + label="Consolidate Highest Student" + color="purple" + /> +
+ ); }; diff --git a/src/hooks/useAssignmentCorporates.tsx b/src/hooks/useAssignmentCorporates.tsx index d6b7dc33..524b31ef 100644 --- a/src/hooks/useAssignmentCorporates.tsx +++ b/src/hooks/useAssignmentCorporates.tsx @@ -1,6 +1,5 @@ -import { Assignment } from "@/interfaces/results"; +import { AssignmentWithCorporateId } from "@/interfaces/results"; import axios from "axios"; -import moment from "moment"; import { useEffect, useState } from "react"; export default function useAssignmentsCorporates({ @@ -12,7 +11,7 @@ export default function useAssignmentsCorporates({ startDate: Date | null; endDate: Date | null; }) { - const [assignments, setAssignments] = useState([]); + const [assignments, setAssignments] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); @@ -30,7 +29,7 @@ export default function useAssignmentsCorporates({ }); axios - .get( + .get( `/api/assignments/corporate?${urlSearchParams.toString()}` ) .then(async (response) => { diff --git a/src/interfaces/results.ts b/src/interfaces/results.ts index 77a92bde..fa4a60d0 100644 --- a/src/interfaces/results.ts +++ b/src/interfaces/results.ts @@ -29,3 +29,5 @@ export interface Assignment { archived?: boolean; released?: boolean; } + +export type AssignmentWithCorporateId = Assignment & { corporateId: string }; diff --git a/src/pages/api/assignments/corporate/index.ts b/src/pages/api/assignments/corporate/index.ts index babea6c2..631878f6 100644 --- a/src/pages/api/assignments/corporate/index.ts +++ b/src/pages/api/assignments/corporate/index.ts @@ -5,7 +5,6 @@ import { sessionOptions } from "@/lib/session"; import { getAllAssignersByCorporate } from "@/utils/groups.be"; import { getAssignmentsByAssigners } from "@/utils/assignments.be"; - export default withIronSessionApiRoute(handler, sessionOptions); async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -20,17 +19,49 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { } async function GET(req: NextApiRequest, res: NextApiResponse) { - const { ids, startDate, endDate } = req.query as { ids: string, startDate?: string, endDate?: string }; + const { ids, startDate, endDate } = req.query as { + ids: string; + startDate?: string; + endDate?: string; + }; const startDateParsed = startDate ? new Date(startDate) : undefined; const endDateParsed = endDate ? new Date(endDate) : undefined; try { const idsList = ids.split(","); - const assigners = await Promise.all(idsList.map(getAllAssignersByCorporate)); - const assignmentList = [...assigners.flat(), ...idsList]; - const assignments = await getAssignmentsByAssigners(assignmentList, startDateParsed, endDateParsed); - res.status(200).json(assignments); + const assigners = await Promise.all( + idsList.map(async (id) => { + const assigners = await getAllAssignersByCorporate(id); + return { + corporateId: id, + assigners, + }; + }) + ); + + const assignments = await Promise.all(assigners.map(async (data) => { + try { + const assigners = [...new Set([...data.assigners, data.corporateId])]; + const assignments = await getAssignmentsByAssigners( + assigners, + startDateParsed, + endDateParsed + ); + return assignments.map((assignment) => ({ + ...assignment, + corporateId: data.corporateId, + })); + } catch (err) { + console.error(err); + return []; + } + })); + + console.log(assignments); + + // const assignments = await getAssignmentsByAssigners(assignmentList, startDateParsed, endDateParsed); + res.status(200).json(assignments.flat()); } catch (err: any) { res.status(500).json({ error: err.message }); } diff --git a/src/utils/stats.ts b/src/utils/stats.ts index e60fcec7..08ba03e8 100644 --- a/src/utils/stats.ts +++ b/src/utils/stats.ts @@ -128,6 +128,7 @@ export const groupBySession = (stats: Stat[]) => groupBy(stats, "session"); export const groupByDate = (stats: Stat[]) => groupBy(stats, "date"); export const groupByExam = (stats: Stat[]) => groupBy(stats, "exam"); export const groupByModule = (stats: Stat[]) => groupBy(stats, "module"); +export const groupByUser = (stats: Stat[]) => groupBy(stats, "user"); export const convertToUserSolutions = (stats: Stat[]): UserSolution[] => { return stats.map((stat) => ({