diff --git a/client/src/Pages/ConsolidatedTable.jsx b/client/src/Pages/ConsolidatedTable.jsx index f8d2d7b..9febcfd 100644 --- a/client/src/Pages/ConsolidatedTable.jsx +++ b/client/src/Pages/ConsolidatedTable.jsx @@ -1,197 +1,7 @@ -// import React, { useState, useEffect } from "react"; -// import axios from "axios"; - -// const ConsolidatedTable = () => { -// const [data, setData] = useState([]); -// const [loading, setLoading] = useState(true); - -// useEffect(() => { -// const fetchData = async () => { -// try { -// const response = await axios.get("http://localhost:8080/api/table/consolidated-table"); -// setData(response.data); -// setLoading(false); -// } catch (error) { -// console.error("Error fetching table data:", error); -// setLoading(false); -// } -// }; - -// fetchData(); -// }, []); - -// if (loading) { -// return
Loading...
; -// } - -// return ( -//
-//

Consolidated Table

-// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// {data.map((row, index) => ( -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// ))} -// -//
SemesterCourse CodeCourse NameExam TypeYearMarksNameAffiliation/CollegeHighest QualificationCareer ExperienceOral/PracticalAssessmentReassessmentPaper SettingModerationPwD Paper Setting
{row.semester}{row.courseCode}{row.courseName}{row.examType}{row.year}{row.marks}{row.Name}{row.affiliation}{row.qualification}{row.experience}{row.oralPractical}{row.assessment}{row.reassessment}{row.paperSetting}{row.moderation}{row.pwdPaperSetting}
-//
-// ); -// }; - -// export default ConsolidatedTable; - - - -// import React, { useState, useEffect } from "react"; -// import axios from "axios"; -// import { CSVLink } from "react-csv"; - -// const ConsolidatedTable = () => { -// const [data, setData] = useState([]); -// const [loading, setLoading] = useState(true); - -// useEffect(() => { -// const fetchData = async () => { -// try { -// const response = await axios.get("http://localhost:8080/api/table/consolidated-table"); -// setData(response.data); -// setLoading(false); -// } catch (error) { -// console.error("Error fetching table data:", error); -// setLoading(false); -// } -// }; - -// fetchData(); -// }, []); - -// if (loading) { -// return
Loading...
; -// } - -// // Extract unique faculty names -// const uniqueTeachers = [...new Set(data.map((row) => row.Name))]; - -// return ( -//
-//

Faculty Tables with Download Option

-// {uniqueTeachers.map((teacher, index) => { -// // Filter rows for the current teacher -// const teacherData = data.filter((row) => row.Name === teacher); - -// return ( -//
-//

{teacher}'s Table

-// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// {teacherData.map((row, idx) => ( -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// ))} -// -//
SemesterCourse CodeCourse NameExam TypeYearMarksNameAffiliation/CollegeHighest QualificationCareer ExperienceOral/PracticalAssessmentReassessmentPaper SettingModerationPwD Paper Setting
{row.semester}{row.courseCode}{row.courseName}{row.examType}{row.year}{row.marks}{row.Name}{row.affiliation}{row.qualification}{row.experience}{row.oralPractical}{row.assessment}{row.reassessment}{row.paperSetting}{row.moderation}{row.pwdPaperSetting}
-// {/* CSV Download Button */} -// -// Download CSV -// -//
-// ); -// })} -//
-// ); -// }; - -// export default ConsolidatedTable; - - - -import React, { useState, useEffect, useRef } from "react"; +import React, { useState, useEffect } from "react"; import axios from "axios"; -import { CSVLink } from "react-csv"; +import * as XLSX from "xlsx-js-style"; +import { sendEmail } from "../api"; const ConsolidatedTable = () => { const [data, setData] = useState([]); @@ -236,7 +46,6 @@ const ConsolidatedTable = () => { [], ]; - // Add table headers const tableHeaders = [ [ "Sr No", @@ -261,7 +70,6 @@ const ConsolidatedTable = () => { ], ]; - // Add table data const dataRows = teacherData.map((row, index) => [ index + 1, row.semester, @@ -284,7 +92,6 @@ const ConsolidatedTable = () => { row.pwdPaperSetting, ]); - // Combine all rows const sheetData = [...headerInfo, ...tableHeaders, ...dataRows]; const worksheet = XLSX.utils.aoa_to_sheet(sheetData); @@ -299,12 +106,12 @@ const ConsolidatedTable = () => { // Define styles const boldStyle = { - font: { bold: true, name: "Times New Roman", sz: 12 }, + font: { bold: true, name: "Times New Roman", sz: 14 }, alignment: { horizontal: "center", vertical: "center" }, }; const redStyle = { - font: { color: { rgb: "FF0000" }, name: "Times New Roman", sz: 12 }, + font: { bold: true, color: { rgb: "FF0000" }, name: "Times New Roman", sz: 14 }, alignment: { horizontal: "center", vertical: "center" }, }; @@ -312,7 +119,7 @@ const ConsolidatedTable = () => { font: { name: "Times New Roman", sz: 12 }, }; - // Apply styles to header + // Apply styles to headers const headerRanges = [ { row: 0, style: boldStyle }, { row: 1, style: boldStyle }, @@ -329,6 +136,29 @@ const ConsolidatedTable = () => { } }); + // Set column widths for better readability + worksheet["!cols"] = [ + { wch: 10 }, // Sr No + { wch: 12 }, // Semester + { wch: 15 }, // Course Code + { wch: 25 }, // Course Name + { wch: 15 }, // Exam Type + { wch: 10 }, // Year + { wch: 10 }, // Marks + { wch: 15 }, // Surname + { wch: 15 }, // First Name + { wch: 15 }, // Middle Name + { wch: 20 }, // Affiliation + { wch: 20 }, // Qualification + { wch: 15 }, // Career Experience + { wch: 15 }, // Oral/Practical + { wch: 15 }, // Assessment + { wch: 15 }, // Reassessment + { wch: 15 }, // Paper Setting + { wch: 15 }, // Moderation + { wch: 15 }, // PwD Paper Setting + ]; + // Apply normal font style to all cells Object.keys(worksheet).forEach((key) => { if (worksheet[key] && key[0] !== "!") { @@ -349,45 +179,97 @@ const ConsolidatedTable = () => { }; const handleSendEmail = async (teacher, teacherData) => { - const csvHeaders = Object.keys(teacherData[0]); - const csvRows = teacherData.map((row) => - csvHeaders - .map( - (header) => `"${row[header]?.toString().replace(/"/g, '""') || ""}"` - ) - .join(",") - ); - const csvContent = [csvHeaders.join(","), ...csvRows].join("\n"); - const fileName = `${teacher.replace(/\s+/g, "_")}_table.csv`; - + const workbook = XLSX.utils.book_new(); + + const headerInfo = [ + ["Somaiya Vidyavihar University"], + ["K. J. SOMAIYA COLLEGE OF ENGINEERING"], + [ + "Appointment of Internal Examiners for Paper Setting / OR/PR/Assessment/Reassessment", + ], + ["Class: B Tech/M Tech/Honour/Minor"], + ["Department - Computer Engineering"], + [], + ]; + + const tableHeaders = [ + [ + "Sr No", + "Semester", + "Course Code", + "Course Name", + "Exam Type", + "Year", + "Marks", + "Surname", + "First Name", + "Middle Name", + "Affiliation/College", + "Highest Qualification", + "Career Experience", + "Oral/Practical", + "Assessment", + "Reassessment", + "Paper Setting", + "Moderation", + "PwD Paper Setting", + ], + ]; + + const dataRows = teacherData.map((row, index) => [ + index + 1, + row.semester, + row.courseCode, + row.courseName, + row.examType, + row.year, + row.marks, + row.surname, + row.firstName, + row.middleName, + row.affiliation, + row.qualification, + row.experience, + row.oralPractical, + row.assessment, + row.reassessment, + row.paperSetting, + row.moderation, + row.pwdPaperSetting, + ]); + + const sheetData = [...headerInfo, ...tableHeaders, ...dataRows]; + const worksheet = XLSX.utils.aoa_to_sheet(sheetData); + XLSX.utils.book_append_sheet(workbook, worksheet, teacher); + + const fileName = `${teacher.replace(/\s+/g, "_")}_table.xlsx`; + const excelBlob = XLSX.write(workbook, { + bookType: "xlsx", + type: "array", + }); + + const file = new File([excelBlob], fileName, { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + + const formData = new FormData(); + formData.append("teacher", teacher); + formData.append("fileName", fileName); + const recipientEmail = prompt(`Enter recipient email for ${teacher}:`); + formData.append("recipientEmail", recipientEmail); + formData.append("file", file); + try { - const recipientEmail = prompt(`Enter recipient email for ${teacher}:`); - if (!recipientEmail) { - alert("Email is required!"); - return; - } - - // Create email data object - const emailData = { - teacher, - csvData: csvContent, - fileName, - recipientEmail, - }; - - // Call sendEmail from api.js - const response = await sendEmail(emailData); - - // Handle success + const response = await sendEmail(formData); alert(`Email sent successfully to ${recipientEmail}`); console.log("Response from server:", response); - alert(`Email sent successfully to ${recipientEmail}`); } catch (error) { console.error("Error sending email:", error); alert("Failed to send email."); } }; + return (

