Imporvements on started management of assignment

This commit is contained in:
Joao Ramos
2024-09-03 22:56:43 +01:00
parent 8aed075553
commit a7c1ea0409
3 changed files with 630 additions and 434 deletions

View File

@@ -16,8 +16,15 @@ import clsx from "clsx";
import { capitalize, uniqBy } from "lodash"; import { capitalize, uniqBy } from "lodash";
import moment from "moment"; import moment from "moment";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import {BsBook, BsClipboard, BsHeadphones, BsMegaphone, BsPen} from "react-icons/bs"; import {
BsBook,
BsClipboard,
BsHeadphones,
BsMegaphone,
BsPen,
} from "react-icons/bs";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { activeAssignmentFilter } from "@/utils/assignments";
interface Props { interface Props {
isOpen: boolean; isOpen: boolean;
@@ -39,7 +46,11 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
axios axios
.delete(`/api/assignments/${assignment?.id}`) .delete(`/api/assignments/${assignment?.id}`)
.then(() => toast.success(`Successfully deleted the assignment "${assignment?.name}".`)) .then(() =>
toast.success(
`Successfully deleted the assignment "${assignment?.name}".`
)
)
.catch(() => toast.error("Something went wrong, please try again later.")) .catch(() => toast.error("Something went wrong, please try again later."))
.finally(onClose); .finally(onClose);
}; };
@@ -49,7 +60,9 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
axios axios
.post(`/api/assignments/${assignment.id}/start`) .post(`/api/assignments/${assignment.id}/start`)
.then(() => { .then(() => {
toast.success(`The assignment "${assignment.name}" has been started successfully!`); toast.success(
`The assignment "${assignment.name}" has been started successfully!`
);
}) })
.catch((e) => { .catch((e) => {
console.log(e); console.log(e);
@@ -71,15 +84,26 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
const resultModuleBandScores = assignment.results.map((r) => { const resultModuleBandScores = assignment.results.map((r) => {
const moduleStats = r.stats.filter((s) => s.module === module); const moduleStats = r.stats.filter((s) => s.module === module);
const correct = moduleStats.reduce((acc, curr) => acc + curr.score.correct, 0); const correct = moduleStats.reduce(
const total = moduleStats.reduce((acc, curr) => acc + curr.score.total, 0); (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 calculateBandScore(correct, total, module, r.type);
}); });
return resultModuleBandScores.length === 0 ? -1 : resultModuleBandScores.reduce((acc, curr) => acc + curr, 0) / assignment.results.length; return resultModuleBandScores.length === 0
? -1
: resultModuleBandScores.reduce((acc, curr) => acc + curr, 0) /
assignment.results.length;
}; };
const aggregateScoresByModule = (stats: Stat[]): {module: Module; total: number; missing: number; correct: number}[] => { const aggregateScoresByModule = (
stats: Stat[]
): { module: Module; total: number; missing: number; correct: number }[] => {
const scores: { const scores: {
[key in Module]: { total: number; missing: number; correct: number }; [key in Module]: { total: number; missing: number; correct: number };
} = { } = {
@@ -123,10 +147,22 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
.map((x) => ({ module: x as Module, ...scores[x as Module] })); .map((x) => ({ module: x as Module, ...scores[x as Module] }));
}; };
const customContent = (stats: Stat[], user: string, focus: "academic" | "general") => { const customContent = (
const correct = stats.reduce((accumulator, current) => accumulator + current.score.correct, 0); stats: Stat[],
const total = stats.reduce((accumulator, current) => accumulator + current.score.total, 0); user: string,
const aggregatedScores = aggregateScoresByModule(stats).filter((x) => x.total > 0); focus: "academic" | "general"
) => {
const correct = stats.reduce(
(accumulator, current) => accumulator + current.score.correct,
0
);
const total = stats.reduce(
(accumulator, current) => accumulator + current.score.total,
0
);
const aggregatedScores = aggregateScoresByModule(stats).filter(
(x) => x.total > 0
);
const aggregatedLevels = aggregatedScores.map((x) => ({ const aggregatedLevels = aggregatedScores.map((x) => ({
module: x.module, module: x.module,
@@ -136,7 +172,9 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
const timeSpent = stats[0].timeSpent; const timeSpent = stats[0].timeSpent;
const selectExam = () => { const selectExam = () => {
const examPromises = uniqBy(stats, "exam").map((stat) => getExamById(stat.module, stat.exam)); const examPromises = uniqBy(stats, "exam").map((stat) =>
getExamById(stat.module, stat.exam)
);
Promise.all(examPromises).then((exams) => { Promise.all(examPromises).then((exams) => {
if (exams.every((x) => !!x)) { if (exams.every((x) => !!x)) {
@@ -147,7 +185,7 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
exams exams
.map((x) => x!) .map((x) => x!)
.sort(sortByModule) .sort(sortByModule)
.map((x) => x!.module), .map((x) => x!.module)
); );
router.push("/exercises"); router.push("/exercises");
} }
@@ -158,11 +196,15 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
<> <>
<div className="-md:items-center flex w-full justify-between 2xl:items-center"> <div className="-md:items-center flex w-full justify-between 2xl:items-center">
<div className="-md:gap-2 -md:items-center flex md:flex-col md:gap-1 2xl:flex-row 2xl:items-center 2xl:gap-2"> <div className="-md:gap-2 -md:items-center flex md:flex-col md:gap-1 2xl:flex-row 2xl:items-center 2xl:gap-2">
<span className="font-medium">{formatTimestamp(stats[0].date.toString())}</span> <span className="font-medium">
{formatTimestamp(stats[0].date.toString())}
</span>
{timeSpent && ( {timeSpent && (
<> <>
<span className="md:hidden 2xl:flex"> </span> <span className="md:hidden 2xl:flex"> </span>
<span className="text-sm">{Math.floor(timeSpent / 60)} minutes</span> <span className="text-sm">
{Math.floor(timeSpent / 60)} minutes
</span>
</> </>
)} )}
</div> </div>
@@ -170,10 +212,16 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
className={clsx( className={clsx(
correct / total >= 0.7 && "text-mti-purple", correct / total >= 0.7 && "text-mti-purple",
correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red", correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red",
correct / total < 0.3 && "text-mti-rose", correct / total < 0.3 && "text-mti-rose"
)}> )}
>
Level{" "} Level{" "}
{(aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length).toFixed(1)} {(
aggregatedLevels.reduce(
(accumulator, current) => accumulator + current.level,
0
) / aggregatedLevels.length
).toFixed(1)}
</span> </span>
</div> </div>
@@ -188,8 +236,9 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
module === "listening" && "bg-ielts-listening", module === "listening" && "bg-ielts-listening",
module === "writing" && "bg-ielts-writing", module === "writing" && "bg-ielts-writing",
module === "speaking" && "bg-ielts-speaking", module === "speaking" && "bg-ielts-speaking",
module === "level" && "bg-ielts-level", module === "level" && "bg-ielts-level"
)}> )}
>
{module === "reading" && <BsBook className="h-4 w-4" />} {module === "reading" && <BsBook className="h-4 w-4" />}
{module === "listening" && <BsHeadphones className="h-4 w-4" />} {module === "listening" && <BsHeadphones className="h-4 w-4" />}
{module === "writing" && <BsPen className="h-4 w-4" />} {module === "writing" && <BsPen className="h-4 w-4" />}
@@ -216,11 +265,14 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
className={clsx( className={clsx(
"border-mti-gray-platinum -md:hidden flex cursor-pointer flex-col gap-4 rounded-xl border p-4 transition duration-300 ease-in-out", "border-mti-gray-platinum -md:hidden flex cursor-pointer flex-col gap-4 rounded-xl border p-4 transition duration-300 ease-in-out",
correct / total >= 0.7 && "hover:border-mti-purple", correct / total >= 0.7 && "hover:border-mti-purple",
correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red", correct / total >= 0.3 &&
correct / total < 0.3 && "hover:border-mti-rose", correct / total < 0.7 &&
"hover:border-mti-red",
correct / total < 0.3 && "hover:border-mti-rose"
)} )}
onClick={selectExam} onClick={selectExam}
role="button"> role="button"
>
{content} {content}
</div> </div>
<div <div
@@ -228,17 +280,32 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
className={clsx( className={clsx(
"border-mti-gray-platinum -md:tooltip flex cursor-pointer flex-col gap-4 rounded-xl border p-4 transition duration-300 ease-in-out md:hidden", "border-mti-gray-platinum -md:tooltip flex cursor-pointer flex-col gap-4 rounded-xl border p-4 transition duration-300 ease-in-out md:hidden",
correct / total >= 0.7 && "hover:border-mti-purple", correct / total >= 0.7 && "hover:border-mti-purple",
correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red", correct / total >= 0.3 &&
correct / total < 0.3 && "hover:border-mti-rose", correct / total < 0.7 &&
"hover:border-mti-red",
correct / total < 0.3 && "hover:border-mti-rose"
)} )}
data-tip="Your screen size is too small to view previous exams." data-tip="Your screen size is too small to view previous exams."
role="button"> role="button"
>
{content} {content}
</div> </div>
</div> </div>
); );
}; };
const shouldRenderStart = () => {
if (assignment) {
if (activeAssignmentFilter(assignment)) {
return false;
}
return assignment.results.length === 0;
}
return false;
};
return ( return (
<Modal isOpen={isOpen} onClose={onClose} title={assignment?.name}> <Modal isOpen={isOpen} onClose={onClose} title={assignment?.name}>
<div className="mt-4 flex w-full flex-col gap-4"> <div className="mt-4 flex w-full flex-col gap-4">
@@ -247,14 +314,27 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
label={`${assignment?.results.length}/${assignment?.assignees.length} assignees completed`} label={`${assignment?.results.length}/${assignment?.assignees.length} assignees completed`}
className="h-6" className="h-6"
textClassName={ textClassName={
(assignment?.results.length || 0) / (assignment?.assignees.length || 1) < 0.5 ? "!text-mti-gray-dim font-light" : "text-white" (assignment?.results.length || 0) /
(assignment?.assignees.length || 1) <
0.5
? "!text-mti-gray-dim font-light"
: "text-white"
}
percentage={
((assignment?.results.length || 0) /
(assignment?.assignees.length || 1)) *
100
} }
percentage={((assignment?.results.length || 0) / (assignment?.assignees.length || 1)) * 100}
/> />
<div className="flex items-start gap-8"> <div className="flex items-start gap-8">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<span>Start Date: {moment(assignment?.startDate).format("DD/MM/YY, HH:mm")}</span> <span>
<span>End Date: {moment(assignment?.endDate).format("DD/MM/YY, HH:mm")}</span> Start Date:{" "}
{moment(assignment?.startDate).format("DD/MM/YY, HH:mm")}
</span>
<span>
End Date: {moment(assignment?.endDate).format("DD/MM/YY, HH:mm")}
</span>
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<span> <span>
@@ -264,7 +344,10 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
.map((u) => `${u.name} (${u.email})`) .map((u) => `${u.name} (${u.email})`)
.join(", ")} .join(", ")}
</span> </span>
<span>Assigner: {getUserName(users.find((x) => x.id === assignment?.assigner))}</span> <span>
Assigner:{" "}
{getUserName(users.find((x) => x.id === assignment?.assigner))}
</span>
</div> </div>
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
@@ -281,15 +364,20 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
module === "listening" && "bg-ielts-listening", module === "listening" && "bg-ielts-listening",
module === "writing" && "bg-ielts-writing", module === "writing" && "bg-ielts-writing",
module === "speaking" && "bg-ielts-speaking", module === "speaking" && "bg-ielts-speaking",
module === "level" && "bg-ielts-level", module === "level" && "bg-ielts-level"
)}> )}
>
{module === "reading" && <BsBook className="h-4 w-4" />} {module === "reading" && <BsBook className="h-4 w-4" />}
{module === "listening" && <BsHeadphones className="h-4 w-4" />} {module === "listening" && (
<BsHeadphones className="h-4 w-4" />
)}
{module === "writing" && <BsPen className="h-4 w-4" />} {module === "writing" && <BsPen className="h-4 w-4" />}
{module === "speaking" && <BsMegaphone className="h-4 w-4" />} {module === "speaking" && <BsMegaphone className="h-4 w-4" />}
{module === "level" && <BsClipboard className="h-4 w-4" />} {module === "level" && <BsClipboard className="h-4 w-4" />}
{calculateAverageModuleScore(module) > -1 && ( {calculateAverageModuleScore(module) > -1 && (
<span className="text-sm">{calculateAverageModuleScore(module).toFixed(1)}</span> <span className="text-sm">
{calculateAverageModuleScore(module).toFixed(1)}
</span>
)} )}
</div> </div>
))} ))}
@@ -297,26 +385,44 @@ export default function AssignmentView({isOpen, assignment, onClose}: Props) {
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<span className="text-xl font-bold"> <span className="text-xl font-bold">
Results ({assignment?.results.length}/{assignment?.assignees.length}) Results ({assignment?.results.length}/{assignment?.assignees.length}
)
</span> </span>
<div> <div>
{assignment && assignment?.results.length > 0 && ( {assignment && assignment?.results.length > 0 && (
<div className="grid w-full grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3 xl:gap-6"> <div className="grid w-full grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3 xl:gap-6">
{assignment.results.map((r) => customContent(r.stats, r.user, r.type))} {assignment.results.map((r) =>
customContent(r.stats, r.user, r.type)
)}
</div> </div>
)} )}
{assignment && assignment?.results.length === 0 && <span className="ml-1 font-semibold">No results yet...</span>} {assignment && assignment?.results.length === 0 && (
<span className="ml-1 font-semibold">No results yet...</span>
)}
</div> </div>
</div> </div>
<div className="flex gap-4 w-full items-center justify-end"> <div className="flex gap-4 w-full items-center justify-end">
{assignment && (assignment.results.length === assignment.assignees.length || moment().isAfter(moment(assignment.endDate))) && ( {assignment &&
<Button variant="outline" color="red" className="w-full max-w-[200px]" onClick={deleteAssignment}> (assignment.results.length === assignment.assignees.length ||
moment().isAfter(moment(assignment.endDate))) && (
<Button
variant="outline"
color="red"
className="w-full max-w-[200px]"
onClick={deleteAssignment}
>
Delete Delete
</Button> </Button>
)} )}
{assignment && (assignment.results.length === 0 || moment().isAfter(moment(assignment.startDate))) && ( {/** if the assignment is not deemed as active yet, display start */}
<Button variant="outline" color="green" className="w-full max-w-[200px]" onClick={startAssignment}> {shouldRenderStart() && (
<Button
variant="outline"
color="green"
className="w-full max-w-[200px]"
onClick={startAssignment}
>
Start Start
</Button> </Button>
)} )}

View File

@@ -1,7 +1,13 @@
import { Assignment } from "@/interfaces/results"; import { Assignment } from "@/interfaces/results";
import { CorporateUser, Group, User } from "@/interfaces/user"; import { CorporateUser, Group, User } from "@/interfaces/user";
import { getUserCompanyName } from "@/resources/user"; import { getUserCompanyName } from "@/resources/user";
import {activeAssignmentFilter, archivedAssignmentFilter, futureAssignmentFilter, pastAssignmentFilter} from "@/utils/assignments"; import {
activeAssignmentFilter,
archivedAssignmentFilter,
futureAssignmentFilter,
pastAssignmentFilter,
unstartedAssignmentFilter,
} from "@/utils/assignments";
import clsx from "clsx"; import clsx from "clsx";
import { groupBy } from "lodash"; import { groupBy } from "lodash";
import { useState } from "react"; import { useState } from "react";
@@ -21,14 +27,28 @@ interface Props {
reloadAssignments: () => void; reloadAssignments: () => void;
} }
export default function AssignmentsPage({assignments, corporateAssignments, user, groups, users, isLoading, onBack, reloadAssignments}: Props) { export default function AssignmentsPage({
assignments,
corporateAssignments,
user,
groups,
users,
isLoading,
onBack,
reloadAssignments,
}: Props) {
const [selectedAssignment, setSelectedAssignment] = useState<Assignment>(); const [selectedAssignment, setSelectedAssignment] = useState<Assignment>();
const [isCreatingAssignment, setIsCreatingAssignment] = useState(false); const [isCreatingAssignment, setIsCreatingAssignment] = useState(false);
const unstartedAssignments = assignments.filter(unstartedAssignmentFilter);
const displayAssignmentView = !!selectedAssignment && !isCreatingAssignment;
return ( return (
<> <>
{displayAssignmentView && (
<AssignmentView <AssignmentView
isOpen={!!selectedAssignment && !isCreatingAssignment} isOpen={displayAssignmentView}
onClose={() => { onClose={() => {
setSelectedAssignment(undefined); setSelectedAssignment(undefined);
setIsCreatingAssignment(false); setIsCreatingAssignment(false);
@@ -36,8 +56,10 @@ export default function AssignmentsPage({assignments, corporateAssignments, user
}} }}
assignment={selectedAssignment} assignment={selectedAssignment}
/> />
)}
{/** I'll be using this is creating assingment as a workaround for a key to trigger a new rendering */} {/** I'll be using this is creating assingment as a workaround for a key to trigger a new rendering */}
{isCreatingAssignment && <AssignmentCreator {isCreatingAssignment && (
<AssignmentCreator
assignment={selectedAssignment} assignment={selectedAssignment}
groups={groups} groups={groups}
users={users} users={users}
@@ -48,53 +70,105 @@ export default function AssignmentsPage({assignments, corporateAssignments, user
setSelectedAssignment(undefined); setSelectedAssignment(undefined);
reloadAssignments(); reloadAssignments();
}} }}
/>} />
)}
<div className="w-full flex justify-between items-center"> <div className="w-full flex justify-between items-center">
<div <div
onClick={onBack} onClick={onBack}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
>
<BsArrowLeft className="text-xl" /> <BsArrowLeft className="text-xl" />
<span>Back</span> <span>Back</span>
</div> </div>
<div <div
onClick={reloadAssignments} onClick={reloadAssignments}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
>
<span>Reload</span> <span>Reload</span>
<BsArrowRepeat className={clsx("text-xl", isLoading && "animate-spin")} /> <BsArrowRepeat
className={clsx("text-xl", isLoading && "animate-spin")}
/>
</div> </div>
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<span className="text-lg font-bold">Active Assignments Status</span> <span className="text-lg font-bold">Active Assignments Status</span>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<span> <span>
<b>Total:</b> {assignments.filter(activeAssignmentFilter).reduce((acc, curr) => acc + curr.results.length, 0)}/ <b>Total:</b>{" "}
{assignments.filter(activeAssignmentFilter).reduce((acc, curr) => curr.exams.length + acc, 0)} {assignments
.filter(activeAssignmentFilter)
.reduce((acc, curr) => acc + curr.results.length, 0)}
/
{assignments
.filter(activeAssignmentFilter)
.reduce((acc, curr) => curr.exams.length + acc, 0)}
</span> </span>
{Object.keys(groupBy(corporateAssignments, (x) => x.corporate?.id)).map((x) => ( {Object.keys(
groupBy(corporateAssignments, (x) => x.corporate?.id)
).map((x) => (
<div key={x}> <div key={x}>
<span className="font-semibold">{getUserCompanyName(users.find((u) => u.id === x)!, users, groups)}: </span> <span className="font-semibold">
{getUserCompanyName(
users.find((u) => u.id === x)!,
users,
groups
)}
:{" "}
</span>
<span> <span>
{groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.results.length + acc, 0)}/ {groupBy(corporateAssignments, (x) => x.corporate?.id)[
{groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.exams.length + acc, 0)} x
].reduce((acc, curr) => curr.results.length + acc, 0)}
/
{groupBy(corporateAssignments, (x) => x.corporate?.id)[
x
].reduce((acc, curr) => curr.exams.length + acc, 0)}
</span> </span>
</div> </div>
))} ))}
</div> </div>
</div> </div>
<section className="flex flex-col gap-4"> <section className="flex flex-col gap-4">
<h2 className="text-2xl font-semibold">Active Assignments ({assignments.filter(activeAssignmentFilter).length})</h2> <h2 className="text-2xl font-semibold">
Active Assignments (
{assignments.filter(activeAssignmentFilter).length})
</h2>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{assignments.filter(activeAssignmentFilter).map((a) => ( {assignments.filter(activeAssignmentFilter).map((a) => (
<AssignmentCard {...a} users={users} onClick={() => setSelectedAssignment(a)} key={a.id} /> <AssignmentCard
{...a}
users={users}
onClick={() => setSelectedAssignment(a)}
key={a.id}
/>
))} ))}
</div> </div>
</section> </section>
<section className="flex flex-col gap-4"> <section className="flex flex-col gap-4">
<h2 className="text-2xl font-semibold">Planned Assignments ({assignments.filter(futureAssignmentFilter).length})</h2> <h2 className="text-2xl font-semibold">
Active Assignments Pending Start ({unstartedAssignments.length})
</h2>
<div className="flex flex-wrap gap-2">
{unstartedAssignments.map((a) => (
<AssignmentCard
{...a}
users={users}
onClick={() => setSelectedAssignment(a)}
key={a.id}
/>
))}
</div>
</section>
<section className="flex flex-col gap-4">
<h2 className="text-2xl font-semibold">
Planned Assignments (
{assignments.filter(futureAssignmentFilter).length})
</h2>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<div <div
onClick={() => setIsCreatingAssignment(true)} onClick={() => 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"> 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"
>
<BsPlus className="text-6xl" /> <BsPlus className="text-6xl" />
<span className="text-lg">New Assignment</span> <span className="text-lg">New Assignment</span>
</div> </div>
@@ -112,7 +186,9 @@ export default function AssignmentsPage({assignments, corporateAssignments, user
</div> </div>
</section> </section>
<section className="flex flex-col gap-4"> <section className="flex flex-col gap-4">
<h2 className="text-2xl font-semibold">Past Assignments ({assignments.filter(pastAssignmentFilter).length})</h2> <h2 className="text-2xl font-semibold">
Past Assignments ({assignments.filter(pastAssignmentFilter).length})
</h2>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{assignments.filter(pastAssignmentFilter).map((a) => ( {assignments.filter(pastAssignmentFilter).map((a) => (
<AssignmentCard <AssignmentCard
@@ -129,7 +205,10 @@ export default function AssignmentsPage({assignments, corporateAssignments, user
</div> </div>
</section> </section>
<section className="flex flex-col gap-4"> <section className="flex flex-col gap-4">
<h2 className="text-2xl font-semibold">Archived Assignments ({assignments.filter(archivedAssignmentFilter).length})</h2> <h2 className="text-2xl font-semibold">
Archived Assignments (
{assignments.filter(archivedAssignmentFilter).length})
</h2>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{assignments.filter(archivedAssignmentFilter).map((a) => ( {assignments.filter(archivedAssignmentFilter).map((a) => (
<AssignmentCard <AssignmentCard

View File

@@ -38,3 +38,14 @@ export const activeAssignmentFilter = (a: Assignment) => {
return false; return false;
}; };
export const unstartedAssignmentFilter = (a: Assignment) => {
const currentDate = moment();
if(moment(a.endDate).isBefore(currentDate)) return false;
if(a.archived) return false;
if(a.autoStart && a.autoStartDate && moment(a.autoStartDate).isBefore(currentDate)) return false;
if(!a.start) return true;
return false;
}