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> |