Faculty Tables with Download Options

@@ -437,8 +319,15 @@ const ConsolidatedTable = () => { return (
-

{teacher}'s Table

- +

{teacher}'s Table

+
@@ -494,8 +383,22 @@ const ConsolidatedTable = () => { borderRadius: "5px", }} > - Download {teacher}'s CSV - + Download {teacher}'s Table + + {/* Send Email Button */} + ); })} diff --git a/client/src/api.js b/client/src/api.js index 5ab5e71..5e942f3 100644 --- a/client/src/api.js +++ b/client/src/api.js @@ -1,4 +1,5 @@ const BASE_URL = "http://localhost:8080/api"; +const XLSX = require("xlsx-js-style"); // Helper function for handling fetch requests const fetchData = async (url, options) => { @@ -138,30 +139,123 @@ export const updateCourseStatus = async (courseId) => { }; // Send email -export const sendEmail = async (emailData) => { - console.log("Sending email with data:", emailData); +const handleSendEmail = async (teacher, teacherData) => { + const workbook = XLSX.utils.book_new(); - // Validate input - // if (!emailData.to || !emailData.subject || !emailData.message) { - // const errorMessage = "Missing required fields: to, subject, message"; - // console.error(errorMessage); - // throw new Error(errorMessage); - // } + const headerInfo = [ + ["Somaiya Vidyavihar University"], + ["K. J. SOMAIYA COLLEGE OF ENGINEERING"], + [ + "Appointment of Internal Examiners for Paper Setting / OR/PR/Assessment/Reassessment", + ], + ["Class: B Tech/M Tech/Honour/Minor"], + ["Department - Computer Engineering"], + [], + ]; + + const tableHeaders = [ + [ + "Sr No", + "Semester", + "Course Code", + "Course Name", + "Exam Type", + "Year", + "Marks", + "Surname", + "First Name", + "Middle Name", + "Affiliation/College", + "Highest Qualification", + "Career Experience", + "Oral/Practical", + "Assessment", + "Reassessment", + "Paper Setting", + "Moderation", + "PwD Paper Setting", + ], + ]; + + const dataRows = teacherData.map((row, index) => [ + index + 1, + row.semester, + row.courseCode, + row.courseName, + row.examType, + row.year, + row.marks, + row.surname, + row.firstName, + row.middleName, + row.affiliation, + row.qualification, + row.experience, + row.oralPractical, + row.assessment, + row.reassessment, + row.paperSetting, + row.moderation, + row.pwdPaperSetting, + ]); + + const sheetData = [...headerInfo, ...tableHeaders, ...dataRows]; + const worksheet = XLSX.utils.aoa_to_sheet(sheetData); + XLSX.utils.book_append_sheet(workbook, worksheet, teacher); + + const fileName = `${teacher.replace(/\s+/g, "_")}_table.xlsx`; + const excelBlob = XLSX.write(workbook, { + bookType: "xlsx", + type: "array", + }); + + const file = new File([excelBlob], fileName, { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + + const formData = new FormData(); + formData.append("teacher", teacher); + formData.append("fileName", fileName); + const recipientEmail = prompt(`Enter recipient email for ${teacher}:`); + formData.append("recipientEmail", recipientEmail); + formData.append("file", file); + try { + const response = await sendEmail(formData); + alert(`Email sent successfully to ${recipientEmail}`); + console.log("Response from server:", response); + } catch (error) { + console.error("Error sending email:", error); + alert("Failed to send email."); + } +}; + + +export const sendEmail = async (formData) => { try { const url = `${BASE_URL}/send-email`; - const response = await fetchData(url, { + const response = await fetch(url, { method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(emailData), // Pass the email data to the server + body: formData, // Directly pass FormData }); - console.log("Email sent successfully:", response); - return response; + if (!response.ok) { + let errorDetails = {}; + try { + errorDetails = await response.json(); + } catch (err) { + console.warn("Failed to parse error details:", err); + } + throw new Error( + `Error: ${response.statusText} (${response.status}) - ${ + errorDetails.message || "No details available" + }` + ); + } + + return await response.json(); } catch (error) { - console.error("Error sending email:", error.message); + console.error( error.message); throw error; } }; \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index d2be342..8fb5c17 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -22,6 +22,7 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^8.9.5", "mongoose-findorcreate": "^4.0.0", + "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.13", "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", @@ -119,6 +120,11 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -270,6 +276,22 @@ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -330,6 +352,20 @@ "dev": true, "license": "MIT" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/connect-mongo": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-5.1.0.tgz", @@ -406,6 +442,11 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -1163,6 +1204,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/json-bigint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", @@ -1390,6 +1436,25 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mongodb": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", @@ -1530,6 +1595,23 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -1818,6 +1900,11 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1895,6 +1982,25 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2084,6 +2190,27 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2154,6 +2281,11 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -2194,6 +2326,11 @@ "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", "license": "BSD" }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2245,6 +2382,14 @@ "engines": { "node": ">=16" } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } } } } diff --git a/server/package.json b/server/package.json index c0df411..9cc037b 100644 --- a/server/package.json +++ b/server/package.json @@ -31,6 +31,7 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^8.9.5", "mongoose-findorcreate": "^4.0.0", + "multer": "^1.4.5-lts.1", "nodemailer": "^6.9.13", "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", diff --git a/server/routes/emailRoutes.js b/server/routes/emailRoutes.js index 17cb07b..e8af839 100644 --- a/server/routes/emailRoutes.js +++ b/server/routes/emailRoutes.js @@ -1,20 +1,90 @@ -// /routes/email.js +// // /routes/email.js +// const express = require("express"); +// const nodemailer = require("nodemailer"); +// const fs = require("fs"); +// const router = express.Router(); + +// router.post("/", async (req, res) => { +// const { teacher, csvData, fileName, recipientEmail } = req.body; + +// if (!teacher || !csvData || !fileName || !recipientEmail) { +// return res.status(400).json({ error: "Missing required fields" }); +// } + +// // Save the CSV data to a temporary file +// const filePath = `./${fileName}`; +// fs.writeFileSync(filePath, csvData); + +// // 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 or token +// }, +// // tls: { +// // rejectUnauthorized: false, // Disable SSL verification +// // } +// }); + +// // Email options +// const mailOptions = { +// from: "swdc.ate@gmail.com", // Replace with your email +// to: recipientEmail, +// subject: `Excel File for ${teacher}`, +// text: `Attached is the Excel file for ${teacher}.`, +// attachments: [ +// { +// filename: fileName, +// path: filePath, +// contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // MIME type for xlsx +// }, +// ], +// }; + +// try { +// // Send email +// await transporter.sendMail(mailOptions); + +// // Delete the temporary file after sending the email +// fs.unlinkSync(filePath); + +// 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; + + const express = require("express"); const nodemailer = require("nodemailer"); const fs = require("fs"); +const multer = require("multer"); const router = express.Router(); -router.post("/", async (req, res) => { - const { teacher, csvData, fileName, recipientEmail } = req.body; +// Setup multer for handling file uploads +const storage = multer.diskStorage({ + destination: function (req, file, cb) { + cb(null, "./"); // Save to the current directory (you can customize this) + }, + filename: function (req, file, cb) { + cb(null, file.originalname); // Use the original file name + }, +}); - if (!teacher || !csvData || !fileName || !recipientEmail) { - return res.status(400).json({ error: "Missing required fields" }); +const upload = multer({ storage }); + +// Route to handle email sending with file attachment +router.post("/", upload.single("file"), async (req, res) => { + const { teacher, fileName, recipientEmail } = req.body; + + if (!teacher || !fileName || !recipientEmail || !req.file) { + return res.status(400).json({ error: "Missing required fields or file" }); } - // Save the CSV data to a temporary file - const filePath = `./${fileName}`; - fs.writeFileSync(filePath, csvData); - // Configure Nodemailer transporter const transporter = nodemailer.createTransport({ service: "gmail", @@ -22,21 +92,18 @@ router.post("/", 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, // Disable SSL verification - // } }); // Email options const mailOptions = { from: "swdc.ate@gmail.com", // Replace with your email to: recipientEmail, - subject: `CSV File for ${teacher}`, - text: `Attached is the CSV file for ${teacher}.`, + subject: `Excel File for ${teacher}`, + text: `Attached is the Excel file for ${teacher}.`, attachments: [ { filename: fileName, - path: filePath, + path: req.file.path, // Use the uploaded file's path }, ], }; @@ -46,7 +113,7 @@ router.post("/", async (req, res) => { await transporter.sendMail(mailOptions); // Delete the temporary file after sending the email - fs.unlinkSync(filePath); + fs.unlinkSync(req.file.path); res.status(200).json({ message: "Email sent successfully" }); } catch (error) {
Semester