diff --git a/src/dashboards/AssignmentCard.tsx b/src/dashboards/AssignmentCard.tsx
index f08ae396..7316c379 100644
--- a/src/dashboards/AssignmentCard.tsx
+++ b/src/dashboards/AssignmentCard.tsx
@@ -1,126 +1,105 @@
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 {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";
+import {BsBook, BsClipboard, BsHeadphones, BsMegaphone, BsPen} from "react-icons/bs";
+import {usePDFDownload} from "@/hooks/usePDFDownload";
+import {useAssignmentArchive} from "@/hooks/useAssignmentArchive";
+import {uniqBy} from "lodash";
+import {useAssignmentUnarchive} from "@/hooks/useAssignmentUnarchive";
interface Props {
- onClick?: () => void;
- allowDownload?: boolean;
- reload?: Function;
- allowArchive?: boolean;
+ onClick?: () => void;
+ allowDownload?: boolean;
+ reload?: Function;
+ allowArchive?: boolean;
+ allowUnarchive?: boolean;
}
export default function AssignmentCard({
- id,
- name,
- assigner,
- startDate,
- endDate,
- assignees,
- results,
- exams,
- archived,
- onClick,
- allowDownload,
- reload,
- allowArchive,
+ id,
+ name,
+ assigner,
+ startDate,
+ endDate,
+ assignees,
+ results,
+ exams,
+ archived,
+ onClick,
+ allowDownload,
+ reload,
+ allowArchive,
+ allowUnarchive,
}: Assignment & Props) {
- const renderPdfIcon = usePDFDownload("assignments");
- const renderArchiveIcon = useAssignmentArchive(id, reload);
+ const renderPdfIcon = usePDFDownload("assignments");
+ const renderArchiveIcon = useAssignmentArchive(id, reload);
+ const renderUnarchiveIcon = useAssignmentUnarchive(id, reload);
- const calculateAverageModuleScore = (module: Module) => {
- const resultModuleBandScores = results.map((r) => {
- const moduleStats = r.stats.filter((s) => s.module === module);
+ 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);
- });
+ 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 resultModuleBandScores.length === 0 ? -1 : resultModuleBandScores.reduce((acc, curr) => acc + curr, 0) / results.length;
+ };
- return (
-
-
-
-
{name}
-
- {allowDownload &&
- renderPdfIcon(id, "text-mti-gray-dim", "text-mti-gray-dim")}
- {allowArchive &&
- !archived &&
- renderArchiveIcon("text-mti-gray-dim", "text-mti-gray-dim")}
-
-
-
-
-
- {moment(startDate).format("DD/MM/YY, HH:mm")}
- -
- {moment(endDate).format("DD/MM/YY, HH:mm")}
-
-
- {uniqBy(exams, (x) => x.module).map(({ module }) => (
-
- {module === "reading" && }
- {module === "listening" && }
- {module === "writing" && }
- {module === "speaking" && }
- {module === "level" && }
- {calculateAverageModuleScore(module) > -1 && (
-
- {calculateAverageModuleScore(module).toFixed(1)}
-
- )}
-
- ))}
-
-
- );
+ return (
+
+
+
+
{name}
+
+ {allowDownload && renderPdfIcon(id, "text-mti-gray-dim", "text-mti-gray-dim")}
+ {allowArchive && !archived && renderArchiveIcon("text-mti-gray-dim", "text-mti-gray-dim")}
+ {allowUnarchive && archived && renderUnarchiveIcon("text-mti-gray-dim", "text-mti-gray-dim")}
+
+
+
+
+
+ {moment(startDate).format("DD/MM/YY, HH:mm")}
+ -
+ {moment(endDate).format("DD/MM/YY, HH:mm")}
+
+
+ {uniqBy(exams, (x) => x.module).map(({module}) => (
+
+ {module === "reading" && }
+ {module === "listening" && }
+ {module === "writing" && }
+ {module === "speaking" && }
+ {module === "level" && }
+ {calculateAverageModuleScore(module) > -1 && (
+ {calculateAverageModuleScore(module).toFixed(1)}
+ )}
+
+ ))}
+
+
+ );
}
diff --git a/src/dashboards/Teacher.tsx b/src/dashboards/Teacher.tsx
index e7f3036d..f5c64125 100644
--- a/src/dashboards/Teacher.tsx
+++ b/src/dashboards/Teacher.tsx
@@ -151,8 +151,10 @@ 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 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 archivedFilter = (a: Assignment) => a.archived;
const futureFilter = (a: Assignment) => moment(a.startDate).isAfter(moment());
return (
@@ -234,7 +236,29 @@ export default function TeacherDashboard({user}: Props) {
Past Assignments ({assignments.filter(pastFilter).length})
{assignments.filter(pastFilter).map((a) => (
-
setSelectedAssignment(a)} key={a.id} allowDownload reload={reloadAssignments} allowArchive/>
+ setSelectedAssignment(a)}
+ key={a.id}
+ allowDownload
+ reload={reloadAssignments}
+ allowArchive
+ />
+ ))}
+
+
+
+ Archived Assignments ({assignments.filter(archivedFilter).length})
+
+ {assignments.filter(archivedFilter).map((a) => (
+
setSelectedAssignment(a)}
+ key={a.id}
+ allowDownload
+ reload={reloadAssignments}
+ allowUnarchive
+ />
))}
diff --git a/src/hooks/useAssignmentArchive.tsx b/src/hooks/useAssignmentArchive.tsx
index 67879189..397ee2a2 100644
--- a/src/hooks/useAssignmentArchive.tsx
+++ b/src/hooks/useAssignmentArchive.tsx
@@ -1,45 +1,42 @@
import React from "react";
import axios from "axios";
-import { toast } from "react-toastify";
-import { BsArchive } from "react-icons/bs";
+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);
- });
- };
+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();
- }}
- />
- );
- };
+ const renderIcon = (downloadClasses: string, loadingClasses: string) => {
+ if (loading) {
+ return ;
+ }
+ return (
+ {
+ e.stopPropagation();
+ archive();
+ }}>
+
+
+ );
+ };
- return renderIcon;
+ return renderIcon;
};
diff --git a/src/hooks/useAssignmentUnarchive.tsx b/src/hooks/useAssignmentUnarchive.tsx
new file mode 100644
index 00000000..7612b504
--- /dev/null
+++ b/src/hooks/useAssignmentUnarchive.tsx
@@ -0,0 +1,42 @@
+import React from "react";
+import axios from "axios";
+import {toast} from "react-toastify";
+import {BsArchive, BsFileEarmarkCheck, BsFileEarmarkCheckFill} from "react-icons/bs";
+
+export const useAssignmentUnarchive = (assignmentId: string, reload?: Function) => {
+ const [loading, setLoading] = React.useState(false);
+ const archive = () => {
+ // archive assignment
+ setLoading(true);
+ axios
+ .post(`/api/assignments/${assignmentId}/unarchive`)
+ .then((res) => {
+ toast.success("Assignment unarchived!");
+ if (reload) reload();
+ setLoading(false);
+ })
+ .catch((err) => {
+ toast.error("Failed to unarchive the assignment!");
+ setLoading(false);
+ });
+ };
+
+ const renderIcon = (downloadClasses: string, loadingClasses: string) => {
+ if (loading) {
+ return ;
+ }
+ return (
+ {
+ e.stopPropagation();
+ archive();
+ }}>
+
+
+ );
+ };
+
+ return renderIcon;
+};
diff --git a/src/pages/api/assignments/[id]/unarchive.tsx b/src/pages/api/assignments/[id]/unarchive.tsx
new file mode 100644
index 00000000..cda7498f
--- /dev/null
+++ b/src/pages/api/assignments/[id]/unarchive.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: false}, {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});
+}