Spaces:
Running
Running
| // Task Manager Class | |
| class TaskManager { | |
| constructor() { | |
| this.tasks = this.loadTasks(); | |
| this.currentFilter = 'all'; | |
| this.init(); | |
| } | |
| init() { | |
| this.setupEventListeners(); | |
| this.renderTasks(); | |
| this.updateStats(); | |
| this.startReminderScheduler(); | |
| } | |
| setupEventListeners() { | |
| // Add task | |
| document.getElementById('addTaskBtn').addEventListener('click', () => this.addTask()); | |
| document.getElementById('taskInput').addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') this.addTask(); | |
| }); | |
| // Filter buttons | |
| document.querySelectorAll('.filter-btn').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| this.setFilter(e.target.dataset.filter); | |
| }); | |
| }); | |
| // Email modal | |
| document.getElementById('closeEmailModal').addEventListener('click', () => { | |
| document.getElementById('emailModal').classList.add('hidden'); | |
| }); | |
| // Reminder modal | |
| document.getElementById('closeReminderModal').addEventListener('click', () => { | |
| document.getElementById('reminderModal').classList.add('hidden'); | |
| this.currentReminderTaskId = null; | |
| }); | |
| document.getElementById('cancelReminder').addEventListener('click', () => { | |
| document.getElementById('reminderModal').classList.add('hidden'); | |
| this.currentReminderTaskId = null; | |
| }); | |
| document.getElementById('stopExistingReminder').addEventListener('click', () => { | |
| if (this.currentReminderTaskId) { | |
| const task = this.tasks.find(t => t.id === this.currentReminderTaskId); | |
| if (task) { | |
| task.hasReminder = false; | |
| task.reminderStopped = true; | |
| this.saveTasks(); | |
| this.renderTasks(); | |
| this.updateStats(); | |
| this.showToast('Reminder stopped', 'info'); | |
| } | |
| } | |
| document.getElementById('reminderModal').classList.add('hidden'); | |
| this.currentReminderTaskId = null; | |
| }); | |
| // Frequency buttons | |
| document.querySelectorAll('.frequency-btn').forEach(btn => { | |
| btn.addEventListener('click', (e) => { | |
| const frequency = e.currentTarget.dataset.frequency; | |
| this.setReminderFrequency(frequency); | |
| }); | |
| }); | |
| // Close modals on outside click | |
| document.getElementById('emailModal').addEventListener('click', (e) => { | |
| if (e.target.id === 'emailModal') { | |
| document.getElementById('emailModal').classList.add('hidden'); | |
| } | |
| }); | |
| document.getElementById('reminderModal').addEventListener('click', (e) => { | |
| if (e.target.id === 'reminderModal') { | |
| document.getElementById('reminderModal').classList.add('hidden'); | |
| this.currentReminderTaskId = null; | |
| } | |
| }); | |
| } | |
| addTask() { | |
| const taskInput = document.getElementById('taskInput'); | |
| const emailInput = document.getElementById('emailInput'); | |
| const taskText = taskInput.value.trim(); | |
| const email = emailInput.value.trim(); | |
| if (!taskText) { | |
| this.showToast('Please enter a task', 'error'); | |
| return; | |
| } | |
| if (!email || !this.isValidEmail(email)) { | |
| this.showToast('Please enter a valid email address', 'error'); | |
| return; | |
| } | |
| const task = { | |
| id: Date.now(), | |
| text: taskText, | |
| email: email, | |
| completed: false, | |
| hasReminder: false, | |
| reminderStopped: false, | |
| createdAt: new Date().toISOString(), | |
| completedAt: null | |
| }; | |
| this.tasks.push(task); | |
| this.saveTasks(); | |
| this.renderTasks(); | |
| this.updateStats(); | |
| // Clear inputs | |
| taskInput.value = ''; | |
| emailInput.value = ''; | |
| this.showToast('Task added successfully!', 'success'); | |
| } | |
| toggleTask(id) { | |
| const task = this.tasks.find(t => t.id === id); | |
| if (task) { | |
| task.completed = !task.completed; | |
| task.completedAt = task.completed ? new Date().toISOString() : null; | |
| // When task is completed, automatically stop reminders | |
| if (task.completed) { | |
| task.hasReminder = false; | |
| task.reminderStopped = true; | |
| } | |
| this.saveTasks(); | |
| this.renderTasks(); | |
| this.updateStats(); | |
| if (task.completed) { | |
| this.showToast('Task completed! Great job! 🎉', 'success'); | |
| } else { | |
| // If unchecking a completed task, reset reminder status | |
| task.reminderStopped = false; | |
| this.showToast('Task marked as incomplete', 'info'); | |
| } | |
| } | |
| } | |
| toggleReminder(id) { | |
| const task = this.tasks.find(t => t.id === id); | |
| if (task) { | |
| if (task.completed) { | |
| this.showToast('Cannot set reminder for completed task', 'error'); | |
| return; | |
| } | |
| this.currentReminderTaskId = id; | |
| if (task.hasReminder && !task.reminderStopped) { | |
| // Show stop reminder option | |
| document.getElementById('reminderModal').classList.remove('hidden'); | |
| document.getElementById('stopExistingReminder').classList.remove('hidden'); | |
| document.querySelectorAll('.frequency-btn').forEach(btn => btn.style.display = 'none'); | |
| } else { | |
| // Show frequency selection | |
| document.getElementById('reminderModal').classList.remove('hidden'); | |
| document.getElementById('stopExistingReminder').classList.add('hidden'); | |
| document.querySelectorAll('.frequency-btn').forEach(btn => btn.style.display = 'block'); | |
| } | |
| } | |
| setReminderFrequency(frequency) { | |
| if (!this.currentReminderTaskId) return; | |
| const task = this.tasks.find(t => t.id === this.currentReminderTaskId); | |
| if (task) { | |
| task.hasReminder = true; | |
| task.reminderStopped = false; | |
| task.reminderFrequency = frequency; | |
| const frequencyText = frequency === 'daily' ? 'daily' : frequency === 'weekly' ? 'weekly' : 'monthly'; | |
| this.showToast(`Reminder set! You'll receive ${frequencyText} email notifications`, 'success'); | |
| this.showEmailPreview(task); | |
| this.saveTasks(); | |
| this.renderTasks(); | |
| this.updateStats(); | |
| } | |
| document.getElementById('reminderModal').classList.add('hidden'); | |
| this.currentReminderTaskId = null; | |
| } | |
| deleteTask(id) { | |
| if (confirm('Are you sure you want to delete this task?')) { | |
| this.tasks = this.tasks.filter(t => t.id !== id); | |
| this.saveTasks(); | |
| this.renderTasks(); | |
| this.updateStats(); | |
| this.showToast('Task deleted', 'info'); | |
| } | |
| } | |
| setFilter(filter) { | |
| this.currentFilter = filter; | |
| // Update button styles | |
| document.querySelectorAll('.filter-btn').forEach(btn => { | |
| if (btn.dataset.filter === filter) { | |
| btn.className = 'filter-btn px-4 py-2 bg-primary-500 text-white rounded-lg transition-colors'; | |
| } else { | |
| btn.className = 'filter-btn px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors'; | |
| } | |
| }); | |
| this.renderTasks(); | |
| } | |
| getFilteredTasks() { | |
| switch (this.currentFilter) { | |
| case 'pending': | |
| return this.tasks.filter(t => !t.completed); | |
| case 'completed': | |
| return this.tasks.filter(t => t.completed); | |
| case 'reminders': | |
| return this.tasks.filter(t => t.hasReminder && !t.reminderStopped && !t.completed); | |
| default: | |
| return this.tasks; | |
| } | |
| } | |
| renderTasks() { | |
| const container = document.getElementById('tasksContainer'); | |
| const emptyState = document.getElementById('emptyState'); | |
| const filteredTasks = this.getFilteredTasks(); | |
| if (filteredTasks.length === 0) { | |
| container.innerHTML = ''; | |
| emptyState.style.display = 'block'; | |
| return; | |
| } | |
| emptyState.style.display = 'none'; | |
| container.innerHTML = filteredTasks.map(task => ` | |
| <task-card | |
| id="${task.id}" | |
| text="${this.escapeHtml(task.text)}" | |
| email="${this.escapeHtml(task.email)}" | |
| completed="${task.completed}" | |
| has-reminder="${task.hasReminder}" | |
| reminder-stopped="${task.reminderStopped}" | |
| reminder-frequency="${task.reminderFrequency || ''}" | |
| created-at="${task.createdAt}" | |
| ></task-card> | |
| `).join(''); | |
| // Re-initialize feather icons for new elements | |
| feather.replace(); | |
| } | |
| updateStats() { | |
| const total = this.tasks.length; | |
| const completed = this.tasks.filter(t => t.completed).length; | |
| const pending = total - completed; | |
| const reminders = this.tasks.filter(t => t.hasReminder && !t.reminderStopped && !t.completed).length; | |
| document.getElementById('totalTasks').textContent = total; | |
| document.getElementById('completedTasks').textContent = completed; | |
| document.getElementById('pendingTasks').textContent = pending; | |
| document.getElementById('activeReminders').textContent = reminders; | |
| } | |
| showEmailPreview(task) { | |
| document.getElementById('emailTo').textContent = task.email; | |
| document.getElementById('emailSubject').textContent = task.text; | |
| document.getElementById('emailTask').textContent = task.text; | |
| document.getElementById('emailModal').classList.remove('hidden'); | |
| } | |
| startReminderScheduler() { | |
| // Check for reminders every minute | |
| setInterval(() => { | |
| this.tasks.forEach(task => { | |
| if (task.hasReminder && !task.reminderStopped && !task.completed) { | |
| const now = new Date(); | |
| const lastReminder = task.lastReminder ? new Date(task.lastReminder) : null; | |
| let shouldSend = false; | |
| if (!lastReminder) { | |
| // First reminder | |
| shouldSend = true; | |
| } else { | |
| const timeDiff = now - lastReminder; | |
| const daysDiff = timeDiff / (1000 * 60 * 60 * 24); | |
| switch (task.reminderFrequency) { | |
| case 'daily': | |
| shouldSend = daysDiff >= 1; | |
| break; | |
| case 'weekly': | |
| shouldSend = daysDiff >= 7; | |
| break; | |
| case 'monthly': | |
| shouldSend = daysDiff >= 30; | |
| break; | |
| } | |
| } | |
| if (shouldSend) { | |
| // In a real app, this would send an email | |
| console.log(`Sending ${task.reminderFrequency} reminder for task: ${task.text} to ${task.email}`); | |
| this.showToast(`Reminder: ${task.text}`, 'info'); | |
| task.lastReminder = now.toISOString(); | |
| this.saveTasks(); | |
| } | |
| } | |
| }); | |
| }, 60000); // Check every minute | |
| } | |
| showToast(message, type = 'info') { | |
| const toast = document.createElement('div'); | |
| const bgColor = type === 'success' ? 'bg-green-500' : type === 'error' ? 'bg-red-500' : 'bg-blue-500'; | |
| toast.className = `fixed bottom-4 right-4 ${bgColor} text-white px-6 py-3 rounded-lg shadow-lg z-50 flex items-center gap-2 task-card-enter`; | |
| const icon = type === 'success' ? 'check-circle' : type === 'error' ? 'alert-circle' : 'info'; | |
| toast.innerHTML = ` | |
| <i data-feather="${icon}" class="w-5 h-5"></i> | |
| <span>${message}</span> | |
| `; | |
| document.body.appendChild(toast); | |
| feather.replace(); | |
| setTimeout(() => { | |
| toast.style.opacity = '0'; | |
| setTimeout(() => toast.remove(), 300); | |
| }, 3000); | |
| } | |
| isValidEmail(email) { | |
| return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); | |
| } | |
| escapeHtml(text) { | |
| const div = document.createElement('div'); | |
| div.textContent = text; | |
| return div.innerHTML; | |
| } | |
| saveTasks() { | |
| localStorage.setItem('tasks', JSON.stringify(this.tasks)); | |
| } | |
| loadTasks() { | |
| const saved = localStorage.getItem('tasks'); | |
| return saved ? JSON.parse(saved) : []; | |
| } | |
| } | |
| // Custom event delegation for dynamic task cards | |
| document.addEventListener('click', (e) => { | |
| const toggleBtn = e.target.closest('[data-action="toggle"]'); | |
| const reminderBtn = e.target.closest('[data-action="reminder"]'); | |
| const deleteBtn = e.target.closest('[data-action="delete"]'); | |
| if (toggleBtn) { | |
| const card = e.target.closest('task-card'); | |
| if (card) { | |
| taskManager.toggleTask(parseInt(card.getAttribute('id')))); | |
| } else if (reminderBtn) { | |
| const card = e.target.closest('task-card'); | |
| if (card) { | |
| taskManager.toggleReminder(parseInt(card.getAttribute('id')))); | |
| } else if (deleteBtn) { | |
| const card = e.target.closest('task-card'); | |
| if (card) { | |
| taskManager.deleteTask(parseInt(card.getAttribute('id')))); | |
| } | |
| } | |
| }); | |
| // Initialize the app | |
| const taskManager = new TaskManager(); |