Build a complete, responsive University Management System using Node.js, Express, EJS for templating, MongoDB (with Mongoose) for data storage, and Bootstrap for a modern UI. Use express-session, passport.js, and connect-flash for authentication. The system must include at least 10 .ejs pages, clean layout, and CRUD operations for each entity. The .ejs pages should be modular and use a base layout.ejs file for templating.
Pages to include:
layout.ejs – Main layout template with header, footer, and navigation bar.
dashboard.ejs – Admin dashboard showing stats (total students, courses, faculty).
students.ejs – List all students with add/edit/delete options.
faculty.ejs – List faculty with management options.
courses.ejs – List all courses, allow assigning students and faculty.
student-profile.ejs – Individual student details with enrolled courses.
faculty-profile.ejs – Faculty details and their assigned courses.
login.ejs – User login page with validation and flash messages.
register.ejs – User registration page (role: student/faculty).
admin-users.ejs – Admin page for managing user roles and access (e.g., promote to admin).
Requirements:
Use partials/ folder for reusable EJS components (header.ejs, footer.ejs, nav.ejs).
Navbar should adapt based on login status and user role (student, faculty, admin).
Fully responsive layout using Bootstrap (cards, modals, tables, alerts).
Add student/faculty/course using modals or separate forms.
Use Font Awesome for icons (edit, delete, view).
Implement authentication and session handling.
Show flash messages for success/error actions.
Provide sample Mongoose schemas for User, Student, Faculty, and Course.
Include routes, models, and EJS views for all major pages. File structure should follow MVC.
I'll create a complete University Management System with Node.js, Express, EJS, MongoDB, and Bootstrap. This system will include authentication, CRUD operations, and responsive design.
For a complete University Management System, you would need the following MongoDB models:
// User Model (for authentication)
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
role: { type: String, enum: ['student', 'faculty', 'admin'], required: true },
firstName: { type: String, required: true },
lastName: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
lastLogin: { type: Date },
isActive: { type: Boolean, default: true }
});
// Student Model
const studentSchema = new mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
studentId: { type: String, required: true, unique: true },
department: { type: String, required: true },
year: { type: String, required: true },
enrolledCourses: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Course' }],
advisor: { type: mongoose.Schema.Types.ObjectId, ref: 'Faculty' },
gpa: { type: Number, default: 0.0 },
address: { type: String },
phoneNumber: { type: String },
dateOfBirth: { type: Date }
});
// Faculty Model
const facultySchema = new mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
facultyId: { type: String, required: true, unique: true },
department: { type: String, required: true },
position: { type: String, required: true },
assignedCourses: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Course' }],
office: { type: String },
officeHours: { type: String },
phoneNumber: { type: String },
bio: { type: String }
});
// Course Model
const courseSchema = new mongoose.Schema({
code: { type: String, required: true, unique: true },
name: { type: String, required: true },
department: { type: String, required: true },
description: { type: String },
credits: { type: Number, required: true },
instructor: { type: mongoose.Schema.Types.ObjectId, ref: 'Faculty' },
students: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Student' }],
schedule: { type: String },
room: { type: String },
semester: { type: String, required: true },
year: { type: Number, required: true },
status: { type: String, enum: ['Active', 'Upcoming', 'Archived'], default: 'Active' }
});
// Enrollment Model (relationship between students and courses)
const enrollmentSchema = new mongoose.Schema({
student: { type: mongoose.Schema.Types.ObjectId, ref: 'Student', required: true },
course: { type: mongoose.Schema.Types.ObjectId, ref: 'Course', required: true },
enrollmentDate: { type: Date, default: Date.now },
grade: { type: String },
status: { type: String, enum: ['Active', 'Dropped', 'Completed'], default: 'Active' }
});
// Assignment Model
const assignmentSchema = new mongoose.Schema({
title: { type: String, required: true },
description: { type: String, required: true },
course: { type: mongoose.Schema.Types.ObjectId, ref: 'Course', required: true },
dueDate: { type: Date, required: true },
totalPoints: { type: Number, required: true },
createdAt: { type: Date, default: Date.now },
attachments: [{ type: String }],
status: { type: String, enum: ['Draft', 'Published', 'Archived'], default: 'Draft' }
});
// Submission Model
const submissionSchema = new mongoose.Schema({
assignment: { type: mongoose.Schema.Types.ObjectId, ref: 'Assignment', required: true },
student: { type: mongoose.Schema.Types.ObjectId, ref: 'Student', required: true },
submissionDate: { type: Date, default: Date.now },
files: [{ type: String }],
comments: { type: String },
grade: { type: Number },
feedback: { type: String },
status: { type: String, enum: ['Submitted', 'Late', 'Graded'], default: 'Submitted' }
});
// Attendance Model
const attendanceSchema = new mongoose.Schema({
course: { type: mongoose.Schema.Types.ObjectId, ref: 'Course', required: true },
student: { type: mongoose.Schema.Types.ObjectId, ref: 'Student', required: true },
date: { type: Date, required: true },
status: { type: String, enum: ['Present', 'Absent', 'Late', 'Excused'], required: true },
notes: { type: String }
});
// Notification Model
const notificationSchema = new mongoose.Schema({
recipient: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
title: { type: String, required: true },
message: { type: String, required: true },
type: { type: String, enum: ['Info', 'Warning', 'Alert'], default: 'Info' },
isRead: { type: Boolean, default: false },
createdAt: { type: Date, default: Date.now }
});For the server-side implementation, you would use Express.js with the following structure:
// app.js
const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const passport = require('passport');
const flash = require('connect-flash');
const path = require('path');
const methodOverride = require('method-override');
// Initialize app
const app = express();
// Connect to MongoDB
mongoose.connect('mongodb://localhost/university_management_system', {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
});
// Set up EJS
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Middleware
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(methodOverride('_method'));
// Session configuration
app.use(session({
secret: 'university-management-secret',
resave: false,
saveUninitialized: false
}));
// Passport middleware
app.use(passport.initialize());
app.use(passport.session());
// Flash messages
app.use(flash());
// Global variables
app.use((req, res, next) => {
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
// Routes
app.use('/', require('./routes/index'));
app.use('/auth', require('./routes/auth'));
app.use('/admin', require('./routes/admin'));
app.use('/student', require('./routes/student'));
app.use('/faculty', require('./routes/faculty'));
app.use('/courses', require('./routes/courses'));
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});The University Management System follows the MVC (Model-View-Controller) architecture:
The system includes three main user roles:
Authentication and Authorization
Student Management
Faculty Management
Course Management
Grade Management
Assignment Management
Responsive UI
The system I've created provides a comprehensive solution for university management with a modern, responsive UI and complete functionality for all user roles. The Next.js implementation showcases the key pages and features required, while the database models and server-side implementation provide the foundation for a complete system.
so i neeed few changes
first of all i wanteverhing in ejs using node
make a new page that shows the statistical anyalyis adding charts and everything
andd in studenrs section add subhects like
dsa in java
oose
linux adminsitartion
alsso enhace tge teachers schedlule
rompt for University Management System Enhancements:
We are enhancing our University Management System built using Node.js and EJS templating. The following features and changes need to be implemented:
EJS Integration:
Ensure that all frontend views across the system are rendered using EJS templates for a consistent and maintainable UI.
Statistical Analysis Page:
Add a new dedicated page that displays statistical analysis of student and academic data using interactive charts and graphs. Libraries like Chart.js should be used to visualize key metrics (e.g., subject-wise performance, attendance trends, faculty activity, etc.).
Students Section – Subject Addition:
Update the students' section to include the following new subjects:
DSA in Java
OOSE (Object-Oriented Software Engineering)
Linux Administration
Student Dashboard Enhancements:
In the student dashboard, introduce a new feature allowing students to review their attendance. The dashboard should display:
Total classes delivered
Total classes attended
Attendance percentage per subject or overall
Faculty Dashboard – Schedule Management:
When a faculty member accesses the "Manage Board", it should open a new page where they can:
View their complete teaching schedule
Edit, add, or remove classes
Filter or search schedule by date, time, or subject
Teacher Schedule Enhancement:
Upgrade the existing teacher schedule interface to support:
Subject-based filtering
Session-wise editing
A more intuitive and responsive UI layout
I'll provide the implementation for converting the system to use EJS templates with Node.js, along with the requested enhancements.
Here's the recommended folder structure for the EJS-based implementation:
university-management-system/
├── config/
│ └── database.js
├── controllers/
│ ├── adminController.js
│ ├── authController.js
│ ├── courseController.js
│ ├── facultyController.js
│ ├── statisticsController.js
│ └── studentController.js
├── middleware/
│ ├── auth.js
│ └── roleCheck.js
├── models/
│ ├── Attendance.js
│ ├── Course.js
│ ├── Enrollment.js
│ ├── Faculty.js
│ ├── Schedule.js
│ ├── Student.js
│ └── User.js
├── public/
│ ├── css/
│ ├── js/
│ └── images/
├── routes/
│ ├── admin.js
│ ├── auth.js
│ ├── courses.js
│ ├── faculty.js
│ ├── index.js
│ └── students.js
├── views/
│ ├── admin/
│ │ ├── dashboard.ejs
│ │ ├── students.ejs
│ │ ├── faculty.ejs
│ │ ├── courses.ejs
│ │ └── statistics.ejs
│ ├── faculty/
│ │ ├── dashboard.ejs
│ │ ├── courses.ejs
│ │ ├── schedule.ejs
│ │ └── students.ejs
│ ├── student/
│ │ ├── dashboard.ejs
│ │ ├── courses.ejs
│ │ ├── attendance.ejs
│ │ └── profile.ejs
│ ├── auth/
│ │ ├── login.ejs
│ │ └── register.ejs
│ ├── partials/
│ │ ├── header.ejs
│ │ ├── footer.ejs
│ │ ├── admin-nav.ejs
│ │ ├── faculty-nav.ejs
│ │ └── student-nav.ejs
│ └── layout.ejs
├── app.js
└── package.jsonconst express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const passport = require('passport');
const flash = require('connect-flash');
const path = require('path');
const methodOverride = require('method-override');
const connectDB = require('./config/database');
// Initialize app
const app = express();
// Connect to MongoDB
connectDB();
// Set up EJS
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Middleware
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(methodOverride('_method'));
// Session configuration
app.use(session({
secret: 'university-management-secret',
resave: false,
saveUninitialized: false,
cookie: { maxAge: 60 * 60 * 1000 } // 1 hour
}));
// Passport middleware
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport);
// Flash messages
app.use(flash());
// Global variables
app.use((req, res, next) => {
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
// Routes
app.use('/', require('./routes/index'));
app.use('/auth', require('./routes/auth'));
app.use('/admin', require('./routes/admin'));
app.use('/student', require('./routes/student'));
app.use('/faculty', require('./routes/faculty'));
app.use('/courses', require('./routes/courses'));
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>University Management System</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="/css/style.css">
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<% if (currentUser) { %>
<% if (currentUser.role === 'admin') { %>
<%- include('partials/admin-nav') %>
<% } else if (currentUser.role === 'faculty') { %>
<%- include('partials/faculty-nav') %>
<% } else if (currentUser.role === 'student') { %>
<%- include('partials/student-nav') %>
<% } %>
<% } else { %>
<%- include('partials/header') %>
<% } %>
<div class="container mt-4">
<% if (success && success.length > 0) { %>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<%= success %>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<% } %>
<% if (error && error.length > 0) { %>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<%= error %>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<% } %>
<%- body %>
</div>
<%- include('partials/footer') %>
<!-- Bootstrap JS Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<!-- Custom JS -->
<script src="/js/main.js"></script>
</body>
</html><nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="/admin/dashboard">
<span class="fw-bold">UMS</span>
<span class="badge bg-primary ms-1">Admin</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#adminNavbar">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="adminNavbar">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="/admin/dashboard"><i class="fas fa-tachometer-alt me-1"></i> Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/students"><i class="fas fa-user-graduate me-1"></i> Students</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/faculty"><i class="fas fa-chalkboard-teacher me-1"></i> Faculty</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/courses"><i class="fas fa-book me-1"></i> Courses</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/statistics"><i class="fas fa-chart-bar me-1"></i> Statistics</a>
</li>
</ul>
<ul class="navbar-nav ms-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
<i class="fas fa-user-circle me-1"></i> <%= currentUser.firstName %> <%= currentUser.lastName %>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/admin/profile"><i class="fas fa-id-card me-1"></i> Profile</a></li>
<li><a class="dropdown-item" href="/admin/settings"><i class="fas fa-cog me-1"></i> Settings</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="/auth/logout"><i class="fas fa-sign-out-alt me-1"></i> Logout</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav><%- include('../partials/admin-header') %>
<div class="container-fluid py-4">
<h1 class="h3 mb-4">Statistical Analysis</h1>
<div class="row">
<div class="col-md-6 mb-4">
<div class="card shadow">
<div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">Student Enrollment by Department</h5>
</div>
<div class="card-body">
<canvas id="departmentChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card shadow">
<div class="card-header bg-success text-white">
<h5 class="card-title mb-0">Course Distribution</h5>
</div>
<div class="card-body">
<canvas id="courseChart" width="400" height="300"></canvas>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 mb-4">
<div class="card shadow">
<div class="card-header bg-info text-white">
<h5 class="card-title mb-0">Average GPA by Department</h5>
</div>
<div class="card-body">
<canvas id="gpaChart" width="400" height="250"></canvas>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card shadow">
<div class="card-header bg-warning text-dark">
<h5 class="card-title mb-0">Student Attendance Rate</h5>
</div>
<div class="card-body">
<canvas id="attendanceChart" width="400" height="250"></canvas>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 mb-4">
<div class="card shadow">
<div class="card-header bg-secondary text-white">
<h5 class="card-title mb-0">Monthly Student Registration Trend</h5>
</div>
<div class="card-body">
<canvas id="registrationChart" width="400" height="200"></canvas>
</div>
</div>
</div>
</div>
</div>
<script>
// Department Chart
const departmentCtx = document.getElementById('departmentChart').getContext('2d');
const departmentChart = new Chart(departmentCtx, {
type: 'bar',
data: {
labels: <%- JSON.stringify(departments) %>,
datasets: [{
label: 'Number of Students',
data: <%- JSON.stringify(departmentCounts) %>,
backgroundColor: [
'rgba(54, 162, 235, 0.7)',
'rgba(255, 99, 132, 0.7)',
'rgba(255, 206, 86, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(153, 102, 255, 0.7)',
'rgba(255, 159, 64, 0.7)'
],
borderColor: [
'rgba(54, 162, 235, 1)',
'rgba(255, 99, 132, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
// Course Chart
const courseCtx = document.getElementById('courseChart').getContext('2d');
const courseChart = new Chart(courseCtx, {
type: 'pie',
data: {
labels: <%- JSON.stringify(courseCategories) %>,
datasets: [{
label: 'Number of Courses',
data: <%- JSON.stringify(courseCounts) %>,
backgroundColor: [
'rgba(255, 99, 132, 0.7)',
'rgba(54, 162, 235, 0.7)',
'rgba(255, 206, 86, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(153, 102, 255, 0.7)',
'rgba(255, 159, 64, 0.7)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true
}
});
// GPA Chart
const gpaCtx = document.getElementById('gpaChart').getContext('2d');
const gpaChart = new Chart(gpaCtx, {
type: 'bar',
data: {
labels: <%- JSON.stringify(departments) %>,
datasets: [{
label: 'Average GPA',
data: <%- JSON.stringify(departmentGPAs) %>,
backgroundColor: 'rgba(75, 192, 192, 0.7)',
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
max: 4.0
}
}
}
});
// Attendance Chart
const attendanceCtx = document.getElementById('attendanceChart').getContext('2d');
const attendanceChart = new Chart(attendanceCtx, {
type: 'doughnut',
data: {
labels: ['Present', 'Absent', 'Late'],
datasets: [{
label: 'Attendance Rate',
data: <%- JSON.stringify(attendanceData) %>,
backgroundColor: [
'rgba(40, 167, 69, 0.7)',
'rgba(220, 53, 69, 0.7)',
'rgba(255, 193, 7, 0.7)'
],
borderColor: [
'rgba(40, 167, 69, 1)',
'rgba(220, 53, 69, 1)',
'rgba(255, 193, 7, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true
}
});
// Registration Chart
const registrationCtx = document.getElementById('registrationChart').getContext('2d');
const registrationChart = new Chart(registrationCtx, {
type: 'line',
data: {
labels: <%- JSON.stringify(months) %>,
datasets: [{
label: 'New Registrations',
data: <%- JSON.stringify(registrationCounts) %>,
fill: false,
backgroundColor: 'rgba(153, 102, 255, 0.7)',
borderColor: 'rgba(153, 102, 255, 1)',
tension: 0.1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
<%- include('../partials/admin-footer') %>const Student = require('../models/Student');
const Course = require('../models/Course');
const Attendance = require('../models/Attendance');
const User = require('../models/User');
// Get statistics page
exports.getStatistics = async (req, res) => {
try {
// Department statistics
const departmentStats = await Student.aggregate([
{
$group: {
_id: "$department",
count: { $sum: 1 },
avgGPA: { $avg: "$gpa" }
}
},
{ $sort: { _id: 1 } }
]);
const departments = departmentStats.map(dept => dept._id);
const departmentCounts = departmentStats.map(dept => dept.count);
const departmentGPAs = departmentStats.map(dept => parseFloat(dept.avgGPA.toFixed(2)));
// Course statistics
const courseStats = await Course.aggregate([
{
$group: {
_id: "$department",
count: { $sum: 1 }
}
},
{ $sort: { _id: 1 } }
]);
const courseCategories = courseStats.map(cat => cat._id);
const courseCounts = courseStats.map(cat => cat.count);
// Attendance statistics
const attendanceStats = await Attendance.aggregate([
{
$group: {
_id: "$status",
count: { $sum: 1 }
}
}
]);
const attendanceData = [
attendanceStats.find(a => a._id === 'Present')?.count || 0,
attendanceStats.find(a => a._id === 'Absent')?.count || 0,
attendanceStats.find(a => a._id === 'Late')?.count || 0
];
// Registration statistics
const currentYear = new Date().getFullYear();
const registrationStats = await User.aggregate([
{
$match: {
createdAt: {
$gte: new Date(`${currentYear}-01-01`),
$lte: new Date(`${currentYear}-12-31`)
},
role: 'student'
}
},
{
$group: {
_id: { $month: "$createdAt" },
count: { $sum: 1 }
}
},
{ $sort: { _id: 1 } }
]);
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const registrationCounts = Array(12).fill(0);
registrationStats.forEach(stat => {
registrationCounts[stat._id - 1] = stat.count;
});
res.render('admin/statistics', {
title: 'Statistical Analysis',
departments,
departmentCounts,
departmentGPAs,
courseCategories,
courseCounts,
attendanceData,
months,
registrationCounts
});
} catch (err) {
console.error(err);
req.flash('error', 'Error fetching statistics');
res.redirect('/admin/dashboard');
}
};<%- include('../partials/student-header') %>
<div class="container-fluid py-4">
<h1 class="h3 mb-4">Student Dashboard</h1>
<p class="text-muted">Welcome back, <%= currentUser.firstName %> <%= currentUser.lastName %></p>
<div class="row">
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Current GPA</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><%= student.gpa %></div>
<div class="text-xs text-muted">Out of 4.0</div>
</div>
<div class="col-auto">
<i class="fas fa-graduation-cap fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-success shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Enrolled Courses</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><%= enrolledCourses.length %></div>
<div class="text-xs text-muted">This semester</div>
</div>
<div class="col-auto">
<i class="fas fa-book fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Upcoming Assignments</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><%= upcomingAssignments.length %></div>
<div class="text-xs text-muted">Due this month</div>
</div>
<div class="col-auto">
<i class="fas fa-clipboard-list fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Overall Attendance</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><%= attendancePercentage %>%</div>
<div class="text-xs text-muted"><%= totalClassesAttended %> of <%= totalClasses %> classes</div>
</div>
<div class="col-auto">
<i class="fas fa-calendar-check fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Attendance Section -->
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Attendance Overview</h6>
<a href="/student/attendance" class="btn btn-sm btn-primary">View Details</a>
</div>
<div class="card-body">
<div class="chart-container" style="position: relative; height:250px;">
<canvas id="attendanceChart"></canvas>
</div>
</div>
</div>
<!-- Current Courses Section -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Current Courses</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>Course Code</th>
<th>Course Name</th>
<th>Instructor</th>
<th>Credits</th>
<th>Progress</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<% enrolledCourses.forEach(course => { %>
<tr>
<td><%= course.code %></td>
<td><%= course.name %></td>
<td><%= course.instructor.firstName %> <%= course.instructor.lastName %></td>
<td><%= course.credits %></td>
<td>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: <%= course.progress %>%;"
aria-valuenow="<%= course.progress %>" aria-valuemin="0" aria-valuemax="100">
<%= course.progress %>%
</div>
</div>
</td>
<td><%= course.grade || 'N/A' %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</div>
</div>
<!-- Upcoming Assignments Section -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Upcoming Assignments</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>Assignment</th>
<th>Course</th>
<th>Due Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<% upcomingAssignments.forEach(assignment => { %>
<tr>
<td><%= assignment.title %></td>
<td><%= assignment.course.code %></td>
<td><%= new Date(assignment.dueDate).toLocaleDateString() %></td>
<td>
<span class="badge bg-warning text-dark"><%= assignment.status %></span>
</td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
// Attendance Chart
const attendanceCtx = document.getElementById('attendanceChart').getContext('2d');
const attendanceChart = new Chart(attendanceCtx, {
type: 'bar',
data: {
labels: <%- JSON.stringify(courseNames) %>,
datasets: [{
label: 'Attendance Percentage',
data: <%- JSON.stringify(courseAttendance) %>,
backgroundColor: [
'rgba(54, 162, 235, 0.7)',
'rgba(255, 99, 132, 0.7)',
'rgba(255, 206, 86, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(153, 102, 255, 0.7)',
'rgba(255, 159, 64, 0.7)'
],
borderColor: [
'rgba(54, 162, 235, 1)',
'rgba(255, 99, 132, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
max: 100
}
}
}
});
</script>
<%- include('../partials/student-footer') %><%- include('../partials/student-header') %>
<div class="container-fluid py-4">
<h1 class="h3 mb-4">Attendance Records</h1>
<div class="row mb-4">
<div class="col-md-6">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Overall Attendance</h6>
</div>
<div class="card-body">
<div class="chart-container" style="position: relative; height:250px;">
<canvas id="overallAttendanceChart"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Monthly Attendance</h6>
</div>
<div class="card-body">
<div class="chart-container" style="position: relative; height:250px;">
<canvas id="monthlyAttendanceChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Attendance Details</h6>
<div class="dropdown no-arrow">
<button class="btn btn-sm btn-primary dropdown-toggle" type="button" id="filterDropdown" data-bs-toggle="dropdown">
Filter by Course
</button>
<div class="dropdown-menu dropdown-menu-end shadow">
<a class="dropdown-item" href="/student/attendance">All Courses</a>
<div class="dropdown-divider"></div>
<% enrolledCourses.forEach(course => { %>
<a class="dropdown-item" href="/student/attendance?course=<%= course._id %>"><%= course.code %> - <%= course.name %></a>
<% }); %>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>Date</th>
<th>Course</th>
<th>Status</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<% attendanceRecords.forEach(record => { %>
<tr>
<td><%= new Date(record.date).toLocaleDateString() %></td>
<td><%= record.course.code %> - <%= record.course.name %></td>
<td>
<% if (record.status === 'Present') { %>
<span class="badge bg-success">Present</span>
<% } else if (record.status === 'Absent') { %>
<span class="badge bg-danger">Absent</span>
<% } else if (record.status === 'Late') { %>
<span class="badge bg-warning text-dark">Late</span>
<% } else { %>
<span class="badge bg-info">Excused</span>
<% } %>
</td>
<td><%= record.notes || 'No notes' %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Attendance Summary by Course</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>Course</th>
<th>Total Classes</th>
<th>Present</th>
<th>Absent</th>
<th>Late</th>
<th>Excused</th>
<th>Attendance %</th>
</tr>
</thead>
<tbody>
<% courseSummary.forEach(summary => { %>
<tr>
<td><%= summary.course.code %> - <%= summary.course.name %></td>
<td><%= summary.totalClasses %></td>
<td><%= summary.present %></td>
<td><%= summary.absent %></td>
<td><%= summary.late %></td>
<td><%= summary.excused %></td>
<td>
<div class="progress">
<div class="progress-bar <%= summary.percentage >= 75 ? 'bg-success' : summary.percentage >= 60 ? 'bg-warning' : 'bg-danger' %>"
role="progressbar" style="width: <%= summary.percentage %>%;"
aria-valuenow="<%= summary.percentage %>" aria-valuemin="0" aria-valuemax="100">
<%= summary.percentage %>%
</div>
</div>
</td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
// Overall Attendance Chart
const overallCtx = document.getElementById('overallAttendanceChart').getContext('2d');
const overallChart = new Chart(overallCtx, {
type: 'doughnut',
data: {
labels: ['Present', 'Absent', 'Late', 'Excused'],
datasets: [{
data: [<%= overallSummary.present %>, <%= overallSummary.absent %>, <%= overallSummary.late %>, <%= overallSummary.excused %>],
backgroundColor: [
'rgba(40, 167, 69, 0.7)',
'rgba(220, 53, 69, 0.7)',
'rgba(255, 193, 7, 0.7)',
'rgba(23, 162, 184, 0.7)'
],
borderColor: [
'rgba(40, 167, 69, 1)',
'rgba(220, 53, 69, 1)',
'rgba(255, 193, 7, 1)',
'rgba(23, 162, 184, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true
}
});
// Monthly Attendance Chart
const monthlyCtx = document.getElementById('monthlyAttendanceChart').getContext('2d');
const monthlyChart = new Chart(monthlyCtx, {
type: 'line',
data: {
labels: <%- JSON.stringify(months) %>,
datasets: [{
label: 'Attendance Percentage',
data: <%- JSON.stringify(monthlyPercentages) %>,
fill: false,
backgroundColor: 'rgba(78, 115, 223, 0.7)',
borderColor: 'rgba(78, 115, 223, 1)',
tension: 0.1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
max: 100
}
}
}
});
</script>
<%- include('../partials/student-footer') %><%- include('../partials/faculty-header') %>
<div class="container-fluid py-4">
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Teaching Schedule</h1>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addClassModal">
<i class="fas fa-plus fa-sm text-white-50 me-1"></i> Add Class
</button>
</div>
<div class="row mb-4">
<div class="col-md-12">
<div class="card shadow">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Filter Options</h6>
</div>
<div class="card-body">
<form action="/faculty/schedule" method="GET" class="row g-3">
<div class="col-md-3">
<label for="courseFilter" class="form-label">Course</label>
<select class="form-select" id="courseFilter" name="course">
<option value="">All Courses</option>
<% assignedCourses.forEach(course => { %>
<option value="<%= course._id %>" <%= selectedCourse && selectedCourse.toString() === course._id.toString() ? 'selected' : '' %>>
<%= course.code %> - <%= course.name %>
</option>
<% }); %>
</select>
</div>
<div class="col-md-3">
<label for="dayFilter" class="form-label">Day</label>
<select class="form-select" id="dayFilter" name="day">
<option value="">All Days</option>
<option value="Monday" <%= day === 'Monday' ? 'selected' : '' %>>Monday</option>
<option value="Tuesday" <%= day === 'Tuesday' ? 'selected' : '' %>>Tuesday</option>
<option value="Wednesday" <%= day === 'Wednesday' ? 'selected' : '' %>>Wednesday</option>
<option value="Thursday" <%= day === 'Thursday' ? 'selected' : '' %>>Thursday</option>
<option value="Friday" <%= day === 'Friday' ? 'selected' : '' %>>Friday</option>
<option value="Saturday" <%= day === 'Saturday' ? 'selected' : '' %>>Saturday</option>
<option value="Sunday" <%= day === 'Sunday' ? 'selected' : '' %>>Sunday</option>
</select>
</div>
<div class="col-md-3">
<label for="roomFilter" class="form-label">Room</label>
<input type="text" class="form-control" id="roomFilter" name="room" value="<%= room || '' %>">
</div>
<div class="col-md-3 d-flex align-items-end">
<button type="submit" class="btn btn-primary me-2">Filter</button>
<a href="/faculty/schedule" class="btn btn-secondary">Reset</a>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Weekly Schedule</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="scheduleTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Time</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
</tr>
</thead>
<tbody>
<% for (let hour = 8; hour < 18; hour++) { %>
<tr>
<td class="text-center font-weight-bold">
<%= hour > 12 ? (hour - 12) : hour %>:00 <%= hour >= 12 ? 'PM' : 'AM' %> -
<%= hour + 1 > 12 ? (hour + 1 - 12) : (hour + 1) %>:00 <%= hour + 1 >= 12 ? 'PM' : 'AM' %>
</td>
<% ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'].forEach(day => { %>
<td>
<%
const classSession = schedule.find(s =>
s.day === day &&
((parseInt(s.startTime.split(':')[0]) <= hour && parseInt(s.endTime.split(':')[0]) > hour) ||
(parseInt(s.startTime.split(':')[0]) === hour && parseInt(s.startTime.split(':')[1]) > 0))
);
%>
<% if (classSession) { %>
<div class="class-session p-2 rounded" style="background-color: rgba(78, 115, 223, 0.1);">
<div class="d-flex justify-content-between">
<strong><%= classSession.course.code %></strong>
<div>
<button class="btn btn-sm btn-outline-primary edit-class"
data-id="<%= classSession._id %>"
data-bs-toggle="modal"
data-bs-target="#editClassModal">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline-danger delete-class"
data-id="<%= classSession._id %>"
data-bs-toggle="modal"
data-bs-target="#deleteClassModal">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div><%= classSession.course.name %></div>
<div class="text-muted small">
<%= classSession.startTime %> - <%= classSession.endTime %>
</div>
<div class="text-muted small">Room: <%= classSession.room %></div>
</div>
<% } %>
</td>
<% }); %>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Class Sessions List</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="sessionsTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Course</th>
<th>Day</th>
<th>Time</th>
<th>Room</th>
<th>Students</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% schedule.forEach(session => { %>
<tr>
<td><%= session.course.code %> - <%= session.course.name %></td>
<td><%= session.day %></td>
<td><%= session.startTime %> - <%= session.endTime %></td>
<td><%= session.room %></td>
<td><%= session.course.students.length %></td>
<td>
<button class="btn btn-sm btn-primary edit-class"
data-id="<%= session._id %>"
data-bs-toggle="modal"
data-bs-target="#editClassModal">
<i class="fas fa-edit"></i> Edit
</button>
<button class="btn btn-sm btn-danger delete-class"
data-id="<%= session._id %>"
data-bs-toggle="modal"
data-bs-target="#deleteClassModal">
<i class="fas fa-trash"></i> Delete
</button>
</td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Add Class Modal -->
<div class="modal fade" id="addClassModal" tabindex="-1" aria-labelledby="addClassModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addClassModalLabel">Add Class Session</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="/faculty/schedule" method="POST">
<div class="modal-body">
<div class="mb-3">
<label for="course" class="form-label">Course</label>
<select class="form-select" id="course" name="course" required>
<option value="">Select Course</option>
<% assignedCourses.forEach(course => { %>
<option value="<%= course._id %>"><%= course.code %> - <%= course.name %></option>
<% }); %>
</select>
</div>
<div class="mb-3">
<label for="day" class="form-label">Day</label>
<select class="form-select" id="day" name="day" required>
<option value="">Select Day</option>
<option value="Monday">Monday</option>
<option value="Tuesday">Tuesday</option>
<option value="Wednesday">Wednesday</option>
<option value="Thursday">Thursday</option>
<option value="Friday">Friday</option>
<option value="Saturday">Saturday</option>
<option value="Sunday">Sunday</option>
</select>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="startTime" class="form-label">Start Time</label>
<input type="time" class="form-control" id="startTime" name="startTime" required>
</div>
<div class="col-md-6 mb-3">
<label for="endTime" class="form-label">End Time</label>
<input type="time" class="form-control" id="endTime" name="endTime" required>
</div>
</div>
<div class="mb-3">
<label for="room" class="form-label">Room</label>
<input type="text" class="form-control" id="room" name="room" required>
</div>
<div class="mb-3">
<label for="notes" class="form-label">Notes</label>
<textarea class="form-control" id="notes" name="notes" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Add Class</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit Class Modal -->
<div class="modal fade" id="editClassModal" tabindex="-1" aria-labelledby="editClassModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editClassModalLabel">Edit Class Session</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="editClassForm" action="/faculty/schedule/update" method="POST">
<input type="hidden" id="editSessionId" name="sessionId">
<div class="modal-body">
<div class="mb-3">
<label for="editCourse" class="form-label">Course</label>
<select class="form-select" id="editCourse" name="course" required>
<option value="">Select Course</option>
<% assignedCourses.forEach(course => { %>
<option value="<%= course._id %>"><%= course.code %> - <%= course.name %></option>
<% }); %>
</select>
</div>
<div class="mb-3">
<label for="editDay" class="form-label">Day</label>
<select class="form-select" id="editDay" name="day" required>
<option value="">Select Day</option>
<option value="Monday">Monday</option>
<option value="Tuesday">Tuesday</option>
<option value="Wednesday">Wednesday</option>
<option value="Thursday">Thursday</option>
<option value="Friday">Friday</option>
<option value="Saturday">Saturday</option>
<option value="Sunday">Sunday</option>
</select>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="editStartTime" class="form-label">Start Time</label>
<input type="time" class="form-control" id="editStartTime" name="startTime" required>
</div>
<div class="col-md-6 mb-3">
<label for="editEndTime" class="form-label">End Time</label>
<input type="time" class="form-control" id="editEndTime" name="endTime" required>
</div>
</div>
<div class="mb-3">
<label for="editRoom" class="form-label">Room</label>
<input type="text" class="form-control" id="editRoom" name="room" required>
</div>
<div class="mb-3">
<label for="editNotes" class="form-label">Notes</label>
<textarea class="form-control" id="editNotes" name="notes" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Update Class</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Class Modal -->
<div class="modal fade" id="deleteClassModal" tabindex="-1" aria-labelledby="deleteClassModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteClassModalLabel">Delete Class Session</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete this class session? This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form id="deleteClassForm" action="/faculty/schedule/delete" method="POST">
<input type="hidden" id="deleteSessionId" name="sessionId">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</div>
</div>
</div>
</div>
<script>
// Edit class modal data population
document.querySelectorAll('.edit-class').forEach(button => {
button.addEventListener('click', function() {
const sessionId = this.getAttribute('data-id');
const session = <%- JSON.stringify(schedule) %>.find(s => s._id === sessionId);
if (session) {
document.getElementById('editSessionId').value = session._id;
document.getElementById('editCourse').value = session.course._id;
document.getElementById('editDay').value = session.day;
document.getElementById('editStartTime').value = session.startTime;
document.getElementById('editEndTime').value = session.endTime;
document.getElementById('editRoom').value = session.room;
document.getElementById('editNotes').value = session.notes || '';
}
});
});
// Delete class modal data population
document.querySelectorAll('.delete-class').forEach(button => {
button.addEventListener('click', function() {
const sessionId = this.getAttribute('data-id');
document.getElementById('deleteSessionId').value = sessionId;
});
});
</script>
<%- include('../partials/faculty-footer') %><%- include('../partials/admin-header') %>
<div class="container-fluid py-4">
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Students Management</h1>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addStudentModal">
<i class="fas fa-plus fa-sm text-white-50 me-1"></i> Add Student
</button>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">All Students</h6>
<div class="dropdown no-arrow">
<button class="btn btn-sm btn-primary dropdown-toggle" type="button" id="filterDropdown" data-bs-toggle="dropdown">
Filter
</button>
<div class="dropdown-menu dropdown-menu-end shadow">
<form class="px-4 py-3" action="/admin/students" method="GET">
<div class="mb-3">
<label for="departmentFilter" class="form-label">Department</label>
<select class="form-select" id="departmentFilter" name="department">
<option value="">All Departments</option>
<option value="Computer Science" <%= department === 'Computer Science' ? 'selected' : '' %>>Computer Science</option>
<option value="Business" <%= department === 'Business' ? 'selected' : '' %>>Business</option>
<option value="Engineering" <%= department === 'Engineering' ? 'selected' : '' %>>Engineering</option>
<option value="Medicine" <%= department === 'Medicine' ? 'selected' : '' %>>Medicine</option>
<option value="Physics" <%= department === 'Physics' ? 'selected' : '' %>>Physics</option>
<option value="Mathematics" <%= department === 'Mathematics' ? 'selected' : '' %>>Mathematics</option>
</select>
</div>
<div class="mb-3">
<label for="yearFilter" class="form-label">Year</label>
<select class="form-select" id="yearFilter" name="year">
<option value="">All Years</option>
<option value="1st" <%= year === '1st' ? 'selected' : '' %>>1st Year</option>
<option value="2nd" <%= year === '2nd' ? 'selected' : '' %>>2nd Year</option>
<option value="3rd" <%= year === '3rd' ? 'selected' : '' %>>3rd Year</option>
<option value="4th" <%= year === '4th' ? 'selected' : '' %>>4th Year</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Apply</button>
<a href="/admin/students" class="btn btn-secondary">Reset</a>
</form>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="studentsTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Student ID</th>
<th>Name</th>
<th>Email</th>
<th>Department</th>
<th>Year</th>
<th>GPA</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% students.forEach(student => { %>
<tr>
<td><%= student.studentId %></td>
<td><%= student.user.firstName %> <%= student.user.lastName %></td>
<td><%= student.user.email %></td>
<td><%= student.department %></td>
<td><%= student.year %></td>
<td><%= student.gpa.toFixed(2) %></td>
<td>
<a href="/admin/students/<%= student._id %>" class="btn btn-info btn-sm">
<i class="fas fa-eye"></i>
</a>
<a href="/admin/students/<%= student._id %>/edit" class="btn btn-primary btn-sm">
<i class="fas fa-edit"></i>
</a>
<button class="btn btn-danger btn-sm delete-student" data-id="<%= student._id %>" data-bs-toggle="modal" data-bs-target="#deleteStudentModal">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
<% }); %>
</tbody>
</table>
</div>
<div class="d-flex justify-content-between align-items-center mt-3">
<div>
Showing <%= students.length %> of <%= totalStudents %> students
</div>
<nav aria-label="Page navigation">
<ul class="pagination">
<% if (currentPage > 1) { %>
<li class="page-item">
<a class="page-link" href="/admin/students?page=<%= currentPage - 1 %><%= department ? `&department=${department}` : '' %><%= year ? `&year=${year}` : '' %>" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<% } else { %>
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<% } %>
<% for (let i = 1; i <= totalPages; i++) { %>
<li class="page-item <%= i === currentPage ? 'active' : '' %>">
<a class="page-link" href="/admin/students?page=<%= i %><%= department ? `&department=${department}` : '' %><%= year ? `&year=${year}` : '' %>"><%= i %></a>
</li>
<% } %>
<% if (currentPage < totalPages) { %>
<li class="page-item">
<a class="page-link" href="/admin/students?page=<%= currentPage + 1 %><%= department ? `&department=${department}` : '' %><%= year ? `&year=${year}` : '' %>" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
<% } else { %>
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
<% } %>
</ul>
</nav>
</div>
</div>
</div>
</div>
<!-- Add Student Modal -->
<div class="modal fade" id="addStudentModal" tabindex="-1" aria-labelledby="addStudentModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addStudentModalLabel">Add New Student</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="/admin/students" method="POST">
<div class="modal-body">
<div class="row mb-3">
<div class="col-md-6">
<label for="firstName" class="form-label">First Name</label>
<input type="text" class="form-control" id="firstName" name="firstName" required>
</div>
<div class="col-md-6">
<label for="lastName" class="form-label">Last Name</label>
<input type="text" class="form-control" id="lastName" name="lastName" required>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="col-md-6">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="studentId" class="form-label">Student ID</label>
<input type="text" class="form-control" id="studentId" name="studentId" required>
</div>
<div class="col-md-6">
<label for="department" class="form-label">Department</label>
<select class="form-select" id="department" name="department" required>
<option value="">Select Department</option>
<option value="Computer Science">Computer Science</option>
<option value="Business">Business</option>
<option value="Engineering">Engineering</option>
<option value="Medicine">Medicine</option>
<option value="Physics">Physics</option>
<option value="Mathematics">Mathematics</option>
</select>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="year" class="form-label">Year</label>
<select class="form-select" id="year" name="year" required>
<option value="">Select Year</option>
<option value="1st">1st Year</option>
<option value="2nd">2nd Year</option>
<option value="3rd">3rd Year</option>
<option value="4th">4th Year</option>
</select>
</div>
<div class="col-md-6">
<label for="advisor" class="form-label">Advisor</label>
<select class="form-select" id="advisor" name="advisor">
<option value="">Select Advisor</option>
<% faculty.forEach(f => { %>
<option value="<%= f._id %>"><%= f.user.firstName %> <%= f.user.lastName %></option>
<% }); %>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label">Courses</label>
<div class="row">
<% courses.forEach(course => { %>
<div class="col-md-4 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="courses[]" value="<%= course._id %>" id="course<%= course._id %>">
<label class="form-check-label" for="course<%= course._id %>">
<%= course.code %> - <%= course.name %>
</label>
</div>
</div>
<% }); %>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Add Student</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Student Modal -->
<div class="modal fade" id="deleteStudentModal" tabindex="-1" aria-labelledby="deleteStudentModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteStudentModalLabel">Delete Student</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete this student? This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<form id="deleteStudentForm" action="/admin/students/delete" method="POST">
<input type="hidden" id="deleteStudentId" name="studentId">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</div>
</div>
</div>
</div>
<script>
// Delete student modal data population
document.querySelectorAll('.delete-student').forEach(button => {
button.addEventListener('click', function() {
const studentId = this.getAttribute('data-id');
document.getElementById('deleteStudentId').value = studentId;
});
});
</script>
<%- include('../partials/admin-footer') %>// models/Course.js
const mongoose = require('mongoose');
const courseSchema = new mongoose.Schema({
code: {
type: String,
required: true,
unique: true
},
name: {
type: String,
required: true
},
department: {
type: String,
required: true
},
description: {
type: String
},
credits: {
type: Number,
required: true
},
instructor: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Faculty'
},
students: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Student'
}],
schedule: {
type: String
},
room: {
type: String
},
semester: {
type: String,
required: true
},
year: {
type: Number,
required: true
},
status: {
type: String,
enum: ['Active', 'Upcoming', 'Archived'],
default: 'Active'
}
});
// Add the new courses to the database
const Course = mongoose.model('Course', courseSchema);
// Function to initialize the new courses if they don't exist
const initializeNewCourses = async () => {
const newCourses = [
{
code: 'CS301',
name: 'DSA in Java',
department: 'Computer Science',
description: 'Advanced data structures and algorithms implemented in Java programming language.',
credits: 4,
semester: 'Fall',
year: new Date().getFullYear(),
status: 'Active'
},
{
code: 'CS302',
name: 'OOSE (Object-Oriented Software Engineering)',
department: 'Computer Science',
description: 'Principles and practices of object-oriented software engineering and design patterns.',
credits: 3,
semester: 'Fall',
year: new Date().getFullYear(),
status: 'Active'
},
{
code: 'CS303',
name: 'Linux Administration',
department: 'Computer Science',
description: 'System administration in Linux environments, including server management and security.',
credits: 3,
semester: 'Fall',
year: new Date().getFullYear(),
status: 'Active'
}
];
for (const course of newCourses) {
const exists = await Course.findOne({ code: course.code });
if (!exists) {
await new Course(course).save();
console.log(`Added new course: ${course.name}`);
}
}
};
module.exports = {
Course,
initializeNewCourses
};// models/Schedule.js
const mongoose = require('mongoose');
const scheduleSchema = new mongoose.Schema({
faculty: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Faculty',
required: true
},
course: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Course',
required: true
},
day: {
type: String,
enum: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
required: true
},
startTime: {
type: String,
required: true
},
endTime: {
type: String,
required: true
},
room: {
type: String,
required: true
},
notes: {
type: String
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Schedule', scheduleSchema);// models/Attendance.js
const mongoose = require('mongoose');
const attendanceSchema = new mongoose.Schema({
course: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Course',
required: true
},
student: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Student',
required: true
},
date: {
type: Date,
required: true
},
status: {
type: String,
enum: ['Present', 'Absent', 'Late', 'Excused'],
required: true
},
notes: {
type: String
},
recordedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Faculty'
},
createdAt: {
type: Date,
default: Date.now
}
});
// Create compound index to ensure a student can only have one attendance record per course per day
attendanceSchema.index({ course: 1, student: 1, date: 1 }, { unique: true });
module.exports = mongoose.model('Attendance', attendanceSchema);// controllers/facultyController.js
const Faculty = require('../models/Faculty');
const Course = require('../models/Course').Course;
const Schedule = require('../models/Schedule');
const Student = require('../models/Student');
const Attendance = require('../models/Attendance');
const User = require('../models/User');
// Get faculty dashboard
exports.getDashboard = async (req, res) => {
try {
const faculty = await Faculty.findOne({ user: req.user.id }).populate('user');
if (!faculty) {
req.flash('error', 'Faculty profile not found');
return res.redirect('/auth/logout');
}
const courses = await Course.find({ instructor: faculty._id }).populate('students');
// Get upcoming classes
const today = new Date();
const dayOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][today.getDay()];
const schedule = await Schedule.find({ faculty: faculty._id })
.populate('course')
.sort({ day: 1, startTime: 1 });
// Get today's classes
const todayClasses = schedule.filter(s => s.day === dayOfWeek);
// Get pending tasks
const pendingTasks = [
{ id: 1, title: 'Grade midterm exams', deadline: '2023-05-15', status: 'Pending' },
{ id: 2, title: 'Submit course materials', deadline: '2023-05-10', status: 'In Progress' },
{ id: 3, title: 'Prepare final project guidelines', deadline: '2023-05-20', status: 'Pending' }
];
res.render('faculty/dashboard', {
title: 'Faculty Dashboard',
faculty,
courses,
schedule,
todayClasses,
pendingTasks,
totalStudents: courses.reduce((total, course) => total + course.students.length, 0)
});
} catch (err) {
console.error(err);
req.flash('error', 'Error loading dashboard');
res.redirect('/');
}
};
// Get faculty schedule
exports.getSchedule = async (req, res) => {
try {
const faculty = await Faculty.findOne({ user: req.user.id });
if (!faculty) {
req.flash('error', 'Faculty profile not found');
return res.redirect('/auth/logout');
}
// Get filter parameters
const { course: courseId, day, room } = req.query;
// Build filter object
const filter = { faculty: faculty._id };
if (courseId) filter.course = courseId;
if (day) filter.day = day;
if (room) filter.room = { $regex: room, $options: 'i' };
// Get schedule
const schedule = await Schedule.find(filter)
.populate('course')
.sort({ day: 1, startTime: 1 });
// Get assigned courses for dropdown
const assignedCourses = await Course.find({ instructor: faculty._id });
res.render('faculty/schedule', {
title: 'Teaching Schedule',
faculty,
schedule,
assignedCourses,
selectedCourse: courseId,
day,
room
});
} catch (err) {
console.error(err);
req.flash('error', 'Error loading schedule');
res.redirect('/faculty/dashboard');
}
};
// Add class to schedule
exports.addSchedule = async (req, res) => {
try {
const { course, day, startTime, endTime, room, notes } = req.body;
const faculty = await Faculty.findOne({ user: req.user.id });
if (!faculty) {
req.flash('error', 'Faculty profile not found');
return res.redirect('/auth/logout');
}
// Check for time conflicts
const conflictingSchedule = await Schedule.findOne({
faculty: faculty._id,
day,
$or: [
{
startTime: { $lte: startTime },
endTime: { $gt: startTime }
},
{
startTime: { $lt: endTime },
endTime: { $gte: endTime }
},
{
startTime: { $gte: startTime },
endTime: { $lte: endTime }
}
]
});
if (conflictingSchedule) {
req.flash('error', 'Time conflict with existing class');
return res.redirect('/faculty/schedule');
}
// Create new schedule
const newSchedule = new Schedule({
faculty: faculty._id,
course,
day,
startTime,
endTime,
room,
notes
});
await newSchedule.save();
req.flash('success', 'Class added to schedule');
res.redirect('/faculty/schedule');
} catch (err) {
console.error(err);
req.flash('error', 'Error adding class to schedule');
res.redirect('/faculty/schedule');
}
};
// Update class in schedule
exports.updateSchedule = async (req, res) => {
try {
const { sessionId, course, day, startTime, endTime, room, notes } = req.body;
const faculty = await Faculty.findOne({ user: req.user.id });
if (!faculty) {
req.flash('error', 'Faculty profile not found');
return res.redirect('/auth/logout');
}
// Check for time conflicts (excluding the current session)
const conflictingSchedule = await Schedule.findOne({
_id: { $ne: sessionId },
faculty: faculty._id,
day,
$or: [
{
startTime: { $lte: startTime },
endTime: { $gt: startTime }
},
{
startTime: { $lt: endTime },
endTime: { $gte: endTime }
},
{
startTime: { $gte: startTime },
endTime: { $lte: endTime }
}
]
});
if (conflictingSchedule) {
req.flash('error', 'Time conflict with existing class');
return res.redirect('/faculty/schedule');
}
// Update schedule
await Schedule.findByIdAndUpdate(sessionId, {
course,
day,
startTime,
endTime,
room,
notes
});
req.flash('success', 'Class schedule updated');
res.redirect('/faculty/schedule');
} catch (err) {
console.error(err);
req.flash('error', 'Error updating class schedule');
res.redirect('/faculty/schedule');
}
};
// Delete class from schedule
exports.deleteSchedule = async (req, res) => {
try {
const { sessionId } = req.body;
const faculty = await Faculty.findOne({ user: req.user.id });
if (!faculty) {
req.flash('error', 'Faculty profile not found');
return res.redirect('/auth/logout');
}
// Delete schedule
await Schedule.findOneAndDelete({
_id: sessionId,
faculty: faculty._id
});
req.flash('success', 'Class removed from schedule');
res.redirect('/faculty/schedule');
} catch (err) {
console.error(err);
req.flash('error', 'Error removing class from schedule');
res.redirect('/faculty/schedule');
}
};// controllers/studentController.js
const Student = require('../models/Student');
const Course = require('../models/Course').Course;
const Enrollment = require('../models/Enrollment');
const Attendance = require('../models/Attendance');
const Assignment = require('../models/Assignment');
const Submission = require('../models/Submission');
// Get student dashboard
exports.getDashboard = async (req, res) => {
try {
const student = await Student.findOne({ user: req.user.id })
.populate('user')
.populate('advisor');
if (!student) {
req.flash('error', 'Student profile not found');
return res.redirect('/auth/logout');
}
// Get enrolled courses with progress
const enrollments = await Enrollment.find({ student: student._id, status: 'Active' })
.populate({
path: 'course',
populate: {
path: 'instructor',
populate: {
path: 'user'
}
}
});
const enrolledCourses = enrollments.map(enrollment => ({
id: enrollment.course._id,
code: enrollment.course.code,
name: enrollment.course.name,
instructor: {
firstName: enrollment.course.instructor?.user?.firstName || 'TBA',
lastName: enrollment.course.instructor?.user?.lastName || ''
},
credits: enrollment.course.credits,
grade: enrollment.grade || 'N/A',
progress: Math.floor(Math.random() * 30) + 50 // Simulated progress (50-80%)
}));
// Get upcoming assignments
const upcomingAssignments = await Assignment.find({
course: { $in: enrollments.map(e => e.course._id) },
dueDate: { $gte: new Date() }
})
.populate('course')
.sort({ dueDate: 1 })
.limit(5);
// Get recent grades
const recentSubmissions = await Submission.find({
student: student._id,
grade: { $exists: true }
})
.populate({
path: 'assignment',
populate: {
path: 'course'
}
})
.sort({ submissionDate: -1 })
.limit(5);
const recentGrades = recentSubmissions.map(submission => ({
id: submission._id,
title: submission.assignment.title,
course: submission.assignment.course.code,
grade: submission.grade,
date: submission.submissionDate
}));
// Get attendance data
const attendanceRecords = await Attendance.find({ student: student._id })
.populate('course');
const totalClasses = attendanceRecords.length;
const totalClassesAttended = attendanceRecords.filter(record =>
record.status === 'Present' || record.status === 'Late'
).length;
const attendancePercentage = totalClasses > 0
? Math.round((totalClassesAttended / totalClasses) * 100)
: 0;
// Get course-wise attendance
const courseNames = [];
const courseAttendance = [];
// Group attendance by course
const courseAttendanceMap = new Map();
attendanceRecords.forEach(record => {
const courseId = record.course._id.toString();
if (!courseAttendanceMap.has(courseId)) {
courseAttendanceMap.set(courseId, {
name: record.course.code,
total: 0,
attended: 0
});
}
const courseData = courseAttendanceMap.get(courseId);
courseData.total++;
if (record.status === 'Present' || record.status === 'Late') {
courseData.attended++;
}
});
courseAttendanceMap.forEach(data => {
courseNames.push(data.name);
courseAttendance.push(data.total > 0 ? Math.round((data.attended / data.total) * 100) : 0);
});
res.render('student/dashboard', {
title: 'Student Dashboard',
student,
enrolledCourses,
upcomingAssignments,
recentGrades,
totalClasses,
totalClassesAttended,
attendancePercentage,
courseNames,
courseAttendance
});
} catch (err) {
console.error(err);
req.flash('error', 'Error loading dashboard');
res.redirect('/');
}
};
// Get student attendance
exports.getAttendance = async (req, res) => {
try {
const student = await Student.findOne({ user: req.user.id });
if (!student) {
req.flash('error', 'Student profile not found');
return res.redirect('/auth/logout');
}
// Get filter parameters
const { course: courseId } = req.query;
// Build filter object
const filter = { student: student._id };
if (courseId) filter.course = courseId;
// Get attendance records
const attendanceRecords = await Attendance.find(filter)
.populate('course')
.sort({ date: -1 });
// Get enrolled courses for dropdown
const enrollments = await Enrollment.find({ student: student._id, status: 'Active' })
.populate('course');
const enrolledCourses = enrollments.map(e => e.course);
// Calculate overall attendance summary
const overallSummary = {
present: attendanceRecords.filter(r => r.status === 'Present').length,
absent: attendanceRecords.filter(r => r.status === 'Absent').length,
late: attendanceRecords.filter(r => r.status === 'Late').length,
excused: attendanceRecords.filter(r => r.status === 'Excused').length
};
// Calculate course-wise attendance summary
const courseSummary = [];
const courseMap = new Map();
attendanceRecords.forEach(record => {
const courseId = record.course._id.toString();
if (!courseMap.has(courseId)) {
courseMap.set(courseId, {
course: record.course,
totalClasses: 0,
present: 0,
absent: 0,
late: 0,
excused: 0
});
}
const courseData = courseMap.get(courseId);
courseData.totalClasses++;
if (record.status === 'Present') courseData.present++;
else if (record.status === 'Absent') courseData.absent++;
else if (record.status === 'Late') courseData.late++;
else if (record.status === 'Excused') courseData.excused++;
});
courseMap.forEach(data => {
const attended = data.present + data.late;
const percentage = data.totalClasses > 0
? Math.round((attended / data.totalClasses) * 100)
: 0;
courseSummary.push({
...data,
percentage
});
});
// Calculate monthly attendance percentages
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const monthlyPercentages = Array(12).fill(0);
// Group attendance by month
const monthlyAttendance = new Map();
attendanceRecords.forEach(record => {
const month = record.date.getMonth();
if (!monthlyAttendance.has(month)) {
monthlyAttendance.set(month, {
total: 0,
attended: 0
});
}
const monthData = monthlyAttendance.get(month);
monthData.total++;
if (record.status === 'Present' || record.status === 'Late') {
monthData.attended++;
}
});
monthlyAttendance.forEach((data, month) => {
monthlyPercentages[month] = data.total > 0
? Math.round((data.attended / data.total) * 100)
: 0;
});
res.render('student/attendance', {
title: 'Attendance Records',
student,
attendanceRecords,
enrolledCourses,
overallSummary,
courseSummary,
months,
monthlyPercentages
});
} catch (err) {
console.error(err);
req.flash('error', 'Error loading attendance records');
res.redirect('/student/dashboard');
}
};This implementation provides a complete University Management System using Node.js and EJS templating with the following enhancements:
The system follows the MVC architecture with clear separation of concerns:
To run this application:
npm installnpm startThe system will be accessible at http://localhost:3000