Navbar, bulk email (fix toggle error), department consolidated

This commit is contained in:
Harshitha Shetty
2025-02-04 12:07:13 +05:30
parent 1e91864769
commit 4a3154752f
7 changed files with 412 additions and 20 deletions

BIN
client/public/logo_.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

View File

@@ -14,6 +14,7 @@ import ConsolidatedTable from "./Pages/ConsolidatedTable";
import CourseConsolidated from "./Pages/courseConsolidated"; import CourseConsolidated from "./Pages/courseConsolidated";
import PrivateRoute from "./components/PrivateRoute"; import PrivateRoute from "./components/PrivateRoute";
import TokenRefresher from "./components/TokenRefresher"; import TokenRefresher from "./components/TokenRefresher";
import DepartmentConsolidated from "./Pages/DepartmentConsolidated";
function App() { function App() {
return ( return (
@@ -30,6 +31,7 @@ function App() {
<Route path="/courses" element={<PrivateRoute element={<CourseTable />} />} /> <Route path="/courses" element={<PrivateRoute element={<CourseTable />} />} />
<Route path="/consolidated" element={<PrivateRoute element={<ConsolidatedTable />} />} /> <Route path="/consolidated" element={<PrivateRoute element={<ConsolidatedTable />} />} />
<Route path="/courseConsolidated" element={<PrivateRoute element={<CourseConsolidated />} />} /> <Route path="/courseConsolidated" element={<PrivateRoute element={<CourseConsolidated />} />} />
<Route path="/departmentConsolidated" element={<PrivateRoute element={<DepartmentConsolidated />} />} />
</Routes> </Routes>
</> </>
); );

View File

@@ -0,0 +1,287 @@
import React, { useState, useEffect } from "react";
import axios from "axios";
import Navbar from "./Navbar";
const DepartmentConsolidated = () => {
const [searchQuery, setSearchQuery] = useState("");
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [currentPage, setCurrentPage] = useState(1);
const tablesPerPage = 5;
const [expandedDepartment, setExpandedDepartment] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get(
"http://localhost:8080/api/table/department-consolidated",
{ withCredentials: true }
);
setData(response.data);
setLoading(false);
} catch (error) {
console.error("Error fetching table data:", error);
setLoading(false);
}
};
fetchData();
}, []);
if (loading) {
return <div>Loading...</div>;
}
// Filter departments based on search query
const filteredDepartments = data.filter((department) =>
department.department.toLowerCase().includes(searchQuery.toLowerCase())
);
const totalPages = Math.ceil(filteredDepartments.length / tablesPerPage);
const indexOfLastTable = currentPage * tablesPerPage;
const indexOfFirstTable = indexOfLastTable - tablesPerPage;
const currentDepartments = filteredDepartments.slice(
indexOfFirstTable,
indexOfLastTable
);
const handleNextPage = () => {
if (currentPage < totalPages) setCurrentPage((prevPage) => prevPage + 1);
};
const handlePrevPage = () => {
if (currentPage > 1) setCurrentPage((prevPage) => prevPage - 1);
};
return (
<>
<Navbar />
<div>
<h1
style={{
textAlign: "center",
background: "#003366",
color: "white",
padding: "20px 0",
fontSize: "24px",
}}
>
Department-wise Course Tables
</h1>
<div style={{ padding: "10px", marginBottom: "30px" }}>
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search for a department..."
style={{
width: "100%",
padding: "10px",
borderRadius: "5px",
border: "1px solid #ccc",
fontSize: "16px",
}}
/>
</div>
<div
style={{
maxHeight: "70vh",
overflowY: "auto",
border: "1px solid #ccc",
padding: "10px",
borderRadius: "5px",
backgroundColor: "#f9f9f9",
}}
>
{currentDepartments.map((department, index) => (
<div key={index} style={{ marginBottom: "20px" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
cursor: "pointer",
backgroundColor: "#ffffff",
color: "black",
padding: "10px",
borderRadius: "5px",
}}
onClick={() =>
setExpandedDepartment(
expandedDepartment === department.department
? null
: department.department
)
}
>
<h2 style={{ margin: 0 }}>{department.department}</h2>
</div>
{expandedDepartment === department.department && (
<div>
{department.courses.map((course, courseIndex) => (
<div key={courseIndex} style={{ marginTop: "20px" }}>
<h3 style={{ marginBottom: "10px" }}>
{course.courseName} ({course.courseCode})
</h3>
<table
border="1"
style={{
width: "100%",
textAlign: "left",
marginBottom: "20px",
}}
>
<thead>
<tr>
<th>Semester</th>
<th>Course Code</th>
<th>Course Name</th>
<th>Exam Type</th>
<th>Year</th>
<th>Oral/Practical</th>
<th>Assessment</th>
<th>Reassessment</th>
<th>Paper Setting</th>
<th>Moderation</th>
<th>PwD Paper Setting</th>
</tr>
</thead>
<tbody>
<tr>
<td>{course.semester}</td>
<td>{course.courseCode}</td>
<td>{course.courseName}</td>
<td>{course.examType}</td>
<td>{course.year}</td>
<td>
{course.oralPracticalTeachers.length > 0 ? (
<ul style={{ margin: 0, paddingLeft: "20px" }}>
{course.oralPracticalTeachers.map(
(teacher, idx) => (
<li key={idx}>{teacher}</li>
)
)}
</ul>
) : (
"N/A"
)}
</td>
<td>
{course.assessmentTeachers.length > 0 ? (
<ul style={{ margin: 0, paddingLeft: "20px" }}>
{course.assessmentTeachers.map(
(teacher, idx) => (
<li key={idx}>{teacher}</li>
)
)}
</ul>
) : (
"N/A"
)}
</td>
<td>
{course.reassessmentTeachers.length > 0 ? (
<ul style={{ margin: 0, paddingLeft: "20px" }}>
{course.reassessmentTeachers.map(
(teacher, idx) => (
<li key={idx}>{teacher}</li>
)
)}
</ul>
) : (
"N/A"
)}
</td>
<td>
{course.paperSettingTeachers.length > 0 ? (
<ul style={{ margin: 0, paddingLeft: "20px" }}>
{course.paperSettingTeachers.map(
(teacher, idx) => (
<li key={idx}>{teacher}</li>
)
)}
</ul>
) : (
"N/A"
)}
</td>
<td>
{course.moderationTeachers.length > 0 ? (
<ul style={{ margin: 0, paddingLeft: "20px" }}>
{course.moderationTeachers.map(
(teacher, idx) => (
<li key={idx}>{teacher}</li>
)
)}
</ul>
) : (
"N/A"
)}
</td>
<td>
{course.pwdPaperSettingTeachers.length > 0 ? (
<ul style={{ margin: 0, paddingLeft: "20px" }}>
{course.pwdPaperSettingTeachers.map(
(teacher, idx) => (
<li key={idx}>{teacher}</li>
)
)}
</ul>
) : (
"N/A"
)}
</td>
</tr>
</tbody>
</table>
</div>
))}
</div>
)}
</div>
))}
</div>
{/* Pagination controls */}
<div style={{ textAlign: "center", marginTop: "20px" }}>
<button
onClick={handlePrevPage}
disabled={currentPage === 1}
style={{
padding: "10px 15px",
marginRight: "10px",
backgroundColor: currentPage === 1 ? "#ccc" : "#007bff",
color: "white",
borderRadius: "5px",
border: "none",
}}
>
Previous
</button>
<span>
Page {currentPage} of {totalPages}
</span>
<button
onClick={handleNextPage}
disabled={currentPage === totalPages}
style={{
padding: "10px 15px",
marginLeft: "10px",
backgroundColor: currentPage === totalPages ? "#ccc" : "#007bff",
color: "white",
borderRadius: "5px",
border: "none",
}}
>
Next
</button>
</div>
</div>
</>
);
};
export default DepartmentConsolidated;

