From d8fe5fcc4263ac62075c863ab71182b55d7214ba Mon Sep 17 00:00:00 2001 From: amNobodyyy Date: Fri, 7 Mar 2025 02:09:15 +0530 Subject: [PATCH] coursePanel dropdown, 6 emails per subject --- client/src/Pages/ConsolidatedTable.jsx | 2 +- client/src/Pages/CourseForm.jsx | 232 +++++--- client/src/Pages/Navbar.jsx | 1 + client/src/Pages/courseConsolidated.jsx | 702 ++++++++++++------------ client/src/api.js | 4 +- server/models/Faculty.js | 1 + server/routes/consolidatedRoutes.js | 46 +- server/routes/emailRoutes.js | 80 ++- 8 files changed, 638 insertions(+), 430 deletions(-) diff --git a/client/src/Pages/ConsolidatedTable.jsx b/client/src/Pages/ConsolidatedTable.jsx index ac9f295..aa5766d 100644 --- a/client/src/Pages/ConsolidatedTable.jsx +++ b/client/src/Pages/ConsolidatedTable.jsx @@ -193,7 +193,7 @@ const ConsolidatedTable = () => { formData.append("file", file); try { - const emailResponse = await sendEmail(formData); + const emailResponse = await sendEmail(formData, "excel"); toast.success(`Email sent successfully to ${facultyEmail}`); console.log("Response from server:", emailResponse); } catch (emailError) { diff --git a/client/src/Pages/CourseForm.jsx b/client/src/Pages/CourseForm.jsx index 1b8aa2b..38583fd 100644 --- a/client/src/Pages/CourseForm.jsx +++ b/client/src/Pages/CourseForm.jsx @@ -43,7 +43,10 @@ const CourseForm = () => { const fetchOptionsAndFaculties = async () => { try { const facultiesData = await fetchFaculties(); - setOptions((prev) => ({ ...prev, faculties: facultiesData })); + const filteredFaculties = facultiesData.filter( + (faculty) => faculty.courses.includes(course?.courseId || id) // Only faculties with this course + ); + setOptions((prev) => ({ ...prev, faculties: filteredFaculties })); } catch (error) { console.error("Failed to fetch faculties:", error); } @@ -67,18 +70,21 @@ const CourseForm = () => { }; const handleAddFaculty = (field) => { + if (!formData[field]) return; + const selectedFaculty = options.faculties.find( (faculty) => faculty.name === formData[field] ); + if (selectedFaculty) { setTempAssignments((prev) => ({ ...prev, [field]: [...prev[field], selectedFaculty], })); setFormData({ ...formData, [field]: "" }); - setSuggestions((prev) => ({ ...prev, [field]: [] })); } }; + const handleRemoveFaculty = (field, index) => { setTempAssignments((prev) => { @@ -160,13 +166,23 @@ const CourseForm = () => {
@@ -180,96 +196,160 @@ const CourseForm = () => { - {errors.examPeriod && {errors.examPeriod}} + {errors.examPeriod && ( + + {errors.examPeriod} + + )}
- {[{ name: "oralsPracticals", label: "Orals/Practicals" }, - { name: "assessment", label: "Assessment" }, - { name: "reassessment", label: "Reassessment" }, - { name: "paperSetting", label: "Paper Setting" }, - { name: "moderation", label: "Moderation" }, - { name: "pwdPaperSetter", label: "PwD Paper Setter" }] - .map(({ name, label }) => ( -
- + {tempAssignments[name].length > 0 && ( + + )} + {errors[name] && ( + + {errors[name]} + + )} +
+ ))} - + - - - ); }; diff --git a/client/src/Pages/Navbar.jsx b/client/src/Pages/Navbar.jsx index 1938ec3..9ddc364 100644 --- a/client/src/Pages/Navbar.jsx +++ b/client/src/Pages/Navbar.jsx @@ -30,6 +30,7 @@ const Navbar = () => { try { // Call the logout API await axios.get("http://localhost:8080/auth/logout", { withCredentials: true }); + localStorage.clear(); // Redirect to the login page after successful logout navigate("/"); diff --git a/client/src/Pages/courseConsolidated.jsx b/client/src/Pages/courseConsolidated.jsx index 383c861..0c0c96a 100644 --- a/client/src/Pages/courseConsolidated.jsx +++ b/client/src/Pages/courseConsolidated.jsx @@ -3,6 +3,8 @@ import axios from "axios"; import { jsPDF } from "jspdf"; import autoTable from "jspdf-autotable"; import Navbar from "./Navbar"; +import { toast, ToastContainer } from "react-toastify"; +import { sendEmail } from "../api"; const CourseConsolidated = () => { @@ -12,6 +14,7 @@ const CourseConsolidated = () => { const [currentPage, setCurrentPage] = useState(1); const tablesPerPage = 5; const [expandedCourse, setExpandedCourse] = useState(null); + useEffect(() => { const fetchData = async () => { @@ -36,17 +39,17 @@ const CourseConsolidated = () => { } // Extract unique courses by courseCode - const filteredCourses = [...new Set(data.map((row) => row.courseCode))].filter( - (courseCode) => { - const courseName = data.find((row) => row.courseCode === courseCode) - ?.courseName; - return ( - courseName && - courseName.toLowerCase().includes(searchQuery.toLowerCase()) - ); - } - ); - + const filteredCourses = [ + ...new Set(data.map((row) => row.courseCode)), + ].filter((courseCode) => { + const courseName = data.find( + (row) => row.courseCode === courseCode + )?.courseName; + return ( + courseName && courseName.toLowerCase().includes(searchQuery.toLowerCase()) + ); + }); + const totalPages = Math.ceil(filteredCourses.length / tablesPerPage); const indexOfLastTable = currentPage * tablesPerPage; const indexOfFirstTable = indexOfLastTable - tablesPerPage; @@ -54,7 +57,6 @@ const CourseConsolidated = () => { indexOfFirstTable, indexOfLastTable ); - const handleNextPage = () => { if (currentPage < totalPages) setCurrentPage((prevPage) => prevPage + 1); @@ -64,150 +66,170 @@ const CourseConsolidated = () => { if (currentPage > 1) setCurrentPage((prevPage) => prevPage - 1); }; - const generateAppointmentPDF = async (courseData, courseName) => { - const doc = new jsPDF(); - const maroon = [128, 0, 0]; - - // College Logo - const logoUrl = "/logo.png"; // Ensure the logo is placed in the public folder - const logoWidth = 40; - const logoHeight = 40; - const logoX = 10; - const logoY = 10; - - 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); - }); - }; - + const fetchFacultyEmail = async (facultyId) => { try { - const logoBase64 = await loadImage(logoUrl); - doc.addImage(logoBase64, "PNG", logoX, logoY, logoWidth, logoHeight); + const response = await axios.get( + `http://localhost:8080/api/faculty/${facultyId}`, + { withCredentials: true } + ); + return response.data.email; // Assuming API returns { email: "faculty@example.com" } } catch (error) { - console.error("Failed to load logo:", error); + console.error(`Error fetching email for ${facultyId}:`, error); + return "N/A"; // Return "N/A" if email is not found } - - // Title Section - doc.setFont("times", "normal"); - doc.setTextColor(0, 0, 0); - doc.setFontSize(12); - doc.text("Date: " + new Date().toLocaleDateString(), 150, 20); - doc.setFontSize(14); - doc.text("CONFIDENTIAL", 10, 60); - doc.setFontSize(10); - doc.text( - "LETTER OF APPOINTMENT AS QUESTION PAPER SETTER", - 105, - 70, - { align: "center" } - ); - - // Appointment Table - const table1Data = [ - ...(courseData.oralPracticalTeachers?.map((teacher) => [ - teacher, - "K. J. Somaiya School of Engineering", - "Oral/Practical Teacher", - "Contact Number", - ]) || []), - ...(courseData.assessmentTeachers?.map((teacher) => [ - teacher, - "K. J. Somaiya School of Engineering", - "Reassessment Teacher", - "Contact Number", - ]) || []), - ...(courseData.reassessmentTeachers?.map((teacher) => [ - teacher, - "K. J. Somaiya School of Engineering", - "Reassessment Teacher", - "Contact Number", - ]) || []), - ...(courseData.paperSettingTeachers?.map((teacher) => [ - teacher, - "K. J. Somaiya School of Engineering", - "Paper Setter", - "Contact Number", - ]) || []), - ...(courseData.moderationTeachers?.map((teacher) => [ - teacher, - "K. J. Somaiya School of Engineering", - "Moderator", - "Contact Number", - ]) || []), - ...(courseData.pwdPaperSettingTeachers?.map((teacher) => [ - teacher, - "K. J. Somaiya School of Engineering", - "PwD Paper Setter", - "Contact Number", - ]) || []), + }; + + 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" }, ]; - autoTable(doc, { - head: [["Name", "Affiliation", "Appointment Role", "Email"]], - body: table1Data, - startY: 80, - theme: "grid", - headStyles: { fillColor: maroon, textColor: [255, 255, 255] }, - styles: { textColor: [0, 0, 0] }, // Keep body text black - }); + for (const { key, role } of roles) { + if (!courseData[key] || courseData[key].length === 0) continue; // Skip empty roles - // Content Table - const detailsTableData = [ - ["Programme:", courseData.courseName], - ["Exam Category:", "Regular Examination"], - ["Exam Type:", courseData.examType], - ["Exam Season:", "Second Half - Winter Examination 2023"], - ["Number of Sets Required:", courseData.paperSettingTeachers.length], - ["Year:", courseData.year], - ["Semester:", courseData.semester], - ["Course Name:", courseName], - ["Course Code:", courseData.courseCode], - ]; + const doc = new jsPDF(); - autoTable(doc, { - body: detailsTableData, - startY: doc.previousAutoTable.finalY + 10, - theme: "grid", // Plain table style - styles: { textColor: [0, 0, 0] }, - }); + // College Logo + 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); + }); + }; - // Footer Section - const footerY = doc.previousAutoTable.finalY + 10; // Dynamic Y-coordinate - doc.setFontSize(12); - doc.text("Dr. S. K. Ukarande", 10, footerY); - doc.text("Principal", 10, footerY + 5); - doc.text("K. J. Somaiya School of Engineering", 10, footerY + 10); + try { + const logoBase64 = await loadImage(logoUrl); + doc.addImage(logoBase64, "PNG", 10, 10, 40, 40); + } catch (error) { + console.error("Failed to load logo:", error); + } - // Footer Contact Details - const footerContactY = footerY + 20; - doc.setFontSize(10); - doc.text( - "Somaiya Vidyavihar, Vidyavihar (East), Mumbai-400 022, India", - 10, - footerContactY - ); - doc.text("Telephone: (91-22) 44444400, 44444404", 10, footerContactY + 5); - doc.text("Email: principal.tech@somaiya.edu", 10, footerContactY + 10); - doc.text("Web: www.somaiya.edu/kjsieit", 10, footerContactY + 15); + // Title Section + doc.setFont("times", "normal"); + doc.setTextColor(0, 0, 0); + doc.setFontSize(12); + doc.text("Date: " + new Date().toLocaleDateString(), 150, 20); + doc.setFontSize(14); + doc.text("CONFIDENTIAL", 10, 60); + doc.setFontSize(10); + doc.text(`LETTER OF APPOINTMENT AS ${role.toUpperCase()}`, 105, 70, { + align: "center", + }); - // Save PDF - doc.save(`${courseName} - AppointmentOrder.pdf`); + // Fetch Emails and Prepare Table Data + let table1Data = []; + let emails = []; + + for (const teacher of courseData[key]) { + const email = await fetchFacultyEmail(teacher.facultyId); + emails.push(email); + table1Data.push([ + teacher.facultyName, + "K. J. Somaiya School of Engineering", + role, + email, + ]); + } + + // Generate Table + autoTable(doc, { + head: [["Name", "Affiliation", "Appointment Role", "Email"]], + body: table1Data, + startY: 80, + theme: "grid", + headStyles: { fillColor: maroon, textColor: [255, 255, 255] }, + styles: { textColor: [0, 0, 0] }, + }); + + // Content Table + const detailsTableData = [ + ["Programme:", courseData.courseName], + ["Exam Category:", "Regular Examination"], + ["Exam Type:", courseData.examType], + ["Exam Season:", "Second Half - Winter Examination 2023"], + ["Number of Sets Required:", courseData.paperSettingTeachers.length], + ["Year:", courseData.year], + ["Semester:", courseData.semester], + ["Course Name:", courseName], + ["Course Code:", courseData.courseCode], + ]; + + autoTable(doc, { + body: detailsTableData, + startY: doc.previousAutoTable.finalY + 10, + theme: "grid", + styles: { textColor: [0, 0, 0] }, + }); + + // Footer + const footerY = doc.previousAutoTable.finalY + 10; + doc.setFontSize(12); + doc.text("Dr. S. K. Ukarande", 10, footerY); + doc.text("Principal", 10, footerY + 5); + doc.text("K. J. Somaiya School of Engineering", 10, footerY + 10); + + const footerContactY = footerY + 20; + doc.setFontSize(10); + doc.text( + "Somaiya Vidyavihar, Vidyavihar (East), Mumbai-400 022, India", + 10, + footerContactY + ); + doc.text("Telephone: (91-22) 44444400, 44444404", 10, footerContactY + 5); + doc.text("Email: principal.tech@somaiya.edu", 10, footerContactY + 10); + doc.text("Web: www.somaiya.edu/kjsieit", 10, footerContactY + 15); + + // Convert PDF to Blob + const pdfBlob = doc.output("blob"); + + // Send Email to Each Faculty + for (const email of emails) { + try { + const formData = new FormData(); + formData.append("pdfFile", pdfBlob, `${courseName}-${role}.pdf`); + formData.append("fileName", `${courseName}-${role}.pdf`); + formData.append("recipientEmail", email); + formData.append("role", role); + formData.append("courseName", courseName); + + const emailResponse = await sendEmail(formData, "pdf"); + toast.success(`✅ Email sent successfully to ${email}`); + console.log("Response from server:", emailResponse); + } catch (emailError) { + toast.error(`❌ Error sending email to ${email}:`, emailError.message); + } + } + } }; return ( <> - -
-

- Course Tables with Download Options -

+ + +
+

+ Course Tables with Download Options +

{ />
- -
- {currentCourses.map((courseCode, index) => { - const courseData = data.filter( - (row) => row.courseCode === courseCode - ); - const courseName = courseData[0]?.courseName; // Get course name from first item - return ( -
-
- setExpandedCourse( - expandedCourse === courseCode ? null : courseCode - ) - } - > -

{courseName}'s Table

- -
+

{courseName}'s Table

+ +
- {expandedCourse === courseCode && ( - - - - - - - - - - - - - - - - - - {courseData.map((row, idx) => ( - - - - - - - - - - - - + {expandedCourse === courseCode && ( +
SemesterCourse CodeCourse NameExam TypeYearOral/PracticalAssessmentReassessmentPaper SettingModerationPwD Paper Setting
{row.semester}{row.courseCode}{row.courseName}{row.examType}{row.year} - {row.oralPracticalTeachers && - row.oralPracticalTeachers.length > 0 ? ( -
    - {row.oralPracticalTeachers.map((teacher, idx) => ( -
  • {teacher}
  • - ))} -
- ) : ( - "N/A" - )} -
- {row.assessmentTeachers && - row.assessmentTeachers.length > 0 ? ( -
    - {row.assessmentTeachers.map((teacher, idx) => ( -
  • {teacher}
  • - ))} -
- ) : ( - "N/A" - )} -
- {row.reassessmentTeachers && - row.reassessmentTeachers.length > 0 ? ( -
    - {row.reassessmentTeachers.map((teacher, idx) => ( -
  • {teacher}
  • - ))} -
- ) : ( - "N/A" - )} -
- {row.paperSettingTeachers && - row.paperSettingTeachers.length > 0 ? ( -
    - {row.paperSettingTeachers.map((teacher, idx) => ( -
  • {teacher}
  • - ))} -
- ) : ( - "N/A" - )} -
- {row.moderationTeachers && - row.moderationTeachers.length > 0 ? ( -
    - {row.moderationTeachers.map((teacher, idx) => ( -
  • {teacher}
  • - ))} -
- ) : ( - "N/A" - )} -
- {row.pwdPaperSettingTeachers && - row.pwdPaperSettingTeachers.length > 0 ? ( -
    - {row.pwdPaperSettingTeachers.map( - (teacher, idx) => ( -
  • {teacher}
  • - ) - )} -
- ) : ( - "N/A" - )} -
+ + + + + + + + + + + + + - ))} - -
SemesterCourse CodeCourse NameExam TypeYearOral/PracticalAssessmentReassessmentPaper SettingModerationPwD Paper Setting
- )} -
- ); - })} -
+ + + {courseData.map((row, idx) => ( + + {row.semester} + {row.courseCode} + {row.courseName} + {row.examType} + {row.year} + + {row.oralPracticalTeachers && + row.oralPracticalTeachers.length > 0 ? ( +
    + {row.oralPracticalTeachers.map( + (teacher, idx) => ( +
  • {teacher.facultyName}
  • + ) + )} +
+ ) : ( + "N/A" + )} + + + {row.assessmentTeachers && + row.assessmentTeachers.length > 0 ? ( +
    + {row.assessmentTeachers.map((teacher, idx) => ( +
  • {teacher.facultyName}
  • + ))} +
+ ) : ( + "N/A" + )} + + + {row.reassessmentTeachers && + row.reassessmentTeachers.length > 0 ? ( +
    + {row.reassessmentTeachers.map( + (teacher, idx) => ( +
  • {teacher.facultyName}
  • + ) + )} +
+ ) : ( + "N/A" + )} + + + {row.paperSettingTeachers && + row.paperSettingTeachers.length > 0 ? ( +
    + {row.paperSettingTeachers.map( + (teacher, idx) => ( +
  • {teacher.facultyName}
  • + ) + )} +
+ ) : ( + "N/A" + )} + + + {row.moderationTeachers && + row.moderationTeachers.length > 0 ? ( +
    + {row.moderationTeachers.map((teacher, idx) => ( +
  • {teacher.facultyName}
  • + ))} +
+ ) : ( + "N/A" + )} + + + {row.pwdPaperSettingTeachers && + row.pwdPaperSettingTeachers.length > 0 ? ( +
    + {row.pwdPaperSettingTeachers.map( + (teacher, idx) => ( +
  • {teacher.facultyName}
  • + ) + )} +
+ ) : ( + "N/A" + )} + + + ))} + + + )} +
+ ); + })} + - {/* Pagination controls */} -
- - - Page {currentPage} of {totalPages} - - + {/* Pagination controls */} +
+ + + Page {currentPage} of {totalPages} + + +
- ); }; diff --git a/client/src/api.js b/client/src/api.js index fae21de..f9bec06 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -151,9 +151,9 @@ export const updateCourseStatus = async (courseId) => { }); }; -export const sendEmail = async (formData) => { +export const sendEmail = async (formData, type) => { try { - const url = `${BASE_URL}/send-email`; + const url = `${BASE_URL}/send-email/${type}`; const response = await fetch(url, { method: "POST", body: formData, // Directly pass FormData diff --git a/server/models/Faculty.js b/server/models/Faculty.js index 9e77a43..35e89b2 100644 --- a/server/models/Faculty.js +++ b/server/models/Faculty.js @@ -6,6 +6,7 @@ const FacultySchema = new mongoose.Schema({ email: { type: String, required: true }, department: { type: String, required: true }, program: { type: String, required: true }, + courses: [{ type: String }], }); module.exports = mongoose.model("Faculty", FacultySchema); diff --git a/server/routes/consolidatedRoutes.js b/server/routes/consolidatedRoutes.js index 815f224..8a7b784 100644 --- a/server/routes/consolidatedRoutes.js +++ b/server/routes/consolidatedRoutes.js @@ -108,7 +108,7 @@ router.get("/course-consolidated", async (req, res) => { // Add the faculty name to the appropriate task if (appointment.task && groupedByCourse[courseId].tasks[appointment.task]) { - groupedByCourse[courseId].tasks[appointment.task].add(appointment.facultyName); + groupedByCourse[courseId].tasks[appointment.task].add(JSON.stringify({ facultyId: appointment.facultyId, facultyName: appointment.facultyName })); } }); @@ -130,12 +130,12 @@ router.get("/course-consolidated", async (req, res) => { semester: course.semester, examType: course.examType, year: course.year, - oralPracticalTeachers: Array.from(course.tasks.oralsPracticals), - assessmentTeachers: Array.from(course.tasks.assessment), - reassessmentTeachers: Array.from(course.tasks.reassessment), - paperSettingTeachers: Array.from(course.tasks.paperSetting), - moderationTeachers: Array.from(course.tasks.moderation), - pwdPaperSettingTeachers: Array.from(course.tasks.pwdPaperSetter), + oralPracticalTeachers: Array.from(course.tasks.oralsPracticals).map((data) => JSON.parse(data)), + assessmentTeachers: Array.from(course.tasks.assessment).map((data) => JSON.parse(data)), + reassessmentTeachers: Array.from(course.tasks.reassessment).map((data) => JSON.parse(data)), + paperSettingTeachers: Array.from(course.tasks.paperSetting).map((data) => JSON.parse(data)), + moderationTeachers: Array.from(course.tasks.moderation).map((data) => JSON.parse(data)), + pwdPaperSettingTeachers: Array.from(course.tasks.pwdPaperSetter).map((data) => JSON.parse(data)), })); res.status(200).json(consolidatedData); @@ -215,5 +215,37 @@ router.get("/department-consolidated", async (req, res) => { } }); +router.get("/panel-consolidated", async (req, res) => { + try { + const courses = await Course.find(); + const faculties = await Faculty.find(); + + // Create a structure where each course has its associated faculty + const consolidatedData = courses.map((course) => { + return { + courseId: course.courseId, + courseName: course.courseName, + semester: course.semester, + examType: course.scheme, + program: course.program, + faculty: faculties + .filter((faculty) => faculty.courses.includes(course.courseId)) + .map((faculty) => ({ + facultyId: faculty.facultyId, + name: faculty.name, + email: faculty.email, + qualification: faculty.qualification, + experience: faculty.experience, + })), + }; + }); + + res.status(200).json(consolidatedData); + } catch (error) { + console.error("Error fetching panel consolidated data:", error); + res.status(500).json({ message: "Failed to fetch panel consolidated data" }); + } +}); + module.exports = router; diff --git a/server/routes/emailRoutes.js b/server/routes/emailRoutes.js index e6eabc1..bb3ebfd 100644 --- a/server/routes/emailRoutes.js +++ b/server/routes/emailRoutes.js @@ -17,7 +17,7 @@ const storage = multer.diskStorage({ const upload = multer({ storage }); // Route to handle email sending with file attachment -router.post("/", upload.single("file"), async (req, res) => { +router.post("/excel", upload.single("file"), async (req, res) => { const { teacher, fileName, recipientEmail } = req.body; if (!teacher || !fileName || !recipientEmail || !req.file) { @@ -31,9 +31,12 @@ router.post("/", upload.single("file"), async (req, res) => { user: "swdc.ate@gmail.com", // Replace with your email pass: "umlc hbkr dpga iywd", // Replace with your app-specific password or token }, + tls: { + rejectUnauthorized: false, // Ignore self-signed certificate errors + }, connectionTimeout: 15000, - greetingTimeout: 15000, - socketTimeout: 15000, + greetingTimeout: 15000, + socketTimeout: 15000, }); // Email options @@ -52,17 +55,18 @@ If you have any queries, please contact the H.O.D or the Examination In-charge. Your cooperation regarding the upcoming examination duties is highly solicited.`, attachments: [ - { - filename: fileName, - path: req.file.path, // Use the uploaded file's path - }, + { + filename: fileName, + path: req.file.path, // Use the uploaded file's path + }, ], -}; + }; try { // Send email await transporter.sendMail(mailOptions); + transporter.close(); // Delete the temporary file after sending the email fs.unlinkSync(req.file.path); @@ -74,4 +78,64 @@ Your cooperation regarding the upcoming examination duties is highly solicited.` } }); +// Route to send emails with PDF attachments +router.post("/pdf", upload.single("pdfFile"), async (req, res) => { + const { fileName, recipientEmail, role, courseName } = req.body; + + if (!fileName || !recipientEmail || !role || !courseName || !req.file) { + return res.status(400).json({ error: "Missing required fields or file" }); + } + + // Configure Nodemailer transporter + const transporter = nodemailer.createTransport({ + service: "gmail", + auth: { + user: "swdc.ate@gmail.com", // Replace with your email + pass: "umlc hbkr dpga iywd", // Replace with your app-specific password + }, + tls: { + rejectUnauthorized: false, + }, + connectionTimeout: 15000, + greetingTimeout: 15000, + socketTimeout: 15000, + }); + + // Email options + const mailOptions = { + from: "SWDC Admin ", + to: recipientEmail, + subject: `Appointment as ${role} - ${courseName}`, + text: `Dear teacher, + +You have been appointed as a ${role} for the course "${courseName}". +Please find your appointment letter attached. + +If you have any queries, please contact the Examination In-charge. + +Best regards, +Examination Department`, + attachments: [ + { + filename: fileName, + path: req.file.path, // Path to the uploaded PDF + }, + ], + }; + + try { + // Send email + await transporter.sendMail(mailOptions); + transporter.close(); + + // Delete the PDF file after sending + // fs.unlinkSync(req.file.path); + + res.status(200).json({ message: "Email sent successfully" }); + } catch (error) { + console.error("Error sending email:", error); + res.status(500).json({ error: "Failed to send email" }); + } +}); + module.exports = router;