Added final touches

This commit is contained in:
Joao Ramos
2024-08-21 00:37:33 +01:00
parent 65f8368708
commit eec1bb0c30
3 changed files with 234 additions and 119 deletions

View File

@@ -3,6 +3,7 @@ import React from "react";
import { Document, Page, View, Text, Image } from "@react-pdf/renderer";
import { ModuleScore } from "@/interfaces/module.scores";
import { styles } from "./styles";
import TestReportFooter from "./test.report.footer";
import { StyleSheet } from "@react-pdf/renderer";
@@ -17,17 +18,16 @@ const customStyles = StyleSheet.create({
},
table: {
width: "100%",
margin: "10px",
},
tableRow: {
flexDirection: "row",
},
tableCol50: {
width: "50%", // First column width (50%)
tableCol70: {
width: "70%", // First column width (50%)
borderStyle: "solid",
borderWidth: 1,
borderColor: "#000",
padding: 5,
// padding: 5,
},
tableCol25: {
width: "16.67%", // Remaining four columns each get 1/6 of the total width (50% / 3 = 16.67%)
@@ -43,11 +43,20 @@ const customStyles = StyleSheet.create({
borderColor: "#000",
padding: 5,
},
tableCol10: {
width: "10%", // Width for each of the 5 sub-columns (50% / 5 = 20%)
borderStyle: "solid",
borderWidth: 1,
borderColor: "#000",
padding: 5,
},
tableCellHeader: {
backgroundColor: "#d3d3d3",
fontWeight: "bold",
fontSize: 12,
textAlign: "center",
},
tableCellHeaderColor: {
backgroundColor: "#d3d3d3",
},
tableCell: {
fontSize: 10,
textAlign: "center",
@@ -86,7 +95,7 @@ const LevelTestReport = ({
const defaultTextStyle = [styles.textFont, { fontSize: 8 }];
return (
<Document>
<Page style={styles.body}>
<Page style={styles.body} orientation="landscape">
<Text style={[styles.textFont, styles.textBold, { fontSize: 11 }]}>
Corporate Name: {corporateName}
</Text>
@@ -111,56 +120,97 @@ const LevelTestReport = ({
<View style={customStyles.table}>
{/* Header Row */}
<View style={customStyles.tableRow}>
<View style={customStyles.tableCol50}>
<Text style={customStyles.tableCellHeader}>Test sections</Text>
<View
style={[
customStyles.tableCol70,
customStyles.tableCellHeaderColor,
]}
>
<Text style={[customStyles.tableCellHeader, { padding: 5 }]}>
Test sections
</Text>
</View>
<View style={customStyles.tableCol25}>
<View
style={[
customStyles.tableCol20,
customStyles.tableCellHeaderColor,
]}
>
<Text style={customStyles.tableCellHeader}>Time spent</Text>
</View>
<View style={customStyles.tableCol25}>
<View
style={[
customStyles.tableCol10,
customStyles.tableCellHeaderColor,
]}
>
<Text style={customStyles.tableCellHeader}>Score</Text>
</View>
</View>
<View style={customStyles.tableRow}>
<View style={customStyles.tableCol50}>
<View style={customStyles.tableCol70}>
<View style={customStyles.tableRow}>
{uniqueExercises.map((exercise, index) => (
<View style={customStyles.tableCol20} key={index}>
<View
style={[
customStyles.tableCol20,
index !== uniqueExercises.length - 1
? { borderWidth: 0, borderRightWidth: 1 }
: { borderWidth: 0 },
]}
key={index}
>
<Text style={customStyles.tableCell}>Part {index + 1}</Text>
</View>
))}
</View>
</View>
<View style={customStyles.tableCol25}>
<View style={customStyles.tableCol20}>
<Text style={customStyles.tableCell}></Text>
</View>
<View style={customStyles.tableCol25}>
<View style={customStyles.tableCol10}>
<Text style={customStyles.tableCell}></Text>
</View>
</View>
<View style={customStyles.tableRow}>
<View style={customStyles.tableCol50}>
<View style={customStyles.tableCol70}>
<View style={customStyles.tableRow}>
{uniqueExercises.map((exercise, index) => (
<View style={customStyles.tableCol20} key={index}>
<View
style={[
customStyles.tableCol20,
index !== uniqueExercises.length - 1
? { borderWidth: 0, borderRightWidth: 1 }
: { borderWidth: 0 },
]}
key={index}
>
<Text style={customStyles.tableCell}>{exercise.name}</Text>
</View>
))}
</View>
</View>
<View style={customStyles.tableCol25}>
<View style={customStyles.tableCol20}>
<Text style={customStyles.tableCell}></Text>
</View>
<View style={customStyles.tableCol25}>
<View style={customStyles.tableCol10}>
<Text style={customStyles.tableCell}></Text>
</View>
</View>
<View style={customStyles.tableRow}>
<View style={customStyles.tableCol50}>
<View style={customStyles.tableCol70}>
<View style={customStyles.tableRow}>
{uniqueExercises.map((exercise, index) => (
<View style={customStyles.tableCol20} key={index}>
<View
style={[
customStyles.tableCol20,
index !== uniqueExercises.length - 1
? { borderWidth: 0, borderRightWidth: 1 }
: { borderWidth: 0 },
]}
key={index}
>
<Text style={customStyles.tableCell}>
{exercise.result}
</Text>
@@ -168,14 +218,15 @@ const LevelTestReport = ({
))}
</View>
</View>
<View style={customStyles.tableCol25}>
<View style={customStyles.tableCol20}>
<Text style={customStyles.tableCell}>{timeSpent}</Text>
</View>
<View style={customStyles.tableCol25}>
<View style={customStyles.tableCol10}>
<Text style={customStyles.tableCell}>{score}</Text>
</View>
</View>
</View>
<TestReportFooter userId={userId} />
</Page>
</Document>
);

View File

@@ -34,7 +34,7 @@ import {
streamToBuffer,
} from "@/utils/pdf";
import moment from "moment-timezone";
import { getCorporateNameForStudent } from "@/utils/groups.be";
const db = getFirestore(app);
export default withIronSessionApiRoute(handler, sessionOptions);
@@ -325,14 +325,14 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
}
const userId = stats[statIndex].user;
// if (hasPDF) {
// // if it does, return the pdf url
// const fileRef = ref(storage, hasPDF.pdf!.path);
// const url = await getDownloadURL(fileRef);
if (hasPDF) {
// if it does, return the pdf url
const fileRef = ref(storage, hasPDF.pdf!.path);
const url = await getDownloadURL(fileRef);
// res.status(200).end(url);
// return;
// }
res.status(200).end(url);
return;
}
try {
// generate the pdf report
@@ -355,7 +355,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
stats.reduce((accm, s: Stat) => accm + (s.timeSpent || 0), 0) / 60
} minutes`;
const score = stats.reduce((accm, s) => accm + s.score.correct, 0);
const corporateName = await getCorporateNameForStudent(userId);
const pdfStream = await ReactPDF.renderToStream(
<LevelTestReport
date={moment.max(dates).format("DD/MM/YYYY")}
@@ -364,7 +364,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
id={stat.exam}
gender={user.demographicInformation?.gender || ""}
passportId={user.demographicInformation?.passport_id || ""}
corporateName="TODO"
corporateName={corporateName}
downloadDate={moment().format("DD/MM/YYYY")}
userId={userId}
uniqueExercises={uniqueExercises}
@@ -376,8 +376,6 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
const url = await getPdfUrl(pdfStream, docsSnap);
res.status(200).end(url);
return;
return;
}
const user = docUser.data() as User;

View File

@@ -1,12 +1,29 @@
import { app } from "@/firebase";
import {CorporateUser, Group, StudentUser, TeacherUser} from "@/interfaces/user";
import {collection, doc, getDoc, getDocs, getFirestore, query, setDoc, where} from "firebase/firestore";
import {
CorporateUser,
Group,
StudentUser,
TeacherUser,
} from "@/interfaces/user";
import {
collection,
doc,
getDoc,
getDocs,
getFirestore,
query,
setDoc,
where,
} from "firebase/firestore";
import moment from "moment";
import { getUser } from "./users.be";
import { getSpecificUsers } from "./users.be";
const db = getFirestore(app);
export const updateExpiryDateOnGroup = async (participantID: string, corporateID: string) => {
export const updateExpiryDateOnGroup = async (
participantID: string,
corporateID: string
) => {
const corporateRef = await getDoc(doc(db, "users", corporateID));
const participantRef = await getDoc(doc(db, "users", participantID));
@@ -16,19 +33,36 @@ export const updateExpiryDateOnGroup = async (participantID: string, corporateID
...corporateRef.data(),
id: corporateRef.id,
} as CorporateUser;
const participant = {...participantRef.data(), id: participantRef.id} as StudentUser | TeacherUser;
const participant = { ...participantRef.data(), id: participantRef.id } as
| StudentUser
| TeacherUser;
if (corporate.type !== "corporate" || (participant.type !== "student" && participant.type !== "teacher")) return;
if (
corporate.type !== "corporate" ||
(participant.type !== "student" && participant.type !== "teacher")
)
return;
if (!corporate.subscriptionExpirationDate || !participant.subscriptionExpirationDate) {
return await setDoc(doc(db, "users", participant.id), {subscriptionExpirationDate: null}, {merge: true});
if (
!corporate.subscriptionExpirationDate ||
!participant.subscriptionExpirationDate
) {
return await setDoc(
doc(db, "users", participant.id),
{ subscriptionExpirationDate: null },
{ merge: true }
);
}
const corporateDate = moment(corporate.subscriptionExpirationDate);
const participantDate = moment(participant.subscriptionExpirationDate);
if (corporateDate.isAfter(participantDate))
return await setDoc(doc(db, "users", participant.id), {subscriptionExpirationDate: corporateDate.toISOString()}, {merge: true});
return await setDoc(
doc(db, "users", participant.id),
{ subscriptionExpirationDate: corporateDate.toISOString() },
{ merge: true }
);
return;
};
@@ -39,17 +73,29 @@ export const getGroups = async () => {
};
export const getUserGroups = async (id: string): Promise<Group[]> => {
const groupDocs = await getDocs(query(collection(db, "groups"), where("admin", "==", id)));
const groupDocs = await getDocs(
query(collection(db, "groups"), where("admin", "==", id))
);
return groupDocs.docs.map((x) => ({ ...x.data(), id })) as Group[];
};
export const getAllAssignersByCorporate = async (corporateID: string): Promise<string[]> => {
export const getAllAssignersByCorporate = async (
corporateID: string
): Promise<string[]> => {
const groups = await getUserGroups(corporateID);
const groupUsers = (await Promise.all(groups.map(async (g) => await Promise.all(g.participants.map(getUser))))).flat();
const groupUsers = (
await Promise.all(
groups.map(async (g) => await Promise.all(g.participants.map(getUser)))
)
).flat();
const teacherPromises = await Promise.all(
groupUsers.map(async (u) =>
u.type === "teacher" ? u.id : u.type === "corporate" ? [...(await getAllAssignersByCorporate(u.id)), u.id] : undefined,
),
u.type === "teacher"
? u.id
: u.type === "corporate"
? [...(await getAllAssignersByCorporate(u.id)), u.id]
: undefined
)
);
return teacherPromises.filter((x) => !!x).flat() as string[];
@@ -80,7 +126,10 @@ export const getGroupsForUser = async (admin: string, participant: string) => {
}
};
export const getStudentGroupsForUsersWithoutAdmin = async (admin: string, participants: string[]) => {
export const getStudentGroupsForUsersWithoutAdmin = async (
admin: string,
participants: string[]
) => {
try {
const queryConstraints = [
...(admin ? [where("admin", "!=", admin)] : []),
@@ -105,3 +154,20 @@ export const getGroupsForUser = async (admin: string, participant: string) => {
return [];
}
};
export const getCorporateNameForStudent = async (studentID: string) => {
const groups = await getStudentGroupsForUsersWithoutAdmin("", [studentID]);
if (groups.length === 0) return '';
const adminUserIds = [...new Set(groups.map((g) => g.admin))];
const adminUsersData = await getSpecificUsers(adminUserIds);
if(adminUsersData.length === 0) return '';
const admins = adminUsersData.filter((x) => x.type === 'corporate');
if(admins.length > 0) {
return (admins[0] as CorporateUser).corporateInformation.companyInformation.name;
}
return '';
};