/* eslint-disable @next/next/no-img-element */ import Table from "@/components/High/Table"; import Checkbox from "@/components/Low/Checkbox"; import Separator from "@/components/Low/Separator"; import { useAllowedEntities } from "@/hooks/useEntityPermissions"; import { Session } from "@/hooks/useSessions"; import { Entity, EntityWithRoles } from "@/interfaces/entity"; import { Exam } from "@/interfaces/exam"; import { Assignment, AssignmentResult } from "@/interfaces/results"; import { StudentUser, User } from "@/interfaces/user"; import { sessionOptions } from "@/lib/session"; import { filterBy, findBy, mapBy, redirect, serialize } from "@/utils"; import { requestUser } from "@/utils/api"; import { getEntitiesAssignments } from "@/utils/assignments.be"; import { getEntitiesWithRoles } from "@/utils/entities.be"; import { getExamsByIds } from "@/utils/exams.be"; import { findAllowedEntities } from "@/utils/permissions"; import { getSessionsByAssignments } from "@/utils/sessions.be"; import { isAdmin } from "@/utils/users"; import { getEntitiesUsers } from "@/utils/users.be"; import { createColumnHelper } from "@tanstack/react-table"; import axios from "axios"; import { clsx } from "clsx"; import { withIronSessionSsr } from "iron-session/next"; import { capitalize, orderBy } from "lodash"; import moment from "moment"; import Head from "next/head"; import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import ReactDatePicker from "react-datepicker"; import { BsBank, BsChevronLeft, BsX } from "react-icons/bs"; interface Props { user: User; students: StudentUser[]; entities: EntityWithRoles[]; assignments: Assignment[]; sessions: Session[]; exams: Exam[]; } export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res); if (!user) return redirect("/login"); const entityIDS = mapBy(user.entities, "id") || []; const entities = await getEntitiesWithRoles( isAdmin(user) ? undefined : entityIDS ); const allowedEntities = findAllowedEntities( user, entities, "view_entity_statistics" ); if (allowedEntities.length === 0) return redirect("/"); const studentsAllowedEntities = findAllowedEntities( user, entities, "view_students" ); const [students, assignments] = await Promise.all([ getEntitiesUsers(mapBy(studentsAllowedEntities, "id"), { type: "student" }), getEntitiesAssignments(mapBy(entities, "id")), ]); const [sessions, exams] = await Promise.all([ getSessionsByAssignments(mapBy(assignments, "id")), getExamsByIds(assignments.flatMap((a) => a.exams)), ]); return { props: serialize({ user, students, entities: allowedEntities, assignments, sessions, exams, }), }; }, sessionOptions); interface Item { student: StudentUser; result?: AssignmentResult; assignment: Assignment; exams: Exam[]; entity: Entity; session?: Session; } const columnHelper = createColumnHelper(); export default function Statistical({ user, students, entities, assignments, sessions, exams, }: Props) { const [startDate, setStartDate] = useState(new Date()); const [endDate, setEndDate] = useState( moment().add(1, "month").toDate() ); const [selectedEntities, setSelectedEntities] = useState([]); const [isDownloading, setIsDownloading] = useState(false); const entitiesAllowDownload = useAllowedEntities( user, entities, "download_statistics_report" ); const resetDateRange = () => { const orderedAssignments = orderBy(assignments, ["startDate"], ["asc"]); setStartDate( moment( orderedAssignments.shift()?.startDate || "2024-01-01T00:00:01.986Z" ).toDate() ); setEndDate(moment().add(1, "month").toDate()); }; useEffect(resetDateRange, [assignments]); const updateDateRange = (dates: [Date, Date | null]) => { const [start, end] = dates; setStartDate(start!); setEndDate(end); }; const toggleEntity = (id: string) => setSelectedEntities((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id] ); const renderAssignmentResolution = (entityID: string) => { const entityAssignments = filterBy(assignments, "entity", entityID); const total = entityAssignments.reduce( (acc, curr) => acc + curr.assignees.length, 0 ); const results = entityAssignments.reduce( (acc, curr) => acc + curr.results.length, 0 ); return `${results}/${total}`; }; const totalAssignmentResolution = useMemo(() => { const total = assignments.reduce( (acc, curr) => acc + curr.assignees.length, 0 ); const results = assignments.reduce( (acc, curr) => acc + curr.results.length, 0 ); return { results, total }; }, [assignments]); const filteredAssignments = useMemo(() => { if (!startDate && !endDate) return assignments; const startDateFiltered = startDate ? assignments.filter((a) => moment(a.startDate).isSameOrAfter(moment(startDate)) ) : assignments; return endDate ? startDateFiltered.filter((a) => moment(a.endDate).isSameOrBefore(moment(endDate)) ) : startDateFiltered; }, [startDate, endDate, assignments]); const data: Item[] = useMemo( () => filteredAssignments .filter((a) => selectedEntities.includes(a.entity || "")) .flatMap((a) => a.assignees.map((x) => { const result = findBy(a.results, "user", x); const student = findBy(students, "id", x); const entity = findBy(entities, "id", a.entity); const assignmentExams = exams.filter((e) => a.exams .map((x) => `${x.id}_${x.module}`) .includes(`${e.id}_${e.module}`) ); const session = sessions.find( (s) => s.assignment?.id === a.id && s.user === x ); if (!student) return undefined; return { student, result, assignment: a, exams: assignmentExams, session, entity, }; }) ) .filter((x) => !!x) as Item[], [students, selectedEntities, filteredAssignments, exams, sessions, entities] ); const sortedData: Item[] = useMemo( () => data.sort((a, b) => { const aTotalScore = a.result?.stats .filter((x) => !x.isPractice) .reduce((acc, curr) => acc + curr.score.correct, 0) || 0; const bTotalScore = b.result?.stats .filter((x) => !x.isPractice) .reduce((acc, curr) => acc + curr.score.correct, 0) || 0; return bTotalScore - aTotalScore; }), [data] ); const downloadExcel = async () => { setIsDownloading(true); const request = await axios.post( "/api/statistical", { entities: entities.filter((e) => selectedEntities.includes(e.id)), items: data, assignments: filteredAssignments, startDate, endDate, }, { responseType: "blob", } ); const href = URL.createObjectURL(request.data); const link = document.createElement("a"); link.href = href; link.setAttribute( "download", `statistical_${new Date().toISOString()}.xlsx` ); document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(href); setIsDownloading(false); }; const columns = [ columnHelper.accessor("student.name", { header: "Student", cell: (info) => info.getValue(), }), columnHelper.accessor("student.studentID", { header: "Student ID", cell: (info) => info.getValue(), }), columnHelper.accessor("student.email", { header: "E-mail", cell: (info) => info.getValue(), }), columnHelper.accessor("entity.label", { header: "Entity", cell: (info) => info.getValue(), }), columnHelper.accessor("assignment.name", { header: "Assignment", cell: (info) => info.getValue(), }), columnHelper.accessor("assignment.startDate", { header: "Date", cell: (info) => moment(info.getValue()).format("DD/MM/YYYY"), }), columnHelper.accessor("result", { header: "Progress", cell: (info) => { const student = info.row.original.student; const session = info.row.original.session; if (!student.lastLogin) return Never logged in; if (info.getValue()) return ( Submitted ); if (!session) return Not started; return ( {capitalize(session.exam?.module || "")} Module, Part{" "} {session.partIndex + 1}, Exercise {session.exerciseIndex + 1} ); }, }), ]; return ( <> Statistical | EnCoach <>

Statistical

setSelectedEntities(value ? mapBy(entities, "id") : []) } isChecked={selectedEntities.length === entities.length} > Select All
{entities.map((entity) => ( ))}
{startDate !== null && endDate !== null && ( )}
Total: {totalAssignmentResolution.results} /{" "} {totalAssignmentResolution.total}
{selectedEntities.length > 0 && ( 0 ? downloadExcel : undefined } isDownloadLoading={isDownloading} /> )} ); }