127 lines
4.1 KiB
TypeScript
127 lines
4.1 KiB
TypeScript
import ProgressBar from "@/components/Low/ProgressBar";
|
|
import useUsers from "@/hooks/useUsers";
|
|
import { Module } from "@/interfaces";
|
|
import { Assignment } from "@/interfaces/results";
|
|
import { calculateBandScore } from "@/utils/score";
|
|
import clsx from "clsx";
|
|
import moment from "moment";
|
|
import {
|
|
BsBook,
|
|
BsClipboard,
|
|
BsHeadphones,
|
|
BsMegaphone,
|
|
BsPen,
|
|
} from "react-icons/bs";
|
|
import { usePDFDownload } from "@/hooks/usePDFDownload";
|
|
import { useAssignmentArchive } from "@/hooks/useAssignmentArchive";
|
|
import { uniqBy } from "lodash";
|
|
|
|
interface Props {
|
|
onClick?: () => void;
|
|
allowDownload?: boolean;
|
|
reload?: Function;
|
|
allowArchive?: boolean;
|
|
}
|
|
|
|
export default function AssignmentCard({
|
|
id,
|
|
name,
|
|
assigner,
|
|
startDate,
|
|
endDate,
|
|
assignees,
|
|
results,
|
|
exams,
|
|
archived,
|
|
onClick,
|
|
allowDownload,
|
|
reload,
|
|
allowArchive,
|
|
}: Assignment & Props) {
|
|
const renderPdfIcon = usePDFDownload("assignments");
|
|
const renderArchiveIcon = useAssignmentArchive(id, reload);
|
|
|
|
const calculateAverageModuleScore = (module: Module) => {
|
|
const resultModuleBandScores = results.map((r) => {
|
|
const moduleStats = r.stats.filter((s) => s.module === module);
|
|
|
|
const correct = moduleStats.reduce(
|
|
(acc, curr) => acc + curr.score.correct,
|
|
0
|
|
);
|
|
const total = moduleStats.reduce(
|
|
(acc, curr) => acc + curr.score.total,
|
|
0
|
|
);
|
|
return calculateBandScore(correct, total, module, r.type);
|
|
});
|
|
|
|
return resultModuleBandScores.length === 0
|
|
? -1
|
|
: resultModuleBandScores.reduce((acc, curr) => acc + curr, 0) /
|
|
results.length;
|
|
};
|
|
|
|
return (
|
|
<div
|
|
onClick={onClick}
|
|
className="border-mti-gray-platinum flex h-fit w-[350px] cursor-pointer flex-col gap-6 rounded-xl border bg-white p-4 transition duration-300 ease-in-out hover:drop-shadow"
|
|
>
|
|
<div className="flex flex-col gap-3">
|
|
<div className="flex flex-row justify-between">
|
|
<h3 className="text-xl font-semibold">{name}</h3>
|
|
<div className="flex gap-2">
|
|
{allowDownload &&
|
|
renderPdfIcon(id, "text-mti-gray-dim", "text-mti-gray-dim")}
|
|
{allowArchive &&
|
|
!archived &&
|
|
renderArchiveIcon("text-mti-gray-dim", "text-mti-gray-dim")}
|
|
</div>
|
|
</div>
|
|
<ProgressBar
|
|
color={results.length / assignees.length < 0.5 ? "red" : "purple"}
|
|
percentage={(results.length / assignees.length) * 100}
|
|
label={`${results.length}/${assignees.length}`}
|
|
className="h-5"
|
|
textClassName={
|
|
results.length / assignees.length < 0.5
|
|
? "!text-mti-gray-dim font-light"
|
|
: "text-white"
|
|
}
|
|
/>
|
|
</div>
|
|
<span className="flex justify-between gap-1">
|
|
<span>{moment(startDate).format("DD/MM/YY, HH:mm")}</span>
|
|
<span>-</span>
|
|
<span>{moment(endDate).format("DD/MM/YY, HH:mm")}</span>
|
|
</span>
|
|
<div className="-md:mt-2 grid w-full grid-cols-4 place-items-start gap-2">
|
|
{uniqBy(exams, (x) => x.module).map(({ module }) => (
|
|
<div
|
|
key={module}
|
|
className={clsx(
|
|
"-md:px-4 flex w-fit items-center gap-2 rounded-xl py-2 text-white md:px-2 xl:px-4",
|
|
module === "reading" && "bg-ielts-reading",
|
|
module === "listening" && "bg-ielts-listening",
|
|
module === "writing" && "bg-ielts-writing",
|
|
module === "speaking" && "bg-ielts-speaking",
|
|
module === "level" && "bg-ielts-level"
|
|
)}
|
|
>
|
|
{module === "reading" && <BsBook className="h-4 w-4" />}
|
|
{module === "listening" && <BsHeadphones className="h-4 w-4" />}
|
|
{module === "writing" && <BsPen className="h-4 w-4" />}
|
|
{module === "speaking" && <BsMegaphone className="h-4 w-4" />}
|
|
{module === "level" && <BsClipboard className="h-4 w-4" />}
|
|
{calculateAverageModuleScore(module) > -1 && (
|
|
<span className="text-sm">
|
|
{calculateAverageModuleScore(module).toFixed(1)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|