Added final touches
This commit is contained in:
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,107 +1,173 @@
|
||||
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 { app } from "@/firebase";
|
||||
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 { getUser } from "./users.be";
|
||||
import { getSpecificUsers } from "./users.be";
|
||||
const db = getFirestore(app);
|
||||
|
||||
export const updateExpiryDateOnGroup = async (participantID: string, corporateID: string) => {
|
||||
const corporateRef = await getDoc(doc(db, "users", corporateID));
|
||||
const participantRef = await getDoc(doc(db, "users", participantID));
|
||||
export const updateExpiryDateOnGroup = async (
|
||||
participantID: string,
|
||||
corporateID: string
|
||||
) => {
|
||||
const corporateRef = await getDoc(doc(db, "users", corporateID));
|
||||
const participantRef = await getDoc(doc(db, "users", participantID));
|
||||
|
||||
if (!corporateRef.exists() || !participantRef.exists()) return;
|
||||
if (!corporateRef.exists() || !participantRef.exists()) return;
|
||||
|
||||
const corporate = {
|
||||
...corporateRef.data(),
|
||||
id: corporateRef.id,
|
||||
} as CorporateUser;
|
||||
const participant = {...participantRef.data(), id: participantRef.id} as StudentUser | TeacherUser;
|
||||
const corporate = {
|
||||
...corporateRef.data(),
|
||||
id: corporateRef.id,
|
||||
} as CorporateUser;
|
||||
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);
|
||||
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});
|
||||
if (corporateDate.isAfter(participantDate))
|
||||
return await setDoc(
|
||||
doc(db, "users", participant.id),
|
||||
{ subscriptionExpirationDate: corporateDate.toISOString() },
|
||||
{ merge: true }
|
||||
);
|
||||
|
||||
return;
|
||||
return;
|
||||
};
|
||||
|
||||
export const getGroups = async () => {
|
||||
const groupDocs = await getDocs(collection(db, "groups"));
|
||||
return groupDocs.docs.map((x) => ({...x.data(), id: x.id})) as Group[];
|
||||
const groupDocs = await getDocs(collection(db, "groups"));
|
||||
return groupDocs.docs.map((x) => ({ ...x.data(), id: x.id })) as Group[];
|
||||
};
|
||||
|
||||
export const getUserGroups = async (id: string): Promise<Group[]> => {
|
||||
const groupDocs = await getDocs(query(collection(db, "groups"), where("admin", "==", id)));
|
||||
return groupDocs.docs.map((x) => ({...x.data(), id})) as Group[];
|
||||
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[]> => {
|
||||
const groups = await getUserGroups(corporateID);
|
||||
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,
|
||||
),
|
||||
);
|
||||
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 teacherPromises = await Promise.all(
|
||||
groupUsers.map(async (u) =>
|
||||
u.type === "teacher"
|
||||
? u.id
|
||||
: u.type === "corporate"
|
||||
? [...(await getAllAssignersByCorporate(u.id)), u.id]
|
||||
: undefined
|
||||
)
|
||||
);
|
||||
|
||||
return teacherPromises.filter((x) => !!x).flat() as string[];
|
||||
return teacherPromises.filter((x) => !!x).flat() as string[];
|
||||
};
|
||||
|
||||
export const getGroupsForUser = async (admin: string, participant: string) => {
|
||||
try {
|
||||
const queryConstraints = [
|
||||
...(admin ? [where("admin", "==", admin)] : []),
|
||||
...(participant
|
||||
? [where("participants", "array-contains", participant)]
|
||||
: []),
|
||||
];
|
||||
const snapshot = await getDocs(
|
||||
queryConstraints.length > 0
|
||||
? query(collection(db, "groups"), ...queryConstraints)
|
||||
: collection(db, "groups")
|
||||
);
|
||||
const groups = snapshot.docs.map((doc) => ({
|
||||
id: doc.id,
|
||||
...doc.data(),
|
||||
})) as Group[];
|
||||
try {
|
||||
const queryConstraints = [
|
||||
...(admin ? [where("admin", "==", admin)] : []),
|
||||
...(participant
|
||||
? [where("participants", "array-contains", participant)]
|
||||
: []),
|
||||
];
|
||||
const snapshot = await getDocs(
|
||||
queryConstraints.length > 0
|
||||
? query(collection(db, "groups"), ...queryConstraints)
|
||||
: collection(db, "groups")
|
||||
);
|
||||
const groups = snapshot.docs.map((doc) => ({
|
||||
id: doc.id,
|
||||
...doc.data(),
|
||||
})) as Group[];
|
||||
|
||||
return groups;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
return groups;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const getStudentGroupsForUsersWithoutAdmin = async (admin: string, participants: string[]) => {
|
||||
try {
|
||||
const queryConstraints = [
|
||||
...(admin ? [where("admin", "!=", admin)] : []),
|
||||
...(participants
|
||||
? [where("participants", "array-contains-any", participants)]
|
||||
: []),
|
||||
where("name", "==", "Students"),
|
||||
];
|
||||
const snapshot = await getDocs(
|
||||
queryConstraints.length > 0
|
||||
? query(collection(db, "groups"), ...queryConstraints)
|
||||
: collection(db, "groups")
|
||||
);
|
||||
const groups = snapshot.docs.map((doc) => ({
|
||||
id: doc.id,
|
||||
...doc.data(),
|
||||
})) as Group[];
|
||||
export const getStudentGroupsForUsersWithoutAdmin = async (
|
||||
admin: string,
|
||||
participants: string[]
|
||||
) => {
|
||||
try {
|
||||
const queryConstraints = [
|
||||
...(admin ? [where("admin", "!=", admin)] : []),
|
||||
...(participants
|
||||
? [where("participants", "array-contains-any", participants)]
|
||||
: []),
|
||||
where("name", "==", "Students"),
|
||||
];
|
||||
const snapshot = await getDocs(
|
||||
queryConstraints.length > 0
|
||||
? query(collection(db, "groups"), ...queryConstraints)
|
||||
: collection(db, "groups")
|
||||
);
|
||||
const groups = snapshot.docs.map((doc) => ({
|
||||
id: doc.id,
|
||||
...doc.data(),
|
||||
})) as Group[];
|
||||
|
||||
return groups;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
return groups;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
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 '';
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user