Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Student Daily Planner</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); | |
body { | |
font-family: 'Poppins', sans-serif; | |
background-image: url(''); | |
background-size: cover; | |
background-attachment: fixed; | |
background-position: center; | |
min-height: 100vh; | |
} | |
.glass-card { | |
background: rgba(255, 255, 255, 0.85); | |
backdrop-filter: blur(10px); | |
border-radius: 15px; | |
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15); | |
} | |
.timeline-item::before { | |
content: ''; | |
position: absolute; | |
left: 0; | |
top: 0; | |
width: 2px; | |
height: 100%; | |
background-color: #4f46e5; | |
} | |
.period-card { | |
transition: all 0.3s ease; | |
} | |
.period-card:hover { | |
transform: translateY(-3px); | |
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); | |
} | |
.notification-badge { | |
position: absolute; | |
top: -8px; | |
right: -8px; | |
width: 20px; | |
height: 20px; | |
background-color: #ef4444; | |
border-radius: 50%; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: white; | |
font-size: 10px; | |
} | |
@media (max-width: 768px) { | |
.routine-grid { | |
grid-template-columns: repeat(1, minmax(0, 1fr)); | |
} | |
} | |
</style> | |
</head> | |
<body class="py-8 px-4"> | |
<div class="max-w-6xl mx-auto"> | |
<!-- Header --> | |
<header class="mb-8 text-center"> | |
<h1 class="text-4xl font-bold text-indigo-800 mb-2">Student Daily Planner</h1> | |
<p class="text-lg text-indigo-600">Organize your tasks efficiently</p> | |
<div class="mt-4 flex justify-center space-x-4"> | |
<button id="tasksTab" class="px-4 py-2 bg-indigo-600 text-white rounded-lg font-medium">Tasks</button> | |
</div> | |
</header> | |
<!-- Main Content --> | |
<main> | |
<!-- Tasks Section --> | |
<section id="tasksSection" class="glass-card p-6 mb-8"> | |
<div class="flex justify-between items-center mb-6"> | |
<h2 class="text-2xl font-semibold text-indigo-700 flex items-center"> | |
<i class="fas fa-tasks mr-2"></i> Today's Tasks | |
</h2> | |
<button id="addTaskBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 flex items-center"> | |
<i class="fas fa-plus mr-2"></i> Add Task | |
</button> | |
</div> | |
<!-- Add Task Modal --> | |
<div id="taskModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> | |
<div class="glass-card p-6 rounded-lg w-full max-w-md"> | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-xl font-semibold text-indigo-700">Add New Task</h3> | |
<button id="closeTaskModal" class="text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<form id="taskForm" class="space-y-4"> | |
<div> | |
<label for="taskTitle" class="block text-sm font-medium text-gray-700">Task Title</label> | |
<input type="text" id="taskTitle" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" required> | |
</div> | |
<div> | |
<label for="taskTime" class="block text-sm font-medium text-gray-700">Time</label> | |
<input type="time" id="taskTime" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" required> | |
</div> | |
<div> | |
<label for="taskTopic" class="block text-sm font-medium text-gray-700">Topic/Details</label> | |
<input type="text" id="taskTopic" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border"> | |
</div> | |
<div> | |
<label for="taskReminder" class="block text-sm font-medium text-gray-700">Reminder Before (minutes)</label> | |
<select id="taskReminder" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border"> | |
<option value="5">5 minutes</option> | |
<option value="10" selected>10 minutes</option> | |
<option value="15">15 minutes</option> | |
</select> | |
</div> | |
<div class="flex justify-end space-x-3"> | |
<button type="button" id="cancelTask" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300">Cancel</button> | |
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700">Save Task</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<!-- Tasks Timeline --> | |
<div id="tasksContainer" class="space-y-4"> | |
<!-- Sample tasks will be inserted here by JavaScript --> | |
<div class="text-center py-8 text-gray-500" id="noTasksMessage"> | |
<i class="fas fa-clipboard-list text-4xl mb-2"></i> | |
<p>No tasks added yet. Click "Add Task" to get started!</p> | |
</div> | |
</div> | |
</section> | |
<script> | |
// DOM Elements | |
const tasksTab = document.getElementById('tasksTab'); | |
const routineTab = document.getElementById('routineTab'); | |
const tasksSection = document.getElementById('tasksSection'); | |
const routineSection = document.getElementById('routineSection'); | |
const addTaskBtn = document.getElementById('addTaskBtn'); | |
const taskModal = document.getElementById('taskModal'); | |
const closeTaskModal = document.getElementById('closeTaskModal'); | |
const cancelTask = document.getElementById('cancelTask'); | |
const taskForm = document.getElementById('taskForm'); | |
const tasksContainer = document.getElementById('tasksContainer'); | |
const noTasksMessage = document.getElementById('noTasksMessage'); | |
const addDayBtn = document.getElementById('addDayBtn'); | |
const dayModal = document.getElementById('dayModal'); | |
const closeDayModal = document.getElementById('closeDayModal'); | |
const cancelDay = document.getElementById('cancelDay'); | |
const dayForm = document.getElementById('dayForm'); | |
const periodsModal = document.getElementById('periodsModal'); | |
const closePeriodsModal = document.getElementById('closePeriodsModal'); | |
const cancelPeriods = document.getElementById('cancelPeriods'); | |
const periodsForm = document.getElementById('periodsForm'); | |
const periodsContainer = document.getElementById('periodsContainer'); | |
const routineContainer = document.getElementById('routineContainer'); | |
const noRoutineMessage = document.getElementById('noRoutineMessage'); | |
const periodsModalTitle = document.getElementById('periodsModalTitle'); | |
// Data | |
let tasks = JSON.parse(localStorage.getItem('tasks')) || []; | |
let routine = JSON.parse(localStorage.getItem('routine')) || {}; | |
let currentDay = ''; | |
let notificationTimeouts = {}; | |
// Initialize | |
document.addEventListener('DOMContentLoaded', () => { | |
renderTasks(); | |
renderRoutine(); | |
checkForNotifications(); | |
}); | |
// Tab Navigation | |
tasksTab.addEventListener('click', () => { | |
tasksSection.classList.remove('hidden'); | |
routineSection.classList.add('hidden'); | |
tasksTab.classList.add('bg-indigo-600', 'text-white'); | |
tasksTab.classList.remove('bg-white', 'text-indigo-600'); | |
routineTab.classList.add('bg-white', 'text-indigo-600'); | |
routineTab.classList.remove('bg-indigo-600', 'text-white'); | |
}); | |
routineTab.addEventListener('click', () => { | |
routineSection.classList.remove('hidden'); | |
tasksSection.classList.add('hidden'); | |
routineTab.classList.add('bg-indigo-600', 'text-white'); | |
routineTab.classList.remove('bg-white', 'text-indigo-600'); | |
tasksTab.classList.add('bg-white', 'text-indigo-600'); | |
tasksTab.classList.remove('bg-indigo-600', 'text-white'); | |
}); | |
// Task Modal | |
addTaskBtn.addEventListener('click', () => { | |
taskModal.classList.remove('hidden'); | |
}); | |
closeTaskModal.addEventListener('click', () => { | |
taskModal.classList.add('hidden'); | |
}); | |
cancelTask.addEventListener('click', () => { | |
taskModal.classList.add('hidden'); | |
}); | |
// Task Form Submission | |
taskForm.addEventListener('submit', (e) => { | |
e.preventDefault(); | |
const title = document.getElementById('taskTitle').value; | |
const time = document.getElementById('taskTime').value; | |
const topic = document.getElementById('taskTopic').value; | |
const reminder = parseInt(document.getElementById('taskReminder').value); | |
const newTask = { | |
id: Date.now(), | |
title, | |
time, | |
topic, | |
reminder, | |
completed: false | |
}; | |
tasks.push(newTask); | |
saveTasks(); | |
renderTasks(); | |
scheduleNotification(newTask); | |
taskForm.reset(); | |
taskModal.classList.add('hidden'); | |
}); | |
// Day Modal | |
addDayBtn.addEventListener('click', () => { | |
dayModal.classList.remove('hidden'); | |
}); | |
closeDayModal.addEventListener('click', () => { | |
dayModal.classList.add('hidden'); | |
}); | |
cancelDay.addEventListener('click', () => { | |
dayModal.classList.add('hidden'); | |
}); | |
// Day Form Submission | |
dayForm.addEventListener('submit', (e) => { | |
e.preventDefault(); | |
const day = document.getElementById('daySelect').value; | |
const periodCount = parseInt(document.getElementById('periodCount').value); | |
currentDay = day; | |
periodsModalTitle.textContent = `Add Periods for ${day}`; | |
// Clear previous inputs | |
periodsContainer.innerHTML = ''; | |
// Create inputs for each period | |
for (let i = 1; i <= periodCount; i++) { | |
const periodDiv = document.createElement('div'); | |
periodDiv.className = 'space-y-2 p-4 bg-gray-50 rounded-lg mb-4'; | |
periodDiv.innerHTML = ` | |
<h4 class="font-medium text-indigo-600">Period ${i}</h4> | |
<div> | |
<label for="subject-${i}" class="block text-sm font-medium text-gray-700">Subject</label> | |
<input type="text" id="subject-${i}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" required> | |
</div> | |
<div class="grid grid-cols-2 gap-4"> | |
<div> | |
<label for="startTime-${i}" class="block text-sm font-medium text-gray-700">Start Time</label> | |
<input type="time" id="startTime-${i}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" required> | |
</div> | |
<div> | |
<label for="endTime-${i}" class="block text-sm font-medium text-gray-700">End Time</label> | |
<input type="time" id="endTime-${i}" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" required> | |
</div> | |
</div> | |
`; | |
periodsContainer.appendChild(periodDiv); | |
} | |
dayModal.classList.add('hidden'); | |
periodsModal.classList.remove('hidden'); | |
}); | |
// Periods Form Submission | |
periodsForm.addEventListener('submit', (e) => { | |
e.preventDefault(); | |
const periodCount = parseInt(document.getElementById('periodCount').value); | |
const periods = []; | |
for (let i = 1; i <= periodCount; i++) { | |
const subject = document.getElementById(`subject-${i}`).value; | |
const startTime = document.getElementById(`startTime-${i}`).value; | |
const endTime = document.getElementById(`endTime-${i}`).value; | |
periods.push({ | |
number: i, | |
subject, | |
startTime, | |
endTime | |
}); | |
} | |
routine[currentDay] = periods; | |
saveRoutine(); | |
renderRoutine(); | |
periodsModal.classList.add('hidden'); | |
dayForm.reset(); | |
}); | |
closePeriodsModal.addEventListener('click', () => { | |
periodsModal.classList.add('hidden'); | |
}); | |
cancelPeriods.addEventListener('click', () => { | |
periodsModal.classList.add('hidden'); | |
}); | |
// Render Tasks | |
function renderTasks() { | |
if (tasks.length === 0) { | |
noTasksMessage.classList.remove('hidden'); | |
tasksContainer.innerHTML = ''; | |
return; | |
} | |
noTasksMessage.classList.add('hidden'); | |
// Sort tasks by time | |
tasks.sort((a, b) => { | |
return a.time.localeCompare(b.time); | |
}); | |
tasksContainer.innerHTML = ''; | |
tasks.forEach(task => { | |
const taskElement = document.createElement('div'); | |
taskElement.className = 'relative pl-8 timeline-item'; | |
taskElement.dataset.id = task.id; | |
const timeParts = task.time.split(':'); | |
const formattedTime = `${parseInt(timeParts[0]) > 12 ? parseInt(timeParts[0]) - 12 : timeParts[0]}:${timeParts[1]} ${parseInt(timeParts[0]) >= 12 ? 'PM' : 'AM'}`; | |
taskElement.innerHTML = ` | |
<div class="bg-white p-4 rounded-lg shadow-sm border-l-4 border-indigo-500 relative"> | |
<div class="absolute -left-3 top-4 w-6 h-6 rounded-full bg-indigo-500 flex items-center justify-center text-white"> | |
<i class="fas fa-clock text-xs"></i> | |
</div> | |
<div class="flex justify-between items-start"> | |
<div> | |
<h3 class="font-medium ${task.completed ? 'line-through text-gray-400' : 'text-gray-800'}">${task.title}</h3> | |
<p class="text-sm text-gray-500">${formattedTime}</p> | |
${task.topic ? `<p class="text-sm text-gray-600 mt-1"><span class="font-medium">Topic:</span> ${task.topic}</p>` : ''} | |
</div> | |
<div class="flex space-x-2"> | |
<button class="edit-task p-2 text-indigo-600 hover:text-indigo-800"> | |
<i class="fas fa-edit"></i> | |
</button> | |
<button class="delete-task p-2 text-red-600 hover:text-red-800"> | |
<i class="fas fa-trash"></i> | |
</button> | |
<button class="complete-task p-2 ${task.completed ? 'text-green-600' : 'text-gray-400 hover:text-green-600'}"> | |
<i class="fas fa-check"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
`; | |
tasksContainer.appendChild(taskElement); | |
}); | |
// Add event listeners for task actions | |
document.querySelectorAll('.edit-task').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const taskId = parseInt(e.target.closest('.timeline-item').dataset.id); | |
editTask(taskId); | |
}); | |
}); | |
document.querySelectorAll('.delete-task').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const taskId = parseInt(e.target.closest('.timeline-item').dataset.id); | |
deleteTask(taskId); | |
}); | |
}); | |
document.querySelectorAll('.complete-task').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const taskId = parseInt(e.target.closest('.timeline-item').dataset.id); | |
toggleTaskComplete(taskId); | |
}); | |
}); | |
} | |
// Render Routine | |
function renderRoutine() { | |
if (Object.keys(routine).length === 0) { | |
noRoutineMessage.classList.remove('hidden'); | |
routineContainer.innerHTML = ''; | |
return; | |
} | |
noRoutineMessage.classList.add('hidden'); | |
routineContainer.innerHTML = ''; | |
Object.entries(routine).forEach(([day, periods]) => { | |
const dayCard = document.createElement('div'); | |
dayCard.className = 'bg-white p-4 rounded-lg shadow-sm border-t-4 border-indigo-500'; | |
let periodsHTML = ''; | |
periods.forEach(period => { | |
periodsHTML += ` | |
<div class="period-card bg-gray-50 p-3 rounded-lg mb-3 relative"> | |
<div class="flex justify-between items-center"> | |
<h4 class="font-medium text-indigo-700">Period ${period.number}: ${period.subject}</h4> | |
<div class="flex space-x-2"> | |
<button class="edit-period p-1 text-indigo-600 hover:text-indigo-800" data-day="${day}" data-period="${period.number}"> | |
<i class="fas fa-edit text-sm"></i> | |
</button> | |
<button class="delete-period p-1 text-red-600 hover:text-red-800" data-day="${day}" data-period="${period.number}"> | |
<i class="fas fa-trash text-sm"></i> | |
</button> | |
</div> | |
</div> | |
<p class="text-sm text-gray-600 mt-1"> | |
<i class="far fa-clock mr-1"></i> ${formatTime(period.startTime)} - ${formatTime(period.endTime)} | |
</p> | |
</div> | |
`; | |
}); | |
dayCard.innerHTML = ` | |
<div class="flex justify-between items-center mb-4"> | |
<h3 class="text-lg font-semibold text-indigo-800">${day}</h3> | |
<div class="flex space-x-2"> | |
<button class="add-period p-1 text-indigo-600 hover:text-indigo-800" data-day="${day}"> | |
<i class="fas fa-plus-circle"></i> | |
</button> | |
<button class="delete-day p-1 text-red-600 hover:text-red-800" data-day="${day}"> | |
<i class="fas fa-trash"></i> | |
</button> | |
</div> | |
</div> | |
<div class="periods-container"> | |
${periodsHTML} | |
</div> | |
`; | |
routineContainer.appendChild(dayCard); | |
}); | |
// Add event listeners for routine actions | |
document.querySelectorAll('.add-period').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const day = e.target.closest('.add-period').dataset.day; | |
addPeriod(day); | |
}); | |
}); | |
document.querySelectorAll('.edit-period').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const day = e.target.closest('.edit-period').dataset.day; | |
const periodNum = parseInt(e.target.closest('.edit-period').dataset.period); | |
editPeriod(day, periodNum); | |
}); | |
}); | |
document.querySelectorAll('.delete-period').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const day = e.target.closest('.delete-period').dataset.day; | |
const periodNum = parseInt(e.target.closest('.delete-period').dataset.period); | |
deletePeriod(day, periodNum); | |
}); | |
}); | |
document.querySelectorAll('.delete-day').forEach(btn => { | |
btn.addEventListener('click', (e) => { | |
const day = e.target.closest('.delete-day').dataset.day; | |
deleteDay(day); | |
}); | |
}); | |
} | |
// Task Actions | |
function editTask(id) { | |
const task = tasks.find(t => t.id === id); | |
if (!task) return; | |
document.getElementById('taskTitle').value = task.title; | |
document.getElementById('taskTime').value = task.time; | |
document.getElementById('taskTopic').value = task.topic || ''; | |
document.getElementById('taskReminder').value = task.reminder; | |
// Remove the task from the array | |
tasks = tasks.filter(t => t.id !== id); | |
saveTasks(); | |
// Open the modal to edit | |
taskModal.classList.remove('hidden'); | |
} | |
function deleteTask(id) { | |
if (confirm('Are you sure you want to delete this task?')) { | |
tasks = tasks.filter(t => t.id !== id); | |
saveTasks(); | |
renderTasks(); | |
// Clear any scheduled notification | |
if (notificationTimeouts[id]) { | |
clearTimeout(notificationTimeouts[id]); | |
delete notificationTimeouts[id]; | |
} | |
} | |
} | |
function toggleTaskComplete(id) { | |
const task = tasks.find(t => t.id === id); | |
if (task) { | |
task.completed = !task.completed; | |
saveTasks(); | |
renderTasks(); | |
} | |
} | |
// Routine Actions | |
function addPeriod(day) { | |
currentDay = day; | |
periodsModalTitle.textContent = `Add Period to ${day}`; | |
// Clear previous inputs | |
periodsContainer.innerHTML = ''; | |
const periodDiv = document.createElement('div'); | |
periodDiv.className = 'space-y-2 p-4 bg-gray-50 rounded-lg mb-4'; | |
const nextPeriodNumber = routine[day] ? routine[day].length + 1 : 1; | |
periodDiv.innerHTML = ` | |
<h4 class="font-medium text-indigo-600">New Period</h4> | |
<div> | |
<label for="subject-new" class="block text-sm font-medium text-gray-700">Subject</label> | |
<input type="text" id="subject-new" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" required> | |
</div> | |
<div class="grid grid-cols-2 gap-4"> | |
<div> | |
<label for="startTime-new" class="block text-sm font-medium text-gray-700">Start Time</label> | |
<input type="time" id="startTime-new" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" required> | |
</div> | |
<div> | |
<label for="endTime-new" class="block text-sm font-medium text-gray-700">End Time</label> | |
<input type="time" id="endTime-new" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" required> | |
</div> | |
</div> | |
`; | |
periodsContainer.appendChild(periodDiv); | |
// Change the form submit handler for adding a single period | |
periodsForm.removeEventListener('submit', handlePeriodsFormSubmit); | |
periodsForm.addEventListener('submit', handleAddPeriodSubmit); | |
periodsModal.classList.remove('hidden'); | |
function handleAddPeriodSubmit(e) { | |
e.preventDefault(); | |
const subject = document.getElementById('subject-new').value; | |
const startTime = document.getElementById('startTime-new').value; | |
const endTime = document.getElementById('endTime-new').value; | |
if (!routine[day]) { | |
routine[day] = []; | |
} | |
routine[day].push({ | |
number: nextPeriodNumber, | |
subject, | |
startTime, | |
endTime | |
}); | |
saveRoutine(); | |
renderRoutine(); | |
periodsModal.classList.add('hidden'); | |
periodsForm.reset(); | |
// Restore the original form handler | |
periodsForm.removeEventListener('submit', handleAddPeriodSubmit); | |
periodsForm.addEventListener('submit', handlePeriodsFormSubmit); | |
} | |
} | |
function editPeriod(day, periodNum) { | |
const period = routine[day].find(p => p.number === periodNum); | |
if (!period) return; | |
currentDay = day; | |
periodsModalTitle.textContent = `Edit Period ${periodNum} for ${day}`; | |
// Clear previous inputs | |
periodsContainer.innerHTML = ''; | |
const periodDiv = document.createElement('div'); | |
periodDiv.className = 'space-y-2 p-4 bg-gray-50 rounded-lg mb-4'; | |
periodDiv.innerHTML = ` | |
<h4 class="font-medium text-indigo-600">Period ${periodNum}</h4> | |
<div> | |
<label for="subject-edit" class="block text-sm font-medium text-gray-700">Subject</label> | |
<input type="text" id="subject-edit" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" value="${period.subject}" required> | |
</div> | |
<div class="grid grid-cols-2 gap-4"> | |
<div> | |
<label for="startTime-edit" class="block text-sm font-medium text-gray-700">Start Time</label> | |
<input type="time" id="startTime-edit" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" value="${period.startTime}" required> | |
</div> | |
<div> | |
<label for="endTime-edit" class="block text-sm font-medium text-gray-700">End Time</label> | |
<input type="time" id="endTime-edit" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 p-2 border" value="${period.endTime}" required> | |
</div> | |
</div> | |
`; | |
periodsContainer.appendChild(periodDiv); | |
// Change the form submit handler for editing a single period | |
periodsForm.removeEventListener('submit', handlePeriodsFormSubmit); | |
periodsForm.addEventListener('submit', handleEditPeriodSubmit); | |
periodsModal.classList.remove('hidden'); | |
function handleEditPeriodSubmit(e) { | |
e.preventDefault(); | |
const subject = document.getElementById('subject-edit').value; | |
const startTime = document.getElementById('startTime-edit').value; | |
const endTime = document.getElementById('endTime-edit').value; | |
const periodIndex = routine[day].findIndex(p => p.number === periodNum); | |
if (periodIndex !== -1) { | |
routine[day][periodIndex] = { | |
number: periodNum, | |
subject, | |
startTime, | |
endTime | |
}; | |
} | |
saveRoutine(); | |
renderRoutine(); | |
periodsModal.classList.add('hidden'); | |
periodsForm.reset(); | |
// Restore the original form handler | |
periodsForm.removeEventListener('submit', handleEditPeriodSubmit); | |
periodsForm.addEventListener('submit', handlePeriodsFormSubmit); | |
} | |
} | |
function deletePeriod(day, periodNum) { | |
if (confirm('Are you sure you want to delete this period?')) { | |
routine[day] = routine[day].filter(p => p.number !== periodNum); | |
// Renumber remaining periods | |
routine[day].forEach((p, index) => { | |
p.number = index + 1; | |
}); | |
saveRoutine(); | |
renderRoutine(); | |
} | |
} | |
function deleteDay(day) { | |
if (confirm(`Are you sure you want to delete all periods for ${day}?`)) { | |
delete routine[day]; | |
saveRoutine(); | |
renderRoutine(); | |
} | |
} | |
function handlePeriodsFormSubmit(e) { | |
e.preventDefault(); | |
const periodCount = parseInt(document.getElementById('periodCount').value); | |
const periods = []; | |
for (let i = 1; i <= periodCount; i++) { | |
const subject = document.getElementById(`subject-${i}`).value; | |
const startTime = document.getElementById(`startTime-${i}`).value; | |
const endTime = document.getElementById(`endTime-${i}`).value; | |
periods.push({ | |
number: i, | |
subject, | |
startTime, | |
endTime | |
}); | |
} | |
routine[currentDay] = periods; | |
saveRoutine(); | |
renderRoutine(); | |
periodsModal.classList.add('hidden'); | |
dayForm.reset(); | |
} | |
// Helper Functions | |
function saveTasks() { | |
localStorage.setItem('tasks', JSON.stringify(tasks)); | |
} | |
function saveRoutine() { | |
localStorage.setItem('routine', JSON.stringify(routine)); | |
} | |
function formatTime(timeStr) { | |
if (!timeStr) return ''; | |
const [hours, minutes] = timeStr.split(':'); | |
const hourNum = parseInt(hours); | |
const ampm = hourNum >= 12 ? 'PM' : 'AM'; | |
const displayHour = hourNum % 12 || 12; | |
return `${displayHour}:${minutes} ${ampm}`; | |
} | |
function scheduleNotification(task) { | |
if (!('Notification' in window)) { | |
console.log('This browser does not support desktop notification'); | |
return; | |
} | |
// Check if notification permission is already granted | |
if (Notification.permission !== 'granted') { | |
Notification.requestPermission().then(permission => { | |
if (permission === 'granted') { | |
createNotificationTimeout(task); | |
} | |
}); | |
} else { | |
createNotificationTimeout(task); | |
} | |
} | |
function createNotificationTimeout(task) { | |
const taskTime = new Date(); | |
const [hours, minutes] = task.time.split(':'); | |
taskTime.setHours(parseInt(hours)); | |
taskTime.setMinutes(parseInt(minutes)); | |
const reminderTime = new Date(taskTime.getTime() - task.reminder * 60000); | |
const now = new Date(); | |
// Only schedule if the reminder time is in the future | |
if (reminderTime > now) { | |
const timeout = reminderTime.getTime() - now.getTime(); | |
notificationTimeouts[task.id] = setTimeout(() => { | |
showNotification(task); | |
}, timeout); | |
} | |
} | |
function showNotification(task) { | |
const notification = new Notification('Task Reminder', { | |
body: `It's time to ${task.title}${task.topic ? ` (${task.topic})` : ''}`, | |
icon: 'https://cdn-icons-png.flaticon.com/512/3652/3652191.png' | |
}); | |
notification.onclick = () => { | |
window.focus(); | |
}; | |
} | |
function checkForNotifications() { | |
tasks.forEach(task => { | |
if (!task.completed) { | |
scheduleNotification(task); | |
} | |
}); | |
} | |
</script> | |
</body> | |
</html> |