ENCOA-86: Added an option to release exam results
This commit is contained in:
@@ -10,6 +10,7 @@ import {usePDFDownload} from "@/hooks/usePDFDownload";
|
||||
import {useAssignmentArchive} from "@/hooks/useAssignmentArchive";
|
||||
import {uniqBy} from "lodash";
|
||||
import {useAssignmentUnarchive} from "@/hooks/useAssignmentUnarchive";
|
||||
import {useAssignmentRelease} from "@/hooks/useAssignmentRelease";
|
||||
import {getUserName} from "@/utils/users";
|
||||
import {User} from "@/interfaces/user";
|
||||
|
||||
@@ -40,11 +41,14 @@ export default function AssignmentCard({
|
||||
allowUnarchive,
|
||||
allowExcelDownload,
|
||||
users,
|
||||
released,
|
||||
}: Assignment & Props) {
|
||||
const renderPdfIcon = usePDFDownload("assignments");
|
||||
const renderExcelIcon = usePDFDownload("assignments", "excel");
|
||||
const renderArchiveIcon = useAssignmentArchive(id, reload);
|
||||
const renderUnarchiveIcon = useAssignmentUnarchive(id, reload);
|
||||
const renderReleaseIcon = useAssignmentRelease(id, reload);
|
||||
|
||||
|
||||
const calculateAverageModuleScore = (module: Module) => {
|
||||
const resultModuleBandScores = results.map((r) => {
|
||||
@@ -66,10 +70,11 @@ export default function AssignmentCard({
|
||||
<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")}
|
||||
{allowExcelDownload && renderExcelIcon(id, "text-mti-gray-dim", "text-mti-gray-dim")}
|
||||
{allowDownload && released && renderPdfIcon(id, "text-mti-gray-dim", "text-mti-gray-dim")}
|
||||
{allowExcelDownload && released && renderExcelIcon(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")}
|
||||
{!released && renderReleaseIcon("text-mti-gray-dim", "text-mti-gray-dim")}
|
||||
</div>
|
||||
</div>
|
||||
<ProgressBar
|
||||
|
||||
42
src/hooks/useAssignmentRelease.tsx
Normal file
42
src/hooks/useAssignmentRelease.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from "react";
|
||||
import axios from "axios";
|
||||
import {toast} from "react-toastify";
|
||||
import {BsDoorOpen} from "react-icons/bs";
|
||||
|
||||
export const useAssignmentRelease = (assignmentId: string, reload?: Function) => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const archive = () => {
|
||||
// archive assignment
|
||||
setLoading(true);
|
||||
axios
|
||||
.post(`/api/assignments/${assignmentId}/release`)
|
||||
.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 <span className={`${loadingClasses} loading loading-infinity w-6`} />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="tooltip flex items-center justify-center w-fit h-fit"
|
||||
data-tip="Release assignment"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
archive();
|
||||
}}>
|
||||
<BsDoorOpen className={`${downloadClasses} text-2xl cursor-pointer tooltip`} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return renderIcon;
|
||||
};
|
||||
@@ -25,4 +25,5 @@ export interface Assignment {
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
archived?: boolean;
|
||||
released?: boolean;
|
||||
}
|
||||
|
||||
33
src/pages/api/assignments/[id]/release.ts
Normal file
33
src/pages/api/assignments/[id]/release.ts
Normal file
@@ -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, { released: 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 });
|
||||
}
|
||||
Reference in New Issue
Block a user