From b54f713df15d0f619966546207e74bc9ee88699f Mon Sep 17 00:00:00 2001 From: Sangam Arora Date: Wed, 3 Jul 2024 22:43:05 +0530 Subject: [PATCH] added export all data component --- backend/index.js | 3 + backend/routes/getCertificate.js | 185 +++++++++++++++++ backend/routes/getUser.js | 31 +-- client/src/Components/ExportCsvData.jsx | 23 ++- client/src/Components/ExportExcelData.jsx | 129 ++++++------ client/src/Components/ProtectedRoute.jsx | 21 +- client/src/pages/EditProfile/EditProfile.jsx | 12 +- .../pages/Placement Graphs/PlacementStats.jsx | 9 +- client/src/pages/StudentData/StudentData.jsx | 192 ++++++++++++++---- .../pages/SuperAdminDashboard/SuperAdmin.jsx | 33 ++- client/src/utils/AdminFunctions.js | 41 +++- client/src/utils/ExportAllData.js | 180 ++++++++++++++++ 12 files changed, 698 insertions(+), 161 deletions(-) create mode 100644 backend/routes/getCertificate.js create mode 100644 client/src/utils/ExportAllData.js diff --git a/backend/index.js b/backend/index.js index 72b9595..5260314 100644 --- a/backend/index.js +++ b/backend/index.js @@ -27,6 +27,8 @@ const tr104 = require("./routes/UserProfileData/Training4"); const placementData = require("./routes/UserProfileData/PlacementData"); const adminControl = require("./routes/adminControlRoutes/adminControl"); const test = require("./routes/test.js"); +const certificate = require('./routes/getCertificate.js') + app.use("/api/userprofiles", userProfileRoutes); app.use("/api/tr101", tr101); @@ -40,6 +42,7 @@ app.use("/api/validate", validateRoute); app.use("/api/password", passwordResetRoute); app.use("/api/admin", adminControl); app.use("/api/test", test); +app.use("/api/certificate", certificate); // Start the server const port = process.env.PORT; app.listen(port, () => { diff --git a/backend/routes/getCertificate.js b/backend/routes/getCertificate.js new file mode 100644 index 0000000..e510e95 --- /dev/null +++ b/backend/routes/getCertificate.js @@ -0,0 +1,185 @@ +const express = require('express'); +const router = express.Router(); +const SignUp = require('../models/UserInfo').SignUp; + +router.get('/tr101/:id', async (req, res) => { + try { + const id = req.params.id; + + // Assuming SignUp model has a method to fetch certificates, adjust as per your actual model + const user = await SignUp.findById(id); + + if (!user) { + return res.status(404).json({ success: false, message: 'User not found or certificate not available' }); + } + + const base64Certificate = user.tr101.certificate.replace(/^.+,/, ''); // Adjust this based on your schema + + // Convert base64 to buffer + const certificateBuffer = Buffer.from(base64Certificate, 'base64'); + const fileName = `${user.userInfo.Name}_${user.userInfo.urn}_tr101.pdf`.replace(/[^a-zA-Z0-9-_\.]/g, '_'); + + // Set response headers for file download + res.set({ + 'Content-Type': 'application/pdf', // Adjust the content type as per your file type + 'Content-Disposition': 'inline; certificate.pdf', // Adjust filename as per your requirement + }); + + res.send(certificateBuffer); + + } catch (error) { + console.error('Error:', error); + res.status(500).json({ success: false, message: 'Internal server error occurred' }); + } +}); +router.get('/tr102/:id', async (req, res) => { + try { + const id = req.params.id; + + // Assuming SignUp model has a method to fetch certificates, adjust as per your actual model + const user = await SignUp.findById(id); + + if (!user) { + return res.status(404).json({ success: false, message: 'User not found or certificate not available' }); + } + + const base64Certificate = user.tr102.certificate.replace(/^.+,/, ''); // Adjust this based on your schema + + // Convert base64 to buffer + const certificateBuffer = Buffer.from(base64Certificate, 'base64'); + const fileName = `${user.userInfo.Name}_${user.userInfo.urn}_tr101.pdf`.replace(/[^a-zA-Z0-9-_\.]/g, '_'); + + // Set response headers for file download + res.set({ + 'Content-Type': 'application/pdf', // Adjust the content type as per your file type + 'Content-Disposition': 'inline; certificate.pdf', // Adjust filename as per your requirement + }); + + res.send(certificateBuffer); + + } catch (error) { + console.error('Error:', error); + res.status(500).json({ success: false, message: 'Internal server error occurred' }); + } +}); +router.get('/tr103/:id', async (req, res) => { + try { + const id = req.params.id; + + // Assuming SignUp model has a method to fetch certificates, adjust as per your actual model + const user = await SignUp.findById(id); + + if (!user) { + return res.status(404).json({ success: false, message: 'User not found or certificate not available' }); + } + + const base64Certificate = user.tr103.certificate.replace(/^.+,/, ''); // Adjust this based on your schema + + // Convert base64 to buffer + const certificateBuffer = Buffer.from(base64Certificate, 'base64'); + const fileName = `${user.userInfo.Name}_${user.userInfo.urn}_tr101.pdf`.replace(/[^a-zA-Z0-9-_\.]/g, '_'); + + // Set response headers for file download + res.set({ + 'Content-Type': 'application/pdf', // Adjust the content type as per your file type + 'Content-Disposition': 'inline; certificate.pdf', // Adjust filename as per your requirement + }); + + res.send(certificateBuffer); + + } catch (error) { + console.error('Error:', error); + res.status(500).json({ success: false, message: 'Internal server error occurred' }); + } +}); +router.get('/tr104/:id', async (req, res) => { + try { + const id = req.params.id; + + // Assuming SignUp model has a method to fetch certificates, adjust as per your actual model + const user = await SignUp.findById(id); + + if (!user) { + return res.status(404).json({ success: false, message: 'User not found or certificate not available' }); + } + + const base64Certificate = user.tr101.certificate.replace(/^.+,/, ''); // Adjust this based on your schema + + // Convert base64 to buffer + const certificateBuffer = Buffer.from(base64Certificate, 'base64'); + const fileName = `${user.userInfo.Name}_${user.userInfo.urn}_tr101.pdf`.replace(/[^a-zA-Z0-9-_\.]/g, '_'); + + // Set response headers for file download + res.set({ + 'Content-Type': 'application/pdf', // Adjust the content type as per your file type + 'Content-Disposition': 'inline; certificate.pdf', // Adjust filename as per your requirement + }); + + res.send(certificateBuffer); + + } catch (error) { + console.error('Error:', error); + res.status(500).json({ success: false, message: 'Internal server error occurred' }); + } +}); +router.get('/appointmentLetter/:id', async (req, res) => { + try { + const id = req.params.id; + + // Assuming SignUp model has a method to fetch certificates, adjust as per your actual model + const user = await SignUp.findById(id); + + if (!user) { + return res.status(404).json({ success: false, message: 'User not found or certificate not available' }); + } + + const base64Certificate = user.placementData.appointmentLetter.replace(/^.+,/, ''); // Adjust this based on your schema + + // Convert base64 to buffer + const certificateBuffer = Buffer.from(base64Certificate, 'base64'); + const fileName = `${user.userInfo.Name}_${user.userInfo.urn}_tr101.pdf`.replace(/[^a-zA-Z0-9-_\.]/g, '_'); + + // Set response headers for file download + res.set({ + 'Content-Type': 'application/pdf', // Adjust the content type as per your file type + 'Content-Disposition': 'inline; certificate.pdf', // Adjust filename as per your requirement + }); + + res.send(certificateBuffer); + + } catch (error) { + console.error('Error:', error); + res.status(500).json({ success: false, message: 'Internal server error occurred' }); + } +}); +router.get('/gateCertificate/:id', async (req, res) => { + try { + const id = req.params.id; + + // Assuming SignUp model has a method to fetch certificates, adjust as per your actual model + const user = await SignUp.findById(id); + + if (!user) { + return res.status(404).json({ success: false, message: 'User not found or certificate not available' }); + } + + const base64Certificate = user.placementData.gateCertificate.replace(/^.+,/, ''); // Adjust this based on your schema + + // Convert base64 to buffer + const certificateBuffer = Buffer.from(base64Certificate, 'base64'); + const fileName = `${user.userInfo.Name}_${user.userInfo.urn}_tr101.pdf`.replace(/[^a-zA-Z0-9-_\.]/g, '_'); + + // Set response headers for file download + res.set({ + 'Content-Type': 'application/pdf', // Adjust the content type as per your file type + 'Content-Disposition': 'inline; certificate.pdf', // Adjust filename as per your requirement + }); + + res.send(certificateBuffer); + + } catch (error) { + console.error('Error:', error); + res.status(500).json({ success: false, message: 'Internal server error occurred' }); + } +}); +module.exports = router; \ No newline at end of file diff --git a/backend/routes/getUser.js b/backend/routes/getUser.js index 121b721..5e1f7a4 100644 --- a/backend/routes/getUser.js +++ b/backend/routes/getUser.js @@ -4,7 +4,6 @@ const SignUp = require('../models/UserInfo').SignUp; const fetchuser = require('../middleware/fetchUser'); const isAdmin = require('../middleware/isAdmin'); - router.get('/getuser/:crn', fetchuser, async (req, res) => { try { const crn = req.params.crn; @@ -23,7 +22,7 @@ router.get('/getuser/:crn', fetchuser, async (req, res) => { router.get('/getallusers', fetchuser, isAdmin, async (req, res) => { try { // Fetch all users - const users = await SignUp.find({}).select('-password'); + const users = await SignUp.find({ role: 'user' }).select('-password'); // Return the list of users return res.status(200).json({ success: true, data: users }); @@ -36,7 +35,7 @@ router.put('/updateUser/:crn', fetchuser, isAdmin, async (req, res) => { const { crn } = req.params; // Get CRN from request parameters const updatedFormData = req.body.updatedFormData; // Get updated user data from request body try { - // Find user by CRN and update with updatedFormData + const updatedUser = await SignUp.findOneAndUpdate( { crn: crn }, // Filter condition: find user by CRN { @@ -75,23 +74,28 @@ router.put('/updateUser/:crn', fetchuser, isAdmin, async (req, res) => { router.get('/getUsersByBatch', fetchuser, isAdmin, async (req, res) => { try { const { batch, trainingType } = req.query; - if (!batch || !trainingType) { return res.status(400).json({ success: false, message: 'Batch and training type are required' }); } - const allowedTrainingTypes = ['tr101', 'tr102', 'tr103', 'tr104', 'placementData']; + const allowedTrainingTypes = ['tr101', 'tr102', 'tr103', 'tr104', 'placementData', 'all']; if (!allowedTrainingTypes.includes(trainingType)) { return res.status(400).json({ success: false, message: 'Invalid training type' }); } - - - // Fetch users with the specified batch and role 'user' - const users = await SignUp.find({ - 'userInfo.batch': batch, - role: 'user' - }).select(`crn email ${trainingType} userInfo`); + let users; + if (trainingType === 'all') { + users = await SignUp.find({ + 'userInfo.batch': batch, + role: 'user' + }).select(`crn email tr101.certificate tr102.certificate tr103.certificate tr104.certificate placementData userInfo`); + } else { + users = await SignUp.find({ + 'userInfo.batch': batch, + role: 'user' + }).select(`crn email ${trainingType} userInfo`); + } + // Return the list of users with the specified training type and user data return res.status(200).json({ success: true, data: users }); @@ -100,8 +104,6 @@ router.get('/getUsersByBatch', fetchuser, isAdmin, async (req, res) => { res.status(500).json({ success: false, message: 'Internal server error occurred' }); } }); - - router.get('/getUsersByPreviousBatches', fetchuser, isAdmin, async (req, res) => { try { const { years, trainingType } = req.query; // Add trainingType to destructuring @@ -136,5 +138,4 @@ router.get('/getUsersByPreviousBatches', fetchuser, isAdmin, async (req, res) => } }); - module.exports = router; \ No newline at end of file diff --git a/client/src/Components/ExportCsvData.jsx b/client/src/Components/ExportCsvData.jsx index 05e977f..d380423 100644 --- a/client/src/Components/ExportCsvData.jsx +++ b/client/src/Components/ExportCsvData.jsx @@ -2,7 +2,11 @@ import React from 'react'; import { Button, Box } from '@mui/material'; import { mkConfig, generateCsv, download } from 'export-to-csv'; import FileDownloadIcon from '@mui/icons-material/FileDownload'; -import { base64toBlob } from '../utils/base64topdf' +const API_URL = + import.meta.env.VITE_ENV === "production" + ? import.meta.env.VITE_PROD_BASE_URL + : import.meta.env.VITE_DEV_BASE_URL; + const ExportCsvComponent = ({ data, selectedTraining }) => { const csvConfig = mkConfig({ fieldSeparator: ',', @@ -34,9 +38,10 @@ const ExportCsvComponent = ({ data, selectedTraining }) => { filteredRow['Project Name'] = trainingData.projectName; filteredRow['Technology Used'] = trainingData.technology.join(', '); if (trainingData.certificate) { - const certifiateBlob = base64toBlob(trainingData.certificate); - const certificateUrl = URL.createObjectURL(certifiateBlob); - filteredRow['Training Certificate'] = certificateUrl; + const certifiateUrl = `${API_URL}certificate/${selectedTraining}/${row._id}`; + + filteredRow['Training Certificate'] = certifiateUrl; + } else { filteredRow['Training Certificate'] = ''; } @@ -56,8 +61,8 @@ const ExportCsvComponent = ({ data, selectedTraining }) => { // Convert appointment letter to data URL if (trainingData.appointmentLetter) { - const appointmentLetterBlob = base64toBlob(trainingData.appointmentLetter); - filteredRow['Appointment Letter'] = URL.createObjectURL(appointmentLetterBlob); + const certificateURL = `${API_URL}certificate/appointmentLetter/${row._id}`; + filteredRow['Appointment Letter'] = certificateURL; } else { filteredRow['Appointment Letter'] = ''; @@ -66,8 +71,8 @@ const ExportCsvComponent = ({ data, selectedTraining }) => { filteredRow['Higher Study Place'] = trainingData.highStudyplace; filteredRow['Gate Appeared Status'] = trainingData.gateStatus; if (trainingData.gateCertificate) { - const gateCertificateBlob = base64toBlob(trainingData.gateCertificate); - filteredRow['Gate Admit Card/ ScoreCard'] = URL.createObjectURL(gateCertificateBlob); + const certificateURL = `${API_URL}certificate/gateCertificate/${row._id}`; // Replace with actual URL + filteredRow['Gate Admit Card/ ScoreCard'] = certificateURL } else { filteredRow['Gate Admit Card/ ScoreCard'] = ''; @@ -81,7 +86,7 @@ const ExportCsvComponent = ({ data, selectedTraining }) => { }).filter(row => Object.keys(row).length > 0); const csv = generateCsv(csvConfig)(filteredData); - + download(csvConfig)(csv); }; diff --git a/client/src/Components/ExportExcelData.jsx b/client/src/Components/ExportExcelData.jsx index 6be24d6..82254a9 100644 --- a/client/src/Components/ExportExcelData.jsx +++ b/client/src/Components/ExportExcelData.jsx @@ -1,9 +1,12 @@ import React from 'react'; import { Button, Box } from '@mui/material'; import FileDownloadIcon from '@mui/icons-material/FileDownload'; -import { base64toBlob } from '../utils/base64topdf'; import { saveAs } from 'file-saver'; import ExcelJS from 'exceljs'; +const API_URL = + import.meta.env.VITE_ENV === "production" + ? import.meta.env.VITE_PROD_BASE_URL + : import.meta.env.VITE_DEV_BASE_URL; const ExportExcelComponent = ({ data, selectedTraining }) => { const handleExportData = async () => { @@ -43,6 +46,7 @@ const ExportExcelComponent = ({ data, selectedTraining }) => { { header: 'Appointment Number', key: 'AppointmentNumber' }, { header: 'Package', key: 'Package' }, { header: 'Appointment Date', key: 'AppointmentDate' }, + { header: 'Appointment Letter', key: 'AppointmentLetter' }, { header: 'Designation', key: 'Designation' }, { header: 'Gate Admit Card/ ScoreCard', key: 'GateAdmitCard' }, { header: 'Higher Study Status', key: 'HigherStudyStatus' }, @@ -51,70 +55,81 @@ const ExportExcelComponent = ({ data, selectedTraining }) => { ); } } - sheet.columns = columns; + sheet.columns = columns; + const hyperlinkStyle = { + font: { color: { argb: '0000FF' }, underline: true }, + alignment: { vertical: 'middle', horizontal: 'left' } + }; // Populate data rows - data.forEach(row => { - const rowData = { - Name: row.userInfo.Name, - CollegeEmail: row.email, - UniversityRollNumber: row.userInfo.urn, - CollegeRollNumber: row.crn, - Gender: row.userInfo.gender, - MentorName: row.userInfo.mentor, - Batch: row.userInfo.batch, - Section: row.userInfo.section, - MotherName: row.userInfo.mother, - FatherName: row.userInfo.father, - ContactNumber: row.userInfo.contact, - AdmissionType: row.userInfo.admissionType, - PersonalEmail: row.userInfo.personalMail - }; + data.forEach(row => { + const rowData = { + Name: row.userInfo.Name, + CollegeEmail: row.email, + UniversityRollNumber: row.userInfo.urn, + CollegeRollNumber: row.crn, + Gender: row.userInfo.gender, + MentorName: row.userInfo.mentor, + Batch: row.userInfo.batch, + Section: row.userInfo.section, + MotherName: row.userInfo.mother, + FatherName: row.userInfo.father, + ContactNumber: row.userInfo.contact, + AdmissionType: row.userInfo.admissionType, + PersonalEmail: row.userInfo.personalMail + }; - // Add training-specific data based on selectedTraining - if (selectedTraining && row[selectedTraining]) { - const trainingData = row[selectedTraining]; - if (selectedTraining !== "placementData") { - rowData['TrainingType'] = trainingData.type; - rowData['OrganizationName'] = trainingData.organization; - rowData['ProjectName'] = trainingData.projectName; - rowData['TechnologyUsed'] = trainingData.technology.join(', '); + // Add training-specific data based on selectedTraining + if (selectedTraining && row[selectedTraining]) { - if (trainingData.certificate) { - const certificateBlob = base64toBlob(trainingData.certificate); - const certificateUrl = URL.createObjectURL(certificateBlob); - rowData['TrainingCertificate'] = { - text: 'View Certificate', - hyperlink: certificateUrl, - tooltip: 'Click to view certificate' - }; - } - } else { - rowData['PlacedStatus'] = trainingData.isPlaced; - rowData['Company'] = trainingData.company; - rowData['PlacementType'] = trainingData.placementType; - rowData['AppointmentNumber'] = trainingData.appointmentNo; - rowData['Package'] = trainingData.package; - rowData['AppointmentDate'] = trainingData.appointmentDate; - rowData['Designation'] = trainingData.designation; + const trainingData = row[selectedTraining]; + if (selectedTraining !== "placementData") { + rowData['TrainingType'] = trainingData.type; + rowData['OrganizationName'] = trainingData.organization; + rowData['ProjectName'] = trainingData.projectName; + rowData['TechnologyUsed'] = trainingData.technology.join(', '); + + if (trainingData.certificate) { + const certificateURL = `${API_URL}certificate/${selectedTraining}/${row._id}`; + rowData['TrainingCertificate'] = { text: 'View Certificate', hyperlink: certificateURL }; + } + + } else { + rowData['PlacedStatus'] = trainingData.isPlaced; + rowData['Company'] = trainingData.company; + rowData['PlacementType'] = trainingData.placementType; + rowData['AppointmentNumber'] = trainingData.appointmentNo; + rowData['Package'] = trainingData.package; + rowData['AppointmentDate'] = trainingData.appointmentDate; + rowData['Designation'] = trainingData.designation; + + if (trainingData.appointmentLetter) { + const certificateURL = `${API_URL}certificate/appointmentLetter/${row._id}`; + rowData['AppointmentLetter'] = { text: 'View Certificate', hyperlink: certificateURL }; + + } + rowData['HigherStudyStatus'] = trainingData.highStudy; + rowData['HigherStudyPlace'] = trainingData.highStudyplace; + rowData['GateAppearedStatus'] = trainingData.gateStatus; + if (trainingData.gateCertificate) { + const certificateURL = `${API_URL}certificate/gateCertificate/${row._id}`; + rowData['GateAdmitCard'] = { text: 'View Certificate', hyperlink: certificateURL }; - if (trainingData.appointmentLetter) { - const appointmentLetterBlob = base64toBlob(trainingData.appointmentLetter); - const certificateUrl = URL.createObjectURL(appointmentLetterBlob); - rowData['GateAdmitCard'] = { - text: 'View Certificate', - hyperlink: certificateUrl, - tooltip: 'Click to view certificate' - }; - } - rowData['HigherStudyStatus'] = trainingData.highStudy; - rowData['HigherStudyPlace'] = trainingData.highStudyplace; - rowData['GateAppearedStatus'] = trainingData.gateStatus; } } + } - sheet.addRow(rowData); + sheet.addRow(rowData); + }); + sheet.eachRow({ includeEmpty: false }, function (row) { + row.eachCell({ includeEmpty: false }, function (cell) { + if (cell.value && cell.value.hyperlink) { + cell.style = hyperlinkStyle; + } + }); + }); + sheet.columns.forEach(column => { + column.width = 20; }); - // Generate Excel file and download const buffer = await workbook.xlsx.writeBuffer(); const excelBlob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); diff --git a/client/src/Components/ProtectedRoute.jsx b/client/src/Components/ProtectedRoute.jsx index 38e2e45..fe16d77 100644 --- a/client/src/Components/ProtectedRoute.jsx +++ b/client/src/Components/ProtectedRoute.jsx @@ -27,31 +27,30 @@ const ProtectedRoute = ({ component: Component, path, ...rest }) => { return ; } if (userRole === 'superadmin' || userRole === 'admin') { - if(path==='/superadmin'){ + if (path === '/superadmin') { return ; } if (path === '/admin/editProfile') { return } - if (path === '/superadmin/studentData'){ + if (path === '/superadmin/studentData') { return } + if (path === "/superadmin/placementStats") { + return ; + } } // Check if the user is authenticated and has the required role if (userRole === "superadmin") { - if (path === "/superadmin/placementStats") { - return ; - } else if (path === "/superadmin/trainingNames") { + if (path === "/superadmin/trainingNames") { return ; - } else if (path === "/admin/editProfile") { - return ; - } - + } + // Redirect superadmin to home if trying to access admin or superadmin route - else { + else { return ; } - + } else { // Redirect to home or another appropriate route if the user doesn't have the required role if ( diff --git a/client/src/pages/EditProfile/EditProfile.jsx b/client/src/pages/EditProfile/EditProfile.jsx index e79e95b..3ac52e1 100644 --- a/client/src/pages/EditProfile/EditProfile.jsx +++ b/client/src/pages/EditProfile/EditProfile.jsx @@ -95,7 +95,9 @@ const EditProfile = () => { const data = response.data.data; const userInfoData = response.data.data.userInfo if (data) { + const datePickerBatch = convertBatchToDate(userInfoData.batch); + setFormData({ ...data }); setuserInfo({ ...userInfoData }); @@ -117,7 +119,6 @@ const EditProfile = () => { }; const hanldeChangePassword = async () => { try { - console.log(passwordState) const passwordError = validateField('password', passwordState.password ); const confirmPasswordError = validateField('confirmPassword', passwordState.confirmPassword, passwordState); @@ -171,18 +172,14 @@ const EditProfile = () => { } }; - // Function to handle edit button click - // Function to handle submit button click const handleSubmit = async () => { try { - // Validate form data - console.log(formData) + const formDataErrors = Object.keys(formData).reduce((acc, key) => { const error = validateField(key, formData[key]); return error ? { ...acc, [key]: error } : acc; }, {}); - console.log(userInfo) // Validate userInfo const userInfoErrors = Object.keys(userInfo).reduce((acc, key) => { const error = validateField(key, userInfo[key]); @@ -311,9 +308,9 @@ const EditProfile = () => { })); }; const handleBatchChange = (newDate) => { - // (newDate); setIsChanged(true); if (newDate) { + setAdmissionYear(newDate) const year = newDate.$y; setuserInfo({ ...userInfo, batch: `${year}-${year + 4}` }); @@ -630,7 +627,6 @@ const EditProfile = () => { } onChange={handleBatchChange} diff --git a/client/src/pages/Placement Graphs/PlacementStats.jsx b/client/src/pages/Placement Graphs/PlacementStats.jsx index 4c318a4..d796b25 100644 --- a/client/src/pages/Placement Graphs/PlacementStats.jsx +++ b/client/src/pages/Placement Graphs/PlacementStats.jsx @@ -1,7 +1,7 @@ import React, { useEffect, useState, useRef } from 'react'; import jsPDF from 'jspdf'; import axios from 'axios'; -import { Box, Button, Grid, FormControl, MenuItem, TextField, LinearProgress } from '@mui/material'; +import { Box, Button, Grid, FormControl, MenuItem, TextField, LinearProgress, Typography } from '@mui/material'; import BarGraph from '../../Components/Charts/BarGraph'; import LineGraph from '../../Components/Charts/genderGraph'; import CompanyGraph from '../../Components/Charts/companyGraph'; @@ -74,6 +74,9 @@ const PlacementStats = () => { return (
+ {loading && ( + + )} { {loading ? ( - - + + Fetching Data .... ) : ( { setLoading(true); const usersData = await fetchUsers(selectedBatch, selectedTraining); - if (usersData && usersData.users) { - setUsers(usersData.users); + if (usersData) { + + setUsers(usersData); } else { + setUsers([]); } } catch (error) { console.error("Error fetching user details:", error); setUsers([]); // Reset users state or handle as needed } finally { + setLoading(false); + } } else { setUsers([]); @@ -113,11 +118,17 @@ const SuperAdminForm = () => { } }; + const navigateToStats = (data) => { return navigate("/superadmin/placementStats", { state: { data } }); }; - const handleViewCertificate = (row) => { - viewCertificate(row, selectedTraining); + const handleViewCertificate = (row, training, certificateType) => { + if (selectedTraining === "all") { + viewCertificate(row, training, certificateType); + } else { + viewCertificate(row, selectedTraining); + } + }; const columns = useMemo(() => { @@ -126,7 +137,6 @@ const SuperAdminForm = () => { { accessorKey: "userInfo.Name", header: "Name" }, { accessorKey: "userInfo.urn", header: "URN" }, { accessorKey: "userInfo.mentor", header: "Mentor" }, - { accessorKey: "userInfo.batch", header: "Batch" }, { accessorKey: "userInfo.section", header: "Section" }, { accessorKey: "userInfo.contact", header: "Contact" }, ]; @@ -137,25 +147,22 @@ const SuperAdminForm = () => { { accessorKey: `${selectedTraining}.isPlaced`, header: "Placement Status", - Cell: ({ row }) => - row.original[selectedTraining]?.isPlaced ? "Yes" : "No", - }, - { - accessorKey: `${selectedTraining}.package`, - header: "Package", - + Cell: ({ row }) => (row.original[selectedTraining]?.isPlaced ? "Yes" : "No"), }, { accessorKey: `${selectedTraining}.highStudy`, header: "Higher Study", - Cell: ({ row }) => - row.original[selectedTraining]?.highStudy ? "Yes" : "No", + Cell: ({ row }) => (row.original[selectedTraining]?.highStudy ? "Yes" : "No"), }, { accessorKey: `${selectedTraining}.gateStatus`, header: "Gate Status", - Cell: ({ row }) => - row.original[selectedTraining]?.gateStatus ? "Yes" : "No", + Cell: ({ row }) => (row.original[selectedTraining]?.gateStatus ? "Yes" : "No"), + }, + { + accessorKey: `${selectedTraining}.package`, + header: "Package", + }, { accessorKey: "viewMore", @@ -169,16 +176,15 @@ const SuperAdminForm = () => { style={{ cursor: "pointer" }} /> ), - }, + } ); } - if (selectedTraining !== "placementData") { + if (selectedTraining !== "placementData" && selectedTraining !== "all") { customColumns.push( { accessorKey: `${selectedTraining}.technology`, header: "Technology", - Cell: ({ row }) => - row.original[selectedTraining]?.technology.join(" , "), + Cell: ({ row }) => row.original[selectedTraining]?.technology.join(" , "), }, { accessorKey: `${selectedTraining}.organization`, @@ -196,35 +202,132 @@ const SuperAdminForm = () => { accessorKey: `${selectedTraining}.certificate`, header: "Certificate", Cell: ({ row }) => ( + // console.log(row.original.tr101.certificate) , handleViewCertificate(row)} style={{ cursor: "pointer" }} /> ), - }, + } ); } + if (selectedTraining === "all") { + customColumns.push( + { + + accessorKey: "tr101.certificate", + header: "Tr101 Certificate", + Cell: ({ row }) => ( + + handleViewCertificate(row, "tr101")} + style={{ cursor: "pointer" }} + /> + ), + }, + { + accessorKey: "tr102.certificate", + header: "Tr102 Certificate", + Cell: ({ row }) => ( + handleViewCertificate(row, "tr102")} + style={{ cursor: "pointer" }} + /> + ), + }, + { + accessorKey: "tr103.certificate", + header: "Tr103 Certificate", + Cell: ({ row }) => ( + handleViewCertificate(row, "tr103")} + style={{ cursor: "pointer" }} + /> + ), + }, + { + accessorKey: "tr104.certificate", + header: "Tr104 Certificate", + Cell: ({ row }) => ( + handleViewCertificate(row, "tr104")} + style={{ cursor: "pointer" }} + /> + ), + }, + { + accessorKey: `${selectedTraining}.isPlaced`, + header: "Placement Status", + Cell: ({ row }) => (row.original[selectedTraining]?.isPlaced ? "Yes" : "No"), + }, + { + accessorKey: `${selectedTraining}.highStudy`, + header: "Higher Study", + Cell: ({ row }) => (row.original[selectedTraining]?.highStudy ? "Yes" : "No"), + }, + { + accessorKey: `${selectedTraining}.gateStatus`, + header: "Gate Status", + Cell: ({ row }) => (row.original[selectedTraining]?.gateStatus ? "Yes" : "No"), + }, + { + accessorKey: `${selectedTraining}.package`, + header: "Package", + }, + { + accessorKey: "placementData.appointmentLetter", + header: "Appointment Letter", + Cell: ({ row }) => ( + handleViewCertificate(row, "placementData", "appointmentLetter")} + style={{ cursor: "pointer" }} + /> + ), + }, + { + accessorKey: "placementData.gateCertificate", + header: "Gate Admit Card/Score Card", + Cell: ({ row }) => ( + handleViewCertificate(row, "placementData", "gateCertificate")} + style={{ cursor: "pointer" }} + /> + ), + }, + + ); + } // Add the "Verified" and "Mark Verification" columns at the end - customColumns.push( - { - accessorKey: `${selectedTraining}.lock`, - header: "Verified", - Cell: ({ row }) => - row.original[selectedTraining]?.lock ? "Yes" : "No", - }, - { - accessorKey: "edit", - header: "Mark Verification", - Cell: ({ row }) => ( - - ), - }, - ); + if (selectedTraining !== "all") { + + + customColumns.push( + { + accessorKey: `${selectedTraining}.lock`, + header: "Verified", + Cell: ({ row }) => (row.original[selectedTraining]?.lock ? "Yes" : "No"), + }, + { + accessorKey: "edit", + header: "Mark Verification", + Cell: ({ row }) => ( + + ), + } + ); + } } return customColumns; @@ -294,14 +397,14 @@ const SuperAdminForm = () => { data: users, columns, localization: { - noRecordsToDisplay: - "Please Select Branch , Batch and Training type to view data.", + noRecordsToDisplay: 'Please Select Branch , Batch and Training type to view data.' }, + + }); // Function to handle refreshing data after verification status change const handleRefresh = () => { - console.log("handle refresh called"); setRefresh((prevRefresh) => !prevRefresh); }; @@ -376,7 +479,7 @@ const SuperAdminForm = () => { label={"Training"} onChange={handleTrainingChange} > - All + None {Array.from( { length: trainingNames[0]["Training_No"] }, (_, index) => { @@ -396,6 +499,7 @@ const SuperAdminForm = () => { {trainingNames[0]["Placement_name"]} + All diff --git a/client/src/pages/SuperAdminDashboard/SuperAdmin.jsx b/client/src/pages/SuperAdminDashboard/SuperAdmin.jsx index 817ce09..1ab1b91 100644 --- a/client/src/pages/SuperAdminDashboard/SuperAdmin.jsx +++ b/client/src/pages/SuperAdminDashboard/SuperAdmin.jsx @@ -1,11 +1,12 @@ import { ToastContainer } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; -import { Edit, School, ViewList } from "@mui/icons-material"; +import { Edit, School, ViewList, Download } from "@mui/icons-material"; import { useNavigate } from "react-router-dom"; -import { Grid, Button, Paper, Typography } from "@mui/material"; +import { Grid, Button, Paper, Typography, CircularProgress } from "@mui/material"; import { styled } from "@mui/system"; import { useState, useEffect } from "react"; import { decodeUserRole } from "../../utils/AdminFunctions"; +import { handleExportFullData } from "../../utils/ExportAllData"; const API_URL = import.meta.env.VITE_ENV === "production" @@ -37,24 +38,25 @@ const CardContainer = styled(Paper)({ }); const SuperAdminForm = () => { - const [role, setRole] = useState(""); + const [role, setRole] = useState("") + const [loading, setLoading] = useState(false) const navigate = useNavigate(); const navigateToTrainingNames = () => navigate("/superadmin/trainingNames"); const navigateToEditProfile = () => navigate("/admin/editProfile"); const navigateToStudentData = () => navigate("/superadmin/studentData"); useEffect(() => { - const token = localStorage.getItem("authtoken"); - const decodedRole = decodeUserRole(token); - setRole(decodedRole); - }, []); + const token = localStorage.getItem('authtoken'); + const decodedRole = decodeUserRole(token) + setRole(decodedRole) + }, []) return (
- {role === "superadmin" && ( + {role === "superadmin" && { > - {"Change Training Names"} + Change Training Names - )} + } @@ -91,6 +93,17 @@ const SuperAdminForm = () => { + + + handleExportFullData(setLoading)}> + {loading ? : } + + + Download All Data As Excel + + + +
diff --git a/client/src/utils/AdminFunctions.js b/client/src/utils/AdminFunctions.js index ca90b55..4e6af3b 100644 --- a/client/src/utils/AdminFunctions.js +++ b/client/src/utils/AdminFunctions.js @@ -45,7 +45,7 @@ export const fetchUsers = async (selectedBatch, selectedTraining) => { }); const filteredUsers = response.data.data - return { users: filteredUsers }; + return filteredUsers; } catch (error) { console.error("Error fetching users:", error); throw new Error("Error fetching users"); @@ -91,7 +91,7 @@ export const changeLock = async ( }; export const getTrainingOptions = (adminType, trainingNames) => { - const options = [{ value: "", label: "All" }]; + const options = [{ value: "", label: "None" }]; const trainingNumber = trainingNames[0]["Training_No"]; @@ -112,8 +112,9 @@ export const getTrainingOptions = (adminType, trainingNames) => { return options; }; -export const viewCertificate = (row, selectedTraining) => { - if (selectedTraining === "placementData") { +export const viewCertificate = (row, selectedTraining, certificateType) => { + + if (selectedTraining === "placementData" && certificateType === "appointmentLetter") { if ( row.original.placementData && row.original.placementData.appointmentLetter @@ -124,6 +125,18 @@ export const viewCertificate = (row, selectedTraining) => { "Appointment Letter not found for this user in placement data.", ); } + } + else if (selectedTraining === "placementData" && certificateType === "gateCertificate") { + if ( + row.original.placementData && + row.original.placementData.gateCertificate + ) { + openBase64NewTab(row.original.placementData.gateCertificate); + } else { + console.error( + "Gate Certificate not found for this user in placement data.", + ); + } } else if ( selectedTraining && row.original[selectedTraining] && @@ -163,3 +176,23 @@ export const decodeUserRole = (token) => { return null; } }; + + + +export const getAllData = async () => { + try { + const token = localStorage.getItem("authtoken"); + const response = await axios.get(`${API_URL}users/getallusers`, { + headers: { + "auth-token": token, + }, + }); + + const users = response.data.data + + return users; + } catch (error) { + console.error("Error fetching users:", error); + throw new Error("Error fetching users"); + } +}; \ No newline at end of file diff --git a/client/src/utils/ExportAllData.js b/client/src/utils/ExportAllData.js new file mode 100644 index 0000000..2529508 --- /dev/null +++ b/client/src/utils/ExportAllData.js @@ -0,0 +1,180 @@ +import { base64toBlob } from '../utils/base64topdf'; +import { saveAs } from 'file-saver'; +import ExcelJS from 'exceljs'; +import { getAllData } from './AdminFunctions'; +const API_URL = + import.meta.env.VITE_ENV === "production" + ? import.meta.env.VITE_PROD_BASE_URL + : import.meta.env.VITE_DEV_BASE_URL; + +export const handleExportFullData = async (setLoading) => { + setLoading(true) + const data = await getAllData() + const workbook = new ExcelJS.Workbook(); + const sheet = workbook.addWorksheet('Training Data'); + + // Define column headers + const columns = [ + { header: 'Name', key: 'Name' }, + { header: 'College Email', key: 'CollegeEmail' }, + { header: 'University Roll Number', key: 'UniversityRollNumber' }, + { header: 'College Roll Number', key: 'CollegeRollNumber' }, + { header: 'Gender', key: 'Gender' }, + { header: 'Mentor Name', key: 'MentorName' }, + { header: 'Batch', key: 'Batch' }, + { header: 'Section', key: 'Section' }, + { header: "Mother's Name", key: 'MotherName' }, + { header: "Father's Name", key: 'FatherName' }, + { header: 'Contact Number', key: 'ContactNumber' }, + { header: 'Admission Type', key: 'AdmissionType' }, + { header: 'Personal Email', key: 'PersonalEmail' }, + + //tr101 + { header: 'Training Type', key: 'TrainingType1' }, + { header: 'Organization Name', key: 'OrganizationName1' }, + { header: 'Project Name', key: 'ProjectName1' }, + { header: 'Technology Used', key: 'TechnologyUsed1' }, + { header: 'Training Certificate', key: 'TrainingCertificate1' }, + + //tr102 + { header: 'Training Type', key: 'TrainingType2' }, + { header: 'Organization Name', key: 'OrganizationName2' }, + { header: 'Project Name', key: 'ProjectName2' }, + { header: 'Technology Used', key: 'TechnologyUsed2' }, + { header: 'Training Certificate', key: 'TrainingCertificate2' }, + + //tr103 + { header: 'Training Type', key: 'TrainingType3' }, + { header: 'Organization Name', key: 'OrganizationName3' }, + { header: 'Project Name', key: 'ProjectName3' }, + { header: 'Technology Used', key: 'TechnologyUsed3' }, + { header: 'Training Certificate', key: 'TrainingCertificate3' }, + + //tr104 + { header: 'Training Type', key: 'TrainingType4' }, + { header: 'Organization Name', key: 'OrganizationName4' }, + { header: 'Project Name', key: 'ProjectName4' }, + { header: 'Technology Used', key: 'TechnologyUsed4' }, + { header: 'Training Certificate', key: 'TrainingCertificate4' }, + + //placement data + { header: 'Placed Status', key: 'PlacedStatus' }, + { header: 'Company', key: 'Company' }, + { header: 'Placement Type', key: 'PlacementType' }, + { header: 'Appointment Number', key: 'AppointmentNumber' }, + { header: 'Appointment Letter', key: 'AppointmentLetter' }, + { header: 'Package', key: 'Package' }, + { header: 'Appointment Date', key: 'AppointmentDate' }, + { header: 'Designation', key: 'Designation' }, + { header: 'Higher Study Status', key: 'HigherStudyStatus' }, + { header: 'Higher Study Place', key: 'HigherStudyPlace' }, + { header: 'Gate Appeared Status', key: 'GateAppearedStatus' }, + { header: 'Gate Admit Card/ ScoreCard', key: 'GateAdmitCard' } + ]; + sheet.columns = columns; + // Populate data rows + const hyperlinkStyle = { + font: { color: { argb: '0000FF' }, underline: true }, + alignment: { vertical: 'middle', horizontal: 'left' } + }; + data.forEach(row => { + const rowData = { + Name: row.userInfo.Name, + CollegeEmail: row.email, + UniversityRollNumber: row.userInfo.urn, + CollegeRollNumber: row.crn, + Gender: row.userInfo.gender, + MentorName: row.userInfo.mentor, + Batch: row.userInfo.batch, + Section: row.userInfo.section, + MotherName: row.userInfo.mother, + FatherName: row.userInfo.father, + ContactNumber: row.userInfo.contact, + AdmissionType: row.userInfo.admissionType, + PersonalEmail: row.userInfo.personalMail, + TrainingType1: row.tr101.type, + OrganizationName1: row.tr101.organization, + ProjectName1: row.tr101.projectName, + TechnologyUsed1: row.tr101.technology.join(', '), + //tr102 + TrainingType2: row.tr102.type, + OrganizationName2: row.tr102.organization, + ProjectName2: row.tr102.projectName, + TechnologyUsed2: row.tr102.technology.join(', '), + //tr103 + TrainingType3: row.tr103.type, + OrganizationName3: row.tr103.organization, + ProjectName3: row.tr103.projectName, + TechnologyUsed3: row.tr103.technology.join(', '), + //tr104 + TrainingType4: row.tr104.type, + OrganizationName4: row.tr104.organization, + ProjectName4: row.tr104.projectName, + TechnologyUsed4: row.tr104.technology.join(', '), + //placementData + PlacedStatus: row.placementData.isPlaced, + Company: row.placementData.company, + PlacementType: row.placementData.placementType, + AppointmentNumber: row.placementData.appointmentNo, + Package: row.placementData.package, + AppointmentDate: row.placementData.appointmentDate, + Designation: row.placementData.designation, + + + }; + if (row.tr101.certificate) { + const certificateURL = `${API_URL}certificate/tr101/${row._id}`; // Replace with actual URL + rowData['TrainingCertificate1'] = { text: 'View Certificate', hyperlink: certificateURL }; + + } + if (row.tr102.certificate) { + const certificateURL = `${API_URL}certificate/tr102/${row._id}`; // Replace with actual URL + rowData['TrainingCertificate1'] = { text: 'View Certificate', hyperlink: certificateURL }; + + } + if (row.tr103.certificate) { + const certificateURL = `${API_URL}certificate/tr103/${row._id}`; // Replace with actual URL + rowData['TrainingCertificate1'] = { text: 'View Certificate', hyperlink: certificateURL }; + + } + if (row.tr104.certificate) { + const certificateURL = `${API_URL}certificate/tr104/${row._id}`; // Replace with actual URL + rowData['TrainingCertificate1'] = { text: 'View Certificate', hyperlink: certificateURL }; + + } + if (row.placementData.appointmentLetter) { + const certificateURL = `${API_URL}certificate/appointmentLetter/${row._id}`; // Replace with actual URL + rowData['AppointmentLetter'] = { text: 'View Certificate', hyperlink: certificateURL }; + + } + if (row.placementData.gateCertificate) { + const certificateURL = `${API_URL}certificate/gateCertificate/${row._id}`; // Replace with actual URL + rowData['GateAdmitCard'] = { text: 'View Certificate', hyperlink: certificateURL }; + + } + + + + sheet.addRow(rowData); + }) + sheet.eachRow({ includeEmpty: false }, function (row) { + row.eachCell({ includeEmpty: false }, function (cell) { + if (cell.value && cell.value.hyperlink) { + cell.style = hyperlinkStyle; + } + }); + }); + sheet.columns.forEach(column => { + column.width = 20; + }); + // Generate Excel file and download + const buffer = await workbook.xlsx.writeBuffer(); + const excelBlob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); + setLoading(false) + saveAs(excelBlob, 'training_data.xlsx'); +}; + + + + +