View File

@@ -150,14 +150,14 @@ function TogglerContainer(props) {
<div className="toggle-container"> <div className="toggle-container">
<div className="toggle"> <div className="toggle">
<div className="toggle-panel toggle-left"> <div className="toggle-panel toggle-left">
<h1>Welcome to MERN Auth App</h1> <h1>Welcome to Appointment to Examiner</h1>
<p>Already Have an Account?</p> <p>Already Have an Account?</p>
<button className="hidden" onClick={props.ToggleSign}> <button className="hidden" onClick={props.ToggleSign}>
Sign In Sign In
</button> </button>
</div> </div>
<div className="toggle-panel toggle-right"> <div className="toggle-panel toggle-right">
<h1>Welcome to MERN Auth App</h1> <h1>Welcome to Appointment to Examiner</h1>
<p>Don't have an account? Create one</p> <p>Don't have an account? Create one</p>
<button className="hidden" onClick={props.ToggleSign}> <button className="hidden" onClick={props.ToggleSign}>
Sign Up Sign Up

View File

@@ -61,3 +61,30 @@ width: 100%;
.button-container button:hover { .button-container button:hover {
background-color: #ffcccc; /* Hover color */ background-color: #ffcccc; /* Hover color */
} }
.logout-button{
background-color: #b22222;
color: #fff;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease;
flex: 1; /* Fields will take up equal space */
max-width: 200px; /* Ensures fields don't get too wide */
padding: 8px 38px;
border-radius: 5px;
border: none;
font-size: 14px;
text-align: center;
text-decoration: none;
}
.navbar-logo {
display: flex;
align-items: center;
/* margin-right: auto; */
}
.logo-img {
width: 60px; /* Adjust size as needed */
height: auto;
}

View File

@@ -43,20 +43,25 @@ const Navbar = () => {
<header className="navbar"> <header className="navbar">
<div className="navbar-container"> <div className="navbar-container">
<ToastContainer /> <ToastContainer />
{/* Appointment To Examiner text at the left */}
<NavLink to="/Welcome" className="navbar-title nav-btn"> <NavLink to="/Welcome" className="navbar-logo">
Appointment To Examiner <img src="logo_.png" alt="Logo" className="logo-img" />
</NavLink> </NavLink>
{/* Consolidated buttons in the center */}
<div className="button-container"> <div className="button-container">
{/* Consolidated buttons in the center */}
<NavLink to="/consolidated" className="consolidated-button nav-btn"> <NavLink to="/consolidated" className="consolidated-button nav-btn">
Faculty Faculty Consolidated
Consolidated
</NavLink> </NavLink>
<NavLink to="/courseConsolidated" className="consolidated-button nav-btn"> <NavLink to="/courseConsolidated" className="consolidated-button nav-btn">
Course Consolidated Course Consolidated
</NavLink> </NavLink>
<NavLink to="/departmentConsolidated" className="navbar-title nav-btn">
Department Consolidated
</NavLink>
</div> </div>
<div> <div>
<button className="logout-button" onClick={handleLogout}> <button className="logout-button" onClick={handleLogout}>

View File

@@ -145,4 +145,75 @@ router.get("/course-consolidated", async (req, res) => {
} }
}); });
router.get("/department-consolidated", async (req, res) => {
try {
const appointments = await Appointment.find();
const courses = await Course.find();
// Group appointments by department and course
const groupedByDepartment = {};
appointments.forEach((appointment) => {
const course = courses.find((c) => c.courseId === appointment.courseId);
if (!course) return; // Skip if course not found
const department = course.department;
if (!groupedByDepartment[department]) {
groupedByDepartment[department] = {
department: department,
courses: [],
};
}
// Find or create the course in the department
let courseData = groupedByDepartment[department].courses.find(
(c) => c.courseCode === appointment.courseId
);
if (!courseData) {
courseData = {
courseCode: appointment.courseId,
courseName: appointment.courseName,
semester: course.semester,
examType: course.scheme,
year: course.program,
oralPracticalTeachers: [],
assessmentTeachers: [],
reassessmentTeachers: [],
paperSettingTeachers: [],
moderationTeachers: [],
pwdPaperSettingTeachers: [],
};
groupedByDepartment[department].courses.push(courseData);
}
// Add the faculty name to the appropriate task
if (appointment.task === "oralsPracticals") {
courseData.oralPracticalTeachers.push(appointment.facultyName);
} else if (appointment.task === "assessment") {
courseData.assessmentTeachers.push(appointment.facultyName);
} else if (appointment.task === "reassessment") {
courseData.reassessmentTeachers.push(appointment.facultyName);
} else if (appointment.task === "paperSetting") {
courseData.paperSettingTeachers.push(appointment.facultyName);
} else if (appointment.task === "moderation") {
courseData.moderationTeachers.push(appointment.facultyName);
} else if (appointment.task === "pwdPaperSetter") {
courseData.pwdPaperSettingTeachers.push(appointment.facultyName);
}
});
// Convert to array format
const consolidatedData = Object.values(groupedByDepartment);
res.status(200).json(consolidatedData);
} catch (error) {
console.error("Error fetching department consolidated data:", error);
res.status(500).json({ message: "Failed to fetch department consolidated data" });
}
});
module.exports = router; module.exports = router;