/**
* Exam Reminder System
* This script handles the exam reminder notifications for students
*/
class ExamReminderService {
constructor() {
this.reminders = [];
this.checkInterval = 60000; // Check every minute
this.initialized = false;
}
/**
* Initialize the reminder service
*/
init() {
if (this.initialized) return;
// Load reminders from localStorage
this.loadReminders();
// Check if we need to request notification permission
if (Notification.permission !== 'granted') {
this.showNotificationPrompt();
}
// Start checking for reminders
this.startReminderCheck();
this.initialized = true;
console.log('Exam reminder service initialized');
}
/**
* Load reminders from localStorage
*/
loadReminders() {
try {
const savedReminders = localStorage.getItem('examReminders');
if (savedReminders) {
this.reminders = JSON.parse(savedReminders);
console.log(`Loaded ${this.reminders.length} reminders`);
}
} catch (e) {
console.error('Error loading reminders:', e);
this.reminders = [];
}
}
/**
* Save reminders to localStorage
*/
saveReminders() {
try {
localStorage.setItem('examReminders', JSON.stringify(this.reminders));
} catch (e) {
console.error('Error saving reminders:', e);
}
}
/**
* Show notification permission prompt
*/
showNotificationPrompt() {
const container = document.createElement('div');
container.className = 'notification-prompt';
container.innerHTML = `
<div class="alert alert-info alert-dismissible fade show" role="alert">
<strong>Enable Exam Reminders!</strong> Allow notifications to get reminded before your exams.
<button type="button" class="btn btn-sm btn-primary mx-2" id="enable-notifications">Enable Notifications</button>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
`;
document.body.prepend(container);
document.getElementById('enable-notifications').addEventListener('click', () => {
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
this.showNotification('Reminders Enabled', 'You will now receive exam reminders.');
container.remove();
}
});
});
}
/**
* Start periodically checking for due reminders
*/
startReminderCheck() {
// Do an initial check
this.checkReminders();
// Set interval for checking
setInterval(() => this.checkReminders(), this.checkInterval);
}
/**
* Check if any reminders need to be sent
*/
checkReminders() {
if (!this.reminders.length) return;
const now = new Date();
const activeReminders = [];
for (const reminder of this.reminders) {
// Calculate when the exam is scheduled
const examDateTime = new Date(`${reminder.examDate}T${reminder.examTime}`);
// Calculate when the reminder should be sent
const reminderMinutes = parseInt(reminder.reminderTime, 10);
const reminderTime = new Date(examDateTime.getTime() - (reminderMinutes * 60 * 1000));
// If the reminder time has passed but not more than 5 minutes ago
// (to prevent duplicate notifications if page is refreshed)
const timeDiff = now - reminderTime;
if (timeDiff >= 0 && timeDiff < 5 * 60 * 1000) {
// Send the reminder
this.sendReminder(reminder);
}
// Keep reminders for future exams
if (examDateTime > now) {
activeReminders.push(reminder);
}
}
// Update reminders list (remove past exams)
if (this.reminders.length !== activeReminders.length) {
this.reminders = activeReminders;
this.saveReminders();
}
}
/**
* Send a reminder notification
* @param {Object} reminder - The reminder object
*/
sendReminder(reminder) {
const reminderMinutes = parseInt(reminder.reminderTime, 10);
let timeText = '';
if (reminderMinutes >= 1440) {
timeText = `${reminderMinutes / 1440} day(s)`;
} else if (reminderMinutes >= 60) {
timeText = `${reminderMinutes / 60} hour(s)`;
} else {
timeText = `${reminderMinutes} minutes`;
}
const title = `Upcoming Exam: ${reminder.examTitle}`;
const body = `Your exam starts in ${timeText}. Prepare now!`;
// Send browser notification if applicable
if (reminder.reminderType === 'browser' || reminder.reminderType === 'both') {
this.showNotification(title, body);
}
// Send email notification if applicable (would typically be server-side)
if (reminder.reminderType === 'email' || reminder.reminderType === 'both') {
// In a real implementation, this would make an AJAX call to a server endpoint
console.log('Email reminder would be sent for:', reminder);
}
}
/**
* Show a browser notification
* @param {string} title - The notification title
* @param {string} body - The notification message
*/
showNotification(title, body) {
if (Notification.permission !== 'granted') return;
try {
const notification = new Notification(title, {
body: body,
icon: '/assets/img/exam-icon.png',
requireInteraction: true
});
notification.onclick = () => {
window.focus();
notification.close();
};
} catch (e) {
console.error('Error showing notification:', e);
}
}
/**
* Add a new reminder
* @param {Object} reminder - The reminder object
*/
addReminder(reminder) {
this.reminders.push(reminder);
this.saveReminders();
}
/**
* Remove a reminder by ID
* @param {string} examId - The exam ID to remove reminders for
*/
removeReminder(examId) {
this.reminders = this.reminders.filter(r => r.examId !== examId);
this.saveReminders();
}
/**
* Get all active reminders
* @returns {Array} - Array of reminders
*/
getReminders() {
return this.reminders;
}
}
// Initialize the service when the document is ready
document.addEventListener('DOMContentLoaded', () => {
window.examReminderService = new ExamReminderService();
window.examReminderService.init();
});