Added pdf download to record page
Reenabled reuse of PDF
This commit is contained in:
@@ -14,12 +14,10 @@ import { withIronSessionApiRoute } from "iron-session/next";
|
|||||||
import { sessionOptions } from "@/lib/session";
|
import { sessionOptions } from "@/lib/session";
|
||||||
import ReactPDF from "@react-pdf/renderer";
|
import ReactPDF from "@react-pdf/renderer";
|
||||||
import TestReport from "@/exams/pdf/test.report";
|
import TestReport from "@/exams/pdf/test.report";
|
||||||
import { ref, uploadBytes } from "firebase/storage";
|
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
|
||||||
import { Stat } from "@/interfaces/user";
|
|
||||||
import { User } from "@/interfaces/user";
|
import { User } from "@/interfaces/user";
|
||||||
import { Module } from "@/interfaces";
|
import { Module } from "@/interfaces";
|
||||||
import { ModuleScore } from "@/interfaces/module.scores";
|
import { ModuleScore } from "@/interfaces/module.scores";
|
||||||
import qrcode from "qrcode";
|
|
||||||
import { SkillExamDetails } from "@/exams/pdf/details/skill.exam";
|
import { SkillExamDetails } from "@/exams/pdf/details/skill.exam";
|
||||||
import { LevelExamDetails } from "@/exams/pdf/details/level.exam";
|
import { LevelExamDetails } from "@/exams/pdf/details/level.exam";
|
||||||
import { calculateBandScore } from "@/utils/score";
|
import { calculateBandScore } from "@/utils/score";
|
||||||
@@ -146,14 +144,17 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const stats = docsSnap.docs.map((d) => d.data());
|
const stats = docsSnap.docs.map((d) => d.data());
|
||||||
// TODO: verify if the stats already have a pdf generated
|
// verify if the stats already have a pdf generated
|
||||||
// const hasPDF = stats.find((s) => s.pdf);
|
const hasPDF = stats.find((s) => s.pdf);
|
||||||
|
|
||||||
// if (hasPDF) {
|
if (hasPDF) {
|
||||||
// // if it does, return the pdf url
|
// if it does, return the pdf url
|
||||||
// res.status(200).end(hasPDF.pdf);
|
const fileRef = ref(storage, hasPDF.pdf);
|
||||||
// return;
|
const url = await getDownloadURL(fileRef);
|
||||||
// }
|
|
||||||
|
res.status(200).end(url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// generate the pdf report
|
// generate the pdf report
|
||||||
@@ -318,7 +319,8 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
|
|
||||||
// generate the file ref for storage
|
// generate the file ref for storage
|
||||||
const fileName = `${Date.now().toString()}.pdf`;
|
const fileName = `${Date.now().toString()}.pdf`;
|
||||||
const fileRef = ref(storage, `exam_report/${fileName}`);
|
const refName = `exam_report/${fileName}`;
|
||||||
|
const fileRef = ref(storage, refName);
|
||||||
|
|
||||||
// upload the pdf to storage
|
// upload the pdf to storage
|
||||||
const pdfBuffer = await streamToBuffer(pdfStream);
|
const pdfBuffer = await streamToBuffer(pdfStream);
|
||||||
@@ -329,10 +331,11 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
// update the stats entries with the pdf url to prevent duplication
|
// update the stats entries with the pdf url to prevent duplication
|
||||||
docsSnap.docs.forEach(async (doc) => {
|
docsSnap.docs.forEach(async (doc) => {
|
||||||
await updateDoc(doc.ref, {
|
await updateDoc(doc.ref, {
|
||||||
pdf: snapshot.ref.fullPath,
|
pdf: refName,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
res.status(200).end(snapshot.ref.fullPath);
|
const url = await getDownloadURL(fileRef);
|
||||||
|
res.status(200).end(url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,10 +364,12 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
|
|
||||||
const stats = docsSnap.docs.map((d) => d.data());
|
const stats = docsSnap.docs.map((d) => d.data());
|
||||||
|
|
||||||
const pdfUrl = stats.find((s) => s.pdf);
|
const hasPDF = stats.find((s) => s.pdf);
|
||||||
|
|
||||||
if (pdfUrl) {
|
if (hasPDF) {
|
||||||
return res.end(pdfUrl);
|
const fileRef = ref(storage, hasPDF.pdf);
|
||||||
|
const url = await getDownloadURL(fileRef);
|
||||||
|
return res.redirect(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(500).end();
|
res.status(500).end();
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import useGroups from "@/hooks/useGroups";
|
|||||||
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
||||||
import useAssignments from "@/hooks/useAssignments";
|
import useAssignments from "@/hooks/useAssignments";
|
||||||
import {uuidv4} from "@firebase/util";
|
import {uuidv4} from "@firebase/util";
|
||||||
|
import { BsFilePdf } from "react-icons/bs";
|
||||||
|
import axios from "axios";
|
||||||
|
import {toast} from "react-toastify";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
@@ -55,6 +58,10 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
|
type DownloadingPdf = {
|
||||||
|
[key: string]: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export default function History({user}: {user: User}) {
|
export default function History({user}: {user: User}) {
|
||||||
const [statsUserId, setStatsUserId] = useState<string | undefined>(user.id);
|
const [statsUserId, setStatsUserId] = useState<string | undefined>(user.id);
|
||||||
const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>();
|
const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>();
|
||||||
@@ -69,7 +76,7 @@ export default function History({user}: {user: User}) {
|
|||||||
const setShowSolutions = useExamStore((state) => state.setShowSolutions);
|
const setShowSolutions = useExamStore((state) => state.setShowSolutions);
|
||||||
const setUserSolutions = useExamStore((state) => state.setUserSolutions);
|
const setUserSolutions = useExamStore((state) => state.setUserSolutions);
|
||||||
const setSelectedModules = useExamStore((state) => state.setSelectedModules);
|
const setSelectedModules = useExamStore((state) => state.setSelectedModules);
|
||||||
|
const [downloadingPdf, setDownloadingPdf] = useState<DownloadingPdf>({});
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -174,7 +181,7 @@ export default function History({user}: {user: User}) {
|
|||||||
level: calculateBandScore(x.correct, x.total, x.module, user.focus),
|
level: calculateBandScore(x.correct, x.total, x.module, user.focus),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const timeSpent = dateStats[0].timeSpent;
|
const { timeSpent, session } = dateStats[0];
|
||||||
|
|
||||||
const selectExam = () => {
|
const selectExam = () => {
|
||||||
const examPromises = uniqBy(dateStats, "exam").map((stat) => getExamById(stat.module, stat.exam));
|
const examPromises = uniqBy(dateStats, "exam").map((stat) => getExamById(stat.module, stat.exam));
|
||||||
@@ -195,6 +202,34 @@ export default function History({user}: {user: User}) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const textColor = clsx(
|
||||||
|
correct / total >= 0.7 && "text-mti-purple",
|
||||||
|
correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red",
|
||||||
|
correct / total < 0.3 && "text-mti-rose",
|
||||||
|
);
|
||||||
|
|
||||||
|
const triggerDownload = async () => {
|
||||||
|
try {
|
||||||
|
setDownloadingPdf((prev) => ({...prev, [session]: true}));
|
||||||
|
const res = await axios.post(`/api/stats/${session}/export`);
|
||||||
|
toast.success("Report ready!");
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = res.data;
|
||||||
|
// download should have worked but there are some CORS issues
|
||||||
|
// https://firebase.google.com/docs/storage/web/download-files#cors_configuration
|
||||||
|
// link.download="report.pdf";
|
||||||
|
link.target = '_blank';
|
||||||
|
link.rel="noreferrer"
|
||||||
|
link.click();
|
||||||
|
setDownloadingPdf((prev) => ({...prev, [session]: false}));
|
||||||
|
} catch(err) {
|
||||||
|
toast.error("Failed to display the report!");
|
||||||
|
console.error(err);
|
||||||
|
setDownloadingPdf((prev) => ({...prev, [session]: false}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
const content = (
|
const content = (
|
||||||
<>
|
<>
|
||||||
<div className="w-full flex justify-between -md:items-center 2xl:items-center">
|
<div className="w-full flex justify-between -md:items-center 2xl:items-center">
|
||||||
@@ -207,15 +242,39 @@ export default function History({user}: {user: User}) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
<span
|
<span
|
||||||
className={clsx(
|
className={textColor}>
|
||||||
correct / total >= 0.7 && "text-mti-purple",
|
|
||||||
correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red",
|
|
||||||
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>
|
||||||
|
{/*<a
|
||||||
|
href="https://firebasestorage.googleapis.com/v0/b/mti-ielts.appspot.com/o/exam_report%2F1704838712225.pdf?alt=media&token=0df9a50a-05a9-40a8-ba7c-4ff10ac5f3c8"
|
||||||
|
//download="report.pdf"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<BsFilePdf
|
||||||
|
className={`${textColor} text-2xl cursor-pointer`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
triggerDownload();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</a>*/}
|
||||||
|
{downloadingPdf[session] ?
|
||||||
|
<span className={`${textColor} loading loading-infinity w-6`} /> :
|
||||||
|
(
|
||||||
|
<BsFilePdf
|
||||||
|
className={`${textColor} text-2xl cursor-pointer`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
triggerDownload();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full flex flex-col gap-1">
|
<div className="w-full flex flex-col gap-1">
|
||||||
|
|||||||
Reference in New Issue
Block a user