diff --git a/src/exams/pdf/details/level.exam.tsx b/src/exams/pdf/details/level.exam.tsx
new file mode 100644
index 00000000..57ccac20
--- /dev/null
+++ b/src/exams/pdf/details/level.exam.tsx
@@ -0,0 +1,165 @@
+import React from "react";
+import { View, Text } from "@react-pdf/renderer";
+import { ModuleScore } from "@/interfaces/module.scores";
+import { styles } from "../styles";
+import { RadialResult } from "./radial.result";
+interface Props {
+ detail: ModuleScore;
+}
+
+const thresholds = [
+ {
+ level: "Low A1",
+ label: "Begginner",
+ minValue: 0,
+ maxValue: 3,
+ },
+ {
+ level: "High A1/Low A2",
+ label: "Elementary",
+ minValue: 4,
+ maxValue: 7,
+ },
+ {
+ level: "High A2/Low B1",
+ label: "Pre-Intermediate",
+ minValue: 8,
+ maxValue: 12,
+ },
+ {
+ level: "High B2/Low C1",
+ label: "Upper-Intermediate",
+ minValue: 16,
+ maxValue: 21,
+ },
+ {
+ level: "C1",
+ label: "Advanced",
+ minValue: 22,
+ maxValue: 25,
+ },
+];
+
+export const LevelExamDetails = ({ detail }: Props) => {
+ const updatedThresholds = thresholds.map((t) => ({
+ ...t,
+ match: detail.score >= t.minValue && detail.score <= t.maxValue,
+ }));
+
+ const getBackgroundColor = (match: boolean, base: boolean) => {
+ if (match) return "#c2bfdd";
+ return base ? "#553b25" : "#ea7c7b";
+ };
+
+ const getTextColor = (match: boolean, base: boolean) => {
+ if (match) return "#9e7936";
+ return base ? "white" : "#553b25";
+ };
+ return (
+
+
+
+
+
+ Level as per CEFR Levels
+
+
+
+ {updatedThresholds.map(
+ ({ level, label, minValue, maxValue, match }, index, arr) => (
+
+
+
+ {level}
+
+
+
+
+ {label}
+
+
+
+
+ {minValue}-{maxValue}
+
+
+
+ )
+ )}
+
+
+
+ );
+};
diff --git a/src/exams/pdf/details/radial.result.tsx b/src/exams/pdf/details/radial.result.tsx
new file mode 100644
index 00000000..df999000
--- /dev/null
+++ b/src/exams/pdf/details/radial.result.tsx
@@ -0,0 +1,36 @@
+import React from "react";
+import {
+ Document,
+ Page,
+ View,
+ Text,
+ StyleSheet,
+ Image,
+} from "@react-pdf/renderer";
+import { styles } from "../styles";
+import { ModuleScore } from "@/interfaces/module.scores";
+
+export const RadialResult = ({ module, score, total }: ModuleScore) => (
+
+
+ {module}
+
+ {score}
+ Out of {total}
+
+);
diff --git a/src/exams/pdf/details/skill.exam.tsx b/src/exams/pdf/details/skill.exam.tsx
new file mode 100644
index 00000000..a23e7c69
--- /dev/null
+++ b/src/exams/pdf/details/skill.exam.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+import {
+ Document,
+ Page,
+ View,
+ Text,
+ StyleSheet,
+ Image,
+} from "@react-pdf/renderer";
+import { ModuleScore } from "@/interfaces/module.scores";
+import { styles } from "../styles";
+import { RadialResult } from "./radial.result";
+interface Props {
+ testDetails: ModuleScore[];
+}
+
+export const SkillExamDetails = ({ testDetails }: Props) => (
+
+ {testDetails.map((detail) => {
+ const { module } = detail;
+
+ return ;
+ })}
+
+);
diff --git a/src/exams/pdf/index.tsx b/src/exams/pdf/index.tsx
index c0ca27b4..dd9d5b98 100644
--- a/src/exams/pdf/index.tsx
+++ b/src/exams/pdf/index.tsx
@@ -1,64 +1,13 @@
/* eslint-disable jsx-a11y/alt-text */
import React from "react";
-import {
- Document,
- Page,
- View,
- Text,
- StyleSheet,
- Image,
-} from "@react-pdf/renderer";
+import { Document, Page, View, Text, Image } from "@react-pdf/renderer";
import ProgressBar from "./progress.bar";
// import RadialProgress from "./radial.progress";
// import RadialProgressSvg from "./radial.progress.svg";
import { Module } from "@/interfaces";
import { ModuleScore } from "@/interfaces/module.scores";
// import logo from './logo_title.png';
-
-const styles = StyleSheet.create({
- body: {
- paddingTop: 10,
- paddingBottom: 20,
- paddingHorizontal: 35,
- },
- titleView: {
- display: "flex",
- // flex: 1,
- alignItems: "center",
- },
- title: {
- textTransform: "uppercase",
- },
- textPadding: {
- margin: "8px",
- },
- separator: {
- width: "100%",
- borderBottom: "1px solid #89b0c2",
- },
- textFont: {
- fontFamily: "Helvetica",
- },
- textBold: {
- fontFamily: "Helvetica-Bold",
- fontWeight: "bold",
- },
- textColor: {
- color: "#4e4969",
- },
- textUnderline: {
- textDecoration: "underline",
- },
- spacedRow: {
- display: "flex",
- flexDirection: "row",
- justifyContent: "space-between",
- },
- alignRightRow: {
- display: "flex",
- flexDirection: "row-reverse",
- }
-});
+import { styles } from "./styles";
interface Props {
date: string;
@@ -70,6 +19,7 @@ interface Props {
summary: string;
logo: string;
qrcode: string;
+ renderDetails: React.ReactNode;
}
const PDFReport = ({
@@ -82,6 +32,7 @@ const PDFReport = ({
summary,
logo,
qrcode,
+ renderDetails,
}: Props) => {
const defaultTextStyle = [styles.textFont, { fontSize: 8 }];
const defaultSkillsTextStyle = [styles.textFont, { fontSize: 8 }];
@@ -94,14 +45,8 @@ const PDFReport = ({
return (
-
-
+
+
Email: {email}
Gender: {gender}
-
+
Test Details:
-
- {testDetails.map(({ module, score, total }) => (
-
-
- {module}
-
- {score}
- Out of {total}
-
- ))}
-
+ {renderDetails}
-
+
diff --git a/src/exams/pdf/styles.ts b/src/exams/pdf/styles.ts
new file mode 100644
index 00000000..1e31de4e
--- /dev/null
+++ b/src/exams/pdf/styles.ts
@@ -0,0 +1,46 @@
+import { StyleSheet } from "@react-pdf/renderer";
+
+export const styles = StyleSheet.create({
+ body: {
+ paddingTop: 10,
+ paddingBottom: 20,
+ paddingHorizontal: 35,
+ },
+ titleView: {
+ display: "flex",
+ // flex: 1,
+ alignItems: "center",
+ },
+ title: {
+ textTransform: "uppercase",
+ },
+ textPadding: {
+ margin: "8px",
+ },
+ separator: {
+ width: "100%",
+ borderBottom: "1px solid #89b0c2",
+ },
+ textFont: {
+ fontFamily: "Helvetica",
+ },
+ textBold: {
+ fontFamily: "Helvetica-Bold",
+ fontWeight: "bold",
+ },
+ textColor: {
+ color: "#4e4969",
+ },
+ textUnderline: {
+ textDecoration: "underline",
+ },
+ spacedRow: {
+ display: "flex",
+ flexDirection: "row",
+ justifyContent: "space-between",
+ },
+ alignRightRow: {
+ display: "flex",
+ flexDirection: "row-reverse",
+ },
+});
diff --git a/src/pages/api/stats/[id]/export.tsx b/src/pages/api/stats/[id]/export.tsx
index 91f3ab69..4a8829b9 100644
--- a/src/pages/api/stats/[id]/export.tsx
+++ b/src/pages/api/stats/[id]/export.tsx
@@ -14,16 +14,14 @@ import { withIronSessionApiRoute } from "iron-session/next";
import { sessionOptions } from "@/lib/session";
import ReactPDF from "@react-pdf/renderer";
import PDFReport from "@/exams/pdf";
-import {
- ref,
- uploadBytes,
-} from "firebase/storage";
+import { ref, uploadBytes } from "firebase/storage";
import { Stat } from "@/interfaces/user";
import { User } from "@/interfaces/user";
import { Module } from "@/interfaces";
import { ModuleScore } from "@/interfaces/module.scores";
-import qrcode from 'qrcode';
-
+import qrcode from "qrcode";
+import { SkillExamDetails } from "@/exams/pdf/details/skill.exam";
+import { LevelExamDetails } from "@/exams/pdf/details/level.exam";
const db = getFirestore(app);
export default withIronSessionApiRoute(handler, sessionOptions);
@@ -122,7 +120,7 @@ const generateQRCode = async (link: string) => {
const qrCodeDataURL = await qrcode.toDataURL(link);
return qrCodeDataURL;
} catch (error) {
- console.error('Error generating QR code:', error);
+ console.error("Error generating QR code:", error);
return null;
}
};
@@ -157,7 +155,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
const stats = docsSnap.docs.map((d) => d.data());
const results = stats.reduce((accm: ModuleScore[], { module, score }) => {
- const fixedModuleStr = module[0].toUpperCase() + module.substring(1)
+ const fixedModuleStr = module[0].toUpperCase() + module.substring(1);
if (accm.find((e: ModuleScore) => e.module === fixedModuleStr)) {
return accm.map((e: ModuleScore) => {
if (e.module === fixedModuleStr) {
@@ -192,9 +190,22 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
const overallResult = overallScore / overallTotal;
const performanceSummary = getPerformanceSummary("level", overallResult);
- const qrcode = await generateQRCode((req.headers.origin || '') + req.url);
+ const qrcode = await generateQRCode((req.headers.origin || "") + req.url);
- if(qrcode) {
+ const overallDetail = {
+ module: "Overall",
+ score: overallScore,
+ total: overallTotal,
+ } as ModuleScore;
+ const testDetails = [overallDetail, ...results];
+ const renderDetails = () => {
+ if (stats[0].module === "level") {
+ return ;
+ }
+
+ return ;
+ };
+ if (qrcode) {
const pdfStream = await ReactPDF.renderToStream(