From cdfafb3eea2cc62cdbaac5625e8518d0e89fc5aa Mon Sep 17 00:00:00 2001 From: Joao Ramos Date: Sun, 18 Feb 2024 11:46:08 +0000 Subject: [PATCH] Added approach to archive past assignments --- src/dashboards/AssignmentCard.tsx | 23 ++++++++--- src/dashboards/Teacher.tsx | 9 ++--- src/hooks/useAssignmentArchive.tsx | 45 ++++++++++++++++++++++ src/interfaces/results.ts | 1 + src/pages/api/assignments/[id]/archive.tsx | 33 ++++++++++++++++ 5 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 src/hooks/useAssignmentArchive.tsx create mode 100644 src/pages/api/assignments/[id]/archive.tsx diff --git a/src/dashboards/AssignmentCard.tsx b/src/dashboards/AssignmentCard.tsx index 9744fdd8..f08ae396 100644 --- a/src/dashboards/AssignmentCard.tsx +++ b/src/dashboards/AssignmentCard.tsx @@ -13,11 +13,14 @@ import { 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({ @@ -29,11 +32,14 @@ export default function AssignmentCard({ assignees, results, exams, + archived, onClick, allowDownload, + reload, + allowArchive, }: Assignment & Props) { - const { users } = useUsers(); const renderPdfIcon = usePDFDownload("assignments"); + const renderArchiveIcon = useAssignmentArchive(id, reload); const calculateAverageModuleScore = (module: Module) => { const resultModuleBandScores = results.map((r) => { @@ -41,11 +47,11 @@ export default function AssignmentCard({ const correct = moduleStats.reduce( (acc, curr) => acc + curr.score.correct, - 0, + 0 ); const total = moduleStats.reduce( (acc, curr) => acc + curr.score.total, - 0, + 0 ); return calculateBandScore(correct, total, module, r.type); }); @@ -64,8 +70,13 @@ export default function AssignmentCard({

{name}

- {allowDownload && - renderPdfIcon(id, "text-mti-gray-dim", "text-mti-gray-dim")} +
+ {allowDownload && + renderPdfIcon(id, "text-mti-gray-dim", "text-mti-gray-dim")} + {allowArchive && + !archived && + renderArchiveIcon("text-mti-gray-dim", "text-mti-gray-dim")} +
{module === "reading" && } diff --git a/src/dashboards/Teacher.tsx b/src/dashboards/Teacher.tsx index 4535d724..e7f3036d 100644 --- a/src/dashboards/Teacher.tsx +++ b/src/dashboards/Teacher.tsx @@ -151,9 +151,8 @@ export default function TeacherDashboard({user}: Props) { }; 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; + 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 futureFilter = (a: Assignment) => moment(a.startDate).isAfter(moment()); return ( @@ -235,7 +234,7 @@ export default function TeacherDashboard({user}: Props) {

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

{assignments.filter(pastFilter).map((a) => ( - setSelectedAssignment(a)} key={a.id} allowDownload /> + setSelectedAssignment(a)} key={a.id} allowDownload reload={reloadAssignments} allowArchive/> ))}
@@ -281,7 +280,7 @@ export default function TeacherDashboard({user}: Props) { Assignments - {assignments.length} + {assignments.filter((a) => !a.archived).length}
diff --git a/src/hooks/useAssignmentArchive.tsx b/src/hooks/useAssignmentArchive.tsx new file mode 100644 index 00000000..67879189 --- /dev/null +++ b/src/hooks/useAssignmentArchive.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import axios from "axios"; +import { toast } from "react-toastify"; +import { BsArchive } from "react-icons/bs"; + +export const useAssignmentArchive = ( + assignmentId: string, + reload?: Function +) => { + const [loading, setLoading] = React.useState(false); + const archive = () => { + // archive assignment + setLoading(true); + axios + .post(`/api/assignments/${assignmentId}/archive`) + .then((res) => { + toast.success("Assignment archived!"); + if(reload) reload(); + setLoading(false); + }) + .catch((err) => { + toast.error("Failed to archive the assignment!"); + setLoading(false); + }); + }; + + const renderIcon = (downloadClasses: string, loadingClasses: string) => { + if (loading) { + return ( + + ); + } + return ( + { + e.stopPropagation(); + archive(); + }} + /> + ); + }; + + return renderIcon; +}; diff --git a/src/interfaces/results.ts b/src/interfaces/results.ts index df04ef3c..1f4f4685 100644 --- a/src/interfaces/results.ts +++ b/src/interfaces/results.ts @@ -24,4 +24,5 @@ export interface Assignment { instructorGender?: InstructorGender; startDate: Date; endDate: Date; + archived?: boolean; } diff --git a/src/pages/api/assignments/[id]/archive.tsx b/src/pages/api/assignments/[id]/archive.tsx new file mode 100644 index 00000000..b7e27c94 --- /dev/null +++ b/src/pages/api/assignments/[id]/archive.tsx @@ -0,0 +1,33 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { app } from "@/firebase"; +import { getFirestore, doc, getDoc, setDoc } from "firebase/firestore"; +import { withIronSessionApiRoute } from "iron-session/next"; +import { sessionOptions } from "@/lib/session"; + +const db = getFirestore(app); + +export default withIronSessionApiRoute(handler, sessionOptions); + +async function post(req: NextApiRequest, res: NextApiResponse) { + // verify if it's a logged user that is trying to archive + if (req.session.user) { + const { id } = req.query as { id: string }; + const docSnap = await getDoc(doc(db, "assignments", id)); + + if (!docSnap.exists()) { + res.status(404).json({ ok: false }); + return; + } + + await setDoc(docSnap.ref, { archived: true }, { merge: true }); + res.status(200).json({ ok: true }); + return; + } + + res.status(401).json({ ok: false }); +} + +async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === "POST") return post(req, res); + res.status(404).json({ ok: false }); +}