import React from "react"; import { CorporateUser, User } from "@/interfaces/user"; import { BsFileExcel, BsBank, BsPersonFill } from "react-icons/bs"; import IconCard from "./IconCard"; import useAssignmentsCorporates from "@/hooks/useAssignmentCorporates"; import ReactDatePicker from "react-datepicker"; import moment from "moment"; import { AssignmentWithCorporateId } from "@/interfaces/results"; import { flexRender, createColumnHelper, getCoreRowModel, useReactTable, } from "@tanstack/react-table"; import Checkbox from "@/components/Low/Checkbox"; import { useListSearch } from "@/hooks/useListSearch"; import axios from "axios"; import { toast } from "react-toastify"; interface Props { corporateUsers: User[]; users: User[]; } interface TableData { user: string; email: string; correct: number; corporate: string; submitted: boolean; date: moment.Moment; assignment: string; corporateId: string; } interface UserCount { userCount: number; maxUserCount: number; } const searchFilters = [["email"], ["user"], ["userId"]]; const MasterStatistical = (props: Props) => { const { users, corporateUsers } = props; 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() ); const [endDate, setEndDate] = React.useState( moment().endOf("year").toDate() ); const { assignments } = useAssignmentsCorporates({ // corporates: [...corporates, "tYU0HTiJdjMsS8SB7XJsUdMMP892"], corporates: selectedCorporates, startDate, endDate, }); const [downloading, setDownloading] = React.useState(false); 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 userData = users.find((u) => u.id === assignee); const corporate = users.find((u) => u.id === a.assigner)?.name || ""; const commonData = { user: userData?.name || "", email: userData?.email || "", userId: assignee, corporateId: a.corporateId, corporate, assignment: a.name, }; if (userStats.length === 0) { return { ...commonData, correct: 0, submitted: false, // date: moment(), }; } 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("email", { header: "Email", id: "email", 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 { rows: filteredRows, renderSearch, text: searchText } = useListSearch( searchFilters, tableResults ); const table = useReactTable({ data: filteredRows, columns: defaultColumns, getCoreRowModel: getCoreRowModel(), }); const areAllSelected = selectedCorporates.length === corporates.length; const getStudentsConsolidateScore = () => { if (tableResults.length === 0) { return { highest: null, lowest: null }; } // Find the student with the highest and lowest score return tableResults.reduce( (acc, curr) => { if (curr.correct > acc.highest.correct) { acc.highest = curr; } if (curr.correct < acc.lowest.correct) { acc.lowest = curr; } return acc; }, { highest: tableResults[0], lowest: tableResults[0] } ); }; const triggerDownload = async () => { try { setDownloading(true); const res = await axios.post("/api/assignments/statistical/excel", { ids: selectedCorporates, ...(startDate ? { startDate: startDate.toISOString() } : {}), ...(endDate ? { endDate: endDate.toISOString() } : {}), searchText, }); toast.success("Report ready!"); const link = document.createElement("a"); link.href = res.data; // download should have worked but there are some CORS issues // https://firebase.google.com/docs/storage/web/download-files#cors_configuration // link.download="report.pdf"; link.target = "_blank"; link.rel = "noreferrer"; link.click(); setDownloading(false); } catch (err) { toast.error("Failed to display the report!"); console.error(err); setDownloading(false); } }; const renderIcon = () => { if (downloading) { return ( ); } return ( { e.stopPropagation(); triggerDownload(); }} /> ); }; const consolidateResults = getStudentsConsolidateScore(); return ( <> {renderIcon()}
{ 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); }} />
{renderSearch()}
{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())}
{consolidateResults.highest && ( {}} Icon={BsPersonFill} label={`Highest result: ${consolidateResults.highest.user}`} color="purple" /> )} {consolidateResults.lowest && ( {}} Icon={BsPersonFill} label={`Lowest result: ${consolidateResults.lowest.user}`} color="purple" /> )}
); }; export default MasterStatistical;