Merge branch 'main' of https://github.com/hk151109/appointment_to_examiner
This commit is contained in:
@@ -80,6 +80,247 @@ const CourseConsolidated = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const generateAppointmentPDFs = async (courseData, courseName) => {
|
||||||
|
// const maroon = [128, 0, 0];
|
||||||
|
// const roles = [
|
||||||
|
// { key: "oralPracticalTeachers", role: "Oral/Practical Teacher" },
|
||||||
|
// { key: "assessmentTeachers", role: "Assessment Teacher" },
|
||||||
|
// { key: "reassessmentTeachers", role: "Reassessment Teacher" },
|
||||||
|
// { key: "paperSettingTeachers", role: "Paper Setter" },
|
||||||
|
// { key: "moderationTeachers", role: "Moderator" },
|
||||||
|
// { key: "pwdPaperSettingTeachers", role: "PwD Paper Setter" },
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// // ✅ NEW: Group appointments by Faculty ID
|
||||||
|
// const facultyMap = {};
|
||||||
|
|
||||||
|
// for (const { key, role } of roles) {
|
||||||
|
// if (!courseData[key] || courseData[key].length === 0) continue;
|
||||||
|
|
||||||
|
// for (const teacher of courseData[key]) {
|
||||||
|
// if (!facultyMap[teacher.facultyId]) {
|
||||||
|
// facultyMap[teacher.facultyId] = {
|
||||||
|
// facultyName: teacher.facultyName,
|
||||||
|
// email: await fetchFacultyEmail(teacher.facultyId),
|
||||||
|
// appointments: [],
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// facultyMap[teacher.facultyId].appointments.push({
|
||||||
|
// role,
|
||||||
|
// teacher,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ✅ NEW: For each Faculty, generate PDFs & send one email
|
||||||
|
// for (const facultyId in facultyMap) {
|
||||||
|
// const faculty = facultyMap[facultyId];
|
||||||
|
// const pdfBlobs = [];
|
||||||
|
|
||||||
|
// for (const appointment of faculty.appointments) {
|
||||||
|
// const { role } = appointment;
|
||||||
|
|
||||||
|
// const doc = new jsPDF();
|
||||||
|
|
||||||
|
// // Add Logo and Title (your original PDF content)
|
||||||
|
// const logoUrl = "/logo.png";
|
||||||
|
// const loadImage = async (url) => {
|
||||||
|
// const response = await fetch(url);
|
||||||
|
// const blob = await response.blob();
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// const reader = new FileReader();
|
||||||
|
// reader.onload = () => resolve(reader.result);
|
||||||
|
// reader.onerror = (error) => reject(error);
|
||||||
|
// reader.readAsDataURL(blob);
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// const logoBase64 = await loadImage(logoUrl);
|
||||||
|
// doc.addImage(logoBase64, "PNG", 10, 10, 40, 40);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Failed to load logo:", error);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// doc.setFont("times", "normal");
|
||||||
|
// doc.text(`Date: ${new Date().toLocaleDateString()}`, 150, 20);
|
||||||
|
// doc.text("CONFIDENTIAL", 10, 60);
|
||||||
|
// doc.text(`LETTER OF APPOINTMENT AS ${role.toUpperCase()}`, 105, 70, { align: "center" });
|
||||||
|
|
||||||
|
// autoTable(doc, {
|
||||||
|
// head: [["Name", "Affiliation", "Appointment Role", "Email"]],
|
||||||
|
// body: [[faculty.facultyName, "K. J. Somaiya School of Engineering", role, faculty.email]],
|
||||||
|
// startY: 80,
|
||||||
|
// theme: "grid",
|
||||||
|
// headStyles: { fillColor: maroon, textColor: [255, 255, 255] },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// autoTable(doc, {
|
||||||
|
// body: [
|
||||||
|
// ["Programme:", courseData.courseName],
|
||||||
|
// ["Exam Category:", "Regular Examination"],
|
||||||
|
// ["Exam Type:", courseData.examType],
|
||||||
|
// ["Exam Season:", "Second Half - Winter Examination 2023"],
|
||||||
|
// ["Year:", courseData.year],
|
||||||
|
// ["Semester:", courseData.semester],
|
||||||
|
// ["Course Name:", courseName],
|
||||||
|
// ["Course Code:", courseData.courseCode],
|
||||||
|
// ],
|
||||||
|
// startY: doc.previousAutoTable.finalY + 10,
|
||||||
|
// theme: "grid",
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const pdfBlob = doc.output("blob");
|
||||||
|
// pdfBlobs.push({ blob: pdfBlob, filename: `${courseName}-${role}.pdf` });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ✅ NEW: Send ONE email with ALL PDFs to the Faculty
|
||||||
|
// try {
|
||||||
|
// const formData = new FormData();
|
||||||
|
|
||||||
|
// pdfBlobs.forEach(({ blob, filename }) => {
|
||||||
|
// formData.append("pdfFiles", blob, filename); // note 'pdfFiles' is plural
|
||||||
|
// });
|
||||||
|
|
||||||
|
// formData.append("recipientEmail", faculty.email);
|
||||||
|
// formData.append("facultyName", faculty.facultyName);
|
||||||
|
// formData.append("courseName", courseName);
|
||||||
|
|
||||||
|
// const emailResponse = await sendEmail(formData, "bulk-pdf");
|
||||||
|
// toast.success(`✅ Email sent successfully to ${faculty.email}`);
|
||||||
|
// console.log("Response:", emailResponse);
|
||||||
|
// } catch (error) {
|
||||||
|
// toast.error(`❌ Error sending email to ${faculty.email}: ${error.message}`);
|
||||||
|
// console.error(error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const generateAppointmentPDFs = async (courseData, courseName) => {
|
||||||
|
// const maroon = [128, 0, 0];
|
||||||
|
// const roles = [
|
||||||
|
// { key: "oralPracticalTeachers", role: "Oral/Practical Teacher" },
|
||||||
|
// { key: "assessmentTeachers", role: "Assessment Teacher" },
|
||||||
|
// { key: "reassessmentTeachers", role: "Reassessment Teacher" },
|
||||||
|
// { key: "paperSettingTeachers", role: "Paper Setter" },
|
||||||
|
// { key: "moderationTeachers", role: "Moderator" },
|
||||||
|
// { key: "pwdPaperSettingTeachers", role: "PwD Paper Setter" },
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// // Group appointments by Faculty ID and by role
|
||||||
|
// const facultyMap = {};
|
||||||
|
// const roleFaculties = {}; // Store faculties by role
|
||||||
|
|
||||||
|
// for (const { key, role } of roles) {
|
||||||
|
// if (!courseData[key] || courseData[key].length === 0) continue;
|
||||||
|
|
||||||
|
// for (const teacher of courseData[key]) {
|
||||||
|
// if (!facultyMap[teacher.facultyId]) {
|
||||||
|
// facultyMap[teacher.facultyId] = {
|
||||||
|
// facultyName: teacher.facultyName,
|
||||||
|
// email: await fetchFacultyEmail(teacher.facultyId),
|
||||||
|
// appointments: [],
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// facultyMap[teacher.facultyId].appointments.push({ role, teacher });
|
||||||
|
|
||||||
|
// // Add faculty to the role-specific list
|
||||||
|
// if (!roleFaculties[role]) roleFaculties[role] = [];
|
||||||
|
// roleFaculties[role].push(teacher.facultyName);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Generate PDF for each faculty
|
||||||
|
// for (const facultyId in facultyMap) {
|
||||||
|
// const faculty = facultyMap[facultyId];
|
||||||
|
// const pdfBlobs = [];
|
||||||
|
|
||||||
|
// for (const appointment of faculty.appointments) {
|
||||||
|
// const { role } = appointment;
|
||||||
|
|
||||||
|
// const doc = new jsPDF();
|
||||||
|
// const logoUrl = "/logo.png";
|
||||||
|
// const loadImage = async (url) => {
|
||||||
|
// const response = await fetch(url);
|
||||||
|
// const blob = await response.blob();
|
||||||
|
// return new Promise((resolve, reject) => {
|
||||||
|
// const reader = new FileReader();
|
||||||
|
// reader.onload = () => resolve(reader.result);
|
||||||
|
// reader.onerror = (error) => reject(error);
|
||||||
|
// reader.readAsDataURL(blob);
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// const logoBase64 = await loadImage(logoUrl);
|
||||||
|
// doc.addImage(logoBase64, "PNG", 10, 10, 40, 40);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Failed to load logo:", error);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// doc.setFont("times", "normal");
|
||||||
|
// doc.text(`Date: ${new Date().toLocaleDateString()}`, 150, 20);
|
||||||
|
// doc.text("CONFIDENTIAL", 10, 60);
|
||||||
|
// doc.text(`LETTER OF APPOINTMENT AS ${role.toUpperCase()}`, 105, 70, { align: "center" });
|
||||||
|
|
||||||
|
// // Include other faculties performing the same role
|
||||||
|
// const otherFaculties = roleFaculties[role].filter(
|
||||||
|
// (name) => name !== faculty.facultyName
|
||||||
|
// );
|
||||||
|
// doc.text(
|
||||||
|
// `Other faculties performing the same role: ${otherFaculties.join(", ")}`,
|
||||||
|
// 10,
|
||||||
|
// 90
|
||||||
|
// );
|
||||||
|
|
||||||
|
// autoTable(doc, {
|
||||||
|
// head: [["Name", "Affiliation", "Appointment Role", "Email"]],
|
||||||
|
// body: [[faculty.facultyName, "K. J. Somaiya School of Engineering", role, faculty.email]],
|
||||||
|
// startY: 100,
|
||||||
|
// theme: "grid",
|
||||||
|
// headStyles: { fillColor: maroon, textColor: [255, 255, 255] },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// autoTable(doc, {
|
||||||
|
// body: [
|
||||||
|
// ["Programme:", courseData.courseName],
|
||||||
|
// ["Exam Category:", "Regular Examination"],
|
||||||
|
// ["Exam Type:", courseData.examType],
|
||||||
|
// ["Exam Season:", "Second Half - Winter Examination 2023"],
|
||||||
|
// ["Year:", courseData.year],
|
||||||
|
// ["Semester:", courseData.semester],
|
||||||
|
// ["Course Name:", courseName],
|
||||||
|
// ["Course Code:", courseData.courseCode],
|
||||||
|
// ],
|
||||||
|
// startY: doc.previousAutoTable.finalY + 10,
|
||||||
|
// theme: "grid",
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const pdfBlob = doc.output("blob");
|
||||||
|
// pdfBlobs.push({ blob: pdfBlob, filename: `${courseName}-${role}.pdf` });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Send ONE email with ALL PDFs to the Faculty
|
||||||
|
// try {
|
||||||
|
// const formData = new FormData();
|
||||||
|
// pdfBlobs.forEach(({ blob, filename }) => {
|
||||||
|
// formData.append("pdfFiles", blob, filename);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// formData.append("recipientEmail", faculty.email);
|
||||||
|
// formData.append("facultyName", faculty.facultyName);
|
||||||
|
// formData.append("courseName", courseName);
|
||||||
|
|
||||||
|
// const emailResponse = await sendEmail(formData, "bulk-pdf");
|
||||||
|
// toast.success(`✅ Email sent successfully to ${faculty.email}`);
|
||||||
|
// console.log("Response:", emailResponse);
|
||||||
|
// } catch (error) {
|
||||||
|
// toast.error(`❌ Error sending email to ${faculty.email}: ${error.message}`);
|
||||||
|
// console.error(error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
const generateAppointmentPDFs = async (courseData, courseName) => {
|
const generateAppointmentPDFs = async (courseData, courseName) => {
|
||||||
const maroon = [128, 0, 0];
|
const maroon = [128, 0, 0];
|
||||||
const roles = [
|
const roles = [
|
||||||
@@ -91,8 +332,9 @@ const CourseConsolidated = () => {
|
|||||||
{ key: "pwdPaperSettingTeachers", role: "PwD Paper Setter" },
|
{ key: "pwdPaperSettingTeachers", role: "PwD Paper Setter" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// ✅ NEW: Group appointments by Faculty ID
|
// Group appointments by Faculty ID and by role
|
||||||
const facultyMap = {};
|
const facultyMap = {};
|
||||||
|
const roleFaculties = {}; // Store faculty IDs by role
|
||||||
|
|
||||||
for (const { key, role } of roles) {
|
for (const { key, role } of roles) {
|
||||||
if (!courseData[key] || courseData[key].length === 0) continue;
|
if (!courseData[key] || courseData[key].length === 0) continue;
|
||||||
@@ -105,14 +347,15 @@ const CourseConsolidated = () => {
|
|||||||
appointments: [],
|
appointments: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
facultyMap[teacher.facultyId].appointments.push({
|
facultyMap[teacher.facultyId].appointments.push({ role, teacher });
|
||||||
role,
|
|
||||||
teacher,
|
// Add facultyId to the role-specific list
|
||||||
});
|
if (!roleFaculties[role]) roleFaculties[role] = [];
|
||||||
|
roleFaculties[role].push(teacher.facultyId); // Store facultyId instead of facultyName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ NEW: For each Faculty, generate PDFs & send one email
|
// Generate PDF for each faculty
|
||||||
for (const facultyId in facultyMap) {
|
for (const facultyId in facultyMap) {
|
||||||
const faculty = facultyMap[facultyId];
|
const faculty = facultyMap[facultyId];
|
||||||
const pdfBlobs = [];
|
const pdfBlobs = [];
|
||||||
@@ -121,8 +364,6 @@ const CourseConsolidated = () => {
|
|||||||
const { role } = appointment;
|
const { role } = appointment;
|
||||||
|
|
||||||
const doc = new jsPDF();
|
const doc = new jsPDF();
|
||||||
|
|
||||||
// Add Logo and Title (your original PDF content)
|
|
||||||
const logoUrl = "/logo.png";
|
const logoUrl = "/logo.png";
|
||||||
const loadImage = async (url) => {
|
const loadImage = async (url) => {
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
@@ -147,10 +388,21 @@ const CourseConsolidated = () => {
|
|||||||
doc.text("CONFIDENTIAL", 10, 60);
|
doc.text("CONFIDENTIAL", 10, 60);
|
||||||
doc.text(`LETTER OF APPOINTMENT AS ${role.toUpperCase()}`, 105, 70, { align: "center" });
|
doc.text(`LETTER OF APPOINTMENT AS ${role.toUpperCase()}`, 105, 70, { align: "center" });
|
||||||
|
|
||||||
|
// Include all faculties performing the same role inside the table
|
||||||
|
const roleFacultyDetails = roleFaculties[role].map((facultyId) => {
|
||||||
|
const facultyDetail = facultyMap[facultyId]; // Access by facultyId
|
||||||
|
return [
|
||||||
|
facultyDetail.facultyName,
|
||||||
|
"K. J. Somaiya School of Engineering",
|
||||||
|
role,
|
||||||
|
facultyDetail.email,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
autoTable(doc, {
|
autoTable(doc, {
|
||||||
head: [["Name", "Affiliation", "Appointment Role", "Email"]],
|
head: [["Name", "Affiliation", "Appointment Role", "Email"]],
|
||||||
body: [[faculty.facultyName, "K. J. Somaiya School of Engineering", role, faculty.email]],
|
body: roleFacultyDetails, // Mapping through all faculties in the role
|
||||||
startY: 80,
|
startY: 100,
|
||||||
theme: "grid",
|
theme: "grid",
|
||||||
headStyles: { fillColor: maroon, textColor: [255, 255, 255] },
|
headStyles: { fillColor: maroon, textColor: [255, 255, 255] },
|
||||||
});
|
});
|
||||||
@@ -174,12 +426,11 @@ const CourseConsolidated = () => {
|
|||||||
pdfBlobs.push({ blob: pdfBlob, filename: `${courseName}-${role}.pdf` });
|
pdfBlobs.push({ blob: pdfBlob, filename: `${courseName}-${role}.pdf` });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ NEW: Send ONE email with ALL PDFs to the Faculty
|
// Send ONE email with ALL PDFs to the Faculty
|
||||||
try {
|
try {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
pdfBlobs.forEach(({ blob, filename }) => {
|
pdfBlobs.forEach(({ blob, filename }) => {
|
||||||
formData.append("pdfFiles", blob, filename); // note 'pdfFiles' is plural
|
formData.append("pdfFiles", blob, filename);
|
||||||
});
|
});
|
||||||
|
|
||||||
formData.append("recipientEmail", faculty.email);
|
formData.append("recipientEmail", faculty.email);
|
||||||
@@ -198,6 +449,7 @@ const CourseConsolidated = () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const multer = require("multer");
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// Multer setup remains intact
|
// Multer setup remains intact
|
||||||
|
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
destination: function (req, file, cb) {
|
destination: function (req, file, cb) {
|
||||||
cb(null, "./"); // Customize directory if needed
|
cb(null, "./"); // Customize directory if needed
|
||||||
|
|||||||
Reference in New Issue
Block a user