Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Todo App - Productivity Tracker</title> | |
| <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <style> | |
| .task-item { | |
| transition: all 0.3s ease; | |
| } | |
| .task-item:hover { | |
| transform: translateX(5px); | |
| } | |
| .completed { | |
| text-decoration: line-through; | |
| color: #9ca3af; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- Header --> | |
| <header class="flex justify-between items-center mb-10"> | |
| <h1 class="text-3xl font-bold text-gray-800">Todo App</h1> | |
| <nav> | |
| <ul class="flex space-x-6"> | |
| <li><a href="index.html" class="text-gray-600 hover:text-gray-900">Home</a></li> | |
| <li><a href="productivity.html" class="text-gray-600 hover:text-gray-900">Productivity</a></li> | |
| </ul> | |
| </nav> | |
| </header> | |
| <!-- Todo Section --> | |
| <main class="max-w-2xl mx-auto"> | |
| <section class="bg-white rounded-xl shadow-md p-6 mb-8"> | |
| <h2 class="text-2xl font-semibold mb-6">My Tasks</h2> | |
| <!-- Add Task Form --> | |
| <form id="taskForm" class="mb-6 flex"> | |
| <input | |
| type="text" | |
| id="taskInput" | |
| placeholder="Add a new task..." | |
| class="flex-grow px-4 py-2 border border-gray-300 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-blue-500" | |
| required | |
| > | |
| <button | |
| type="submit" | |
| class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded-r-lg font-medium transition" | |
| > | |
| Add | |
| </button> | |
| </form> | |
| <!-- Task List --> | |
| <div id="taskList" class="space-y-3"> | |
| <!-- Tasks will be added here dynamically --> | |
| </div> | |
| <!-- Stats --> | |
| <div class="mt-6 pt-4 border-t border-gray-200 flex justify-between text-sm text-gray-500"> | |
| <span id="totalTasks">Total: 0 tasks</span> | |
| <span id="completedTasks">Completed: 0</span> | |
| </div> | |
| </section> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="mt-12 text-center text-gray-500 text-sm"> | |
| <p>© 2023 Productivity Tracker. All rights reserved.</p> | |
| </footer> | |
| </div> | |
| <script> | |
| // Initialize Feather icons | |
| feather.replace(); | |
| // Todo App Functionality | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const taskForm = document.getElementById('taskForm'); | |
| const taskInput = document.getElementById('taskInput'); | |
| const taskList = document.getElementById('taskList'); | |
| const totalTasksSpan = document.getElementById('totalTasks'); | |
| const completedTasksSpan = document.getElementById('completedTasks'); | |
| let tasks = JSON.parse(localStorage.getItem('tasks')) || []; | |
| // Render tasks | |
| function renderTasks() { | |
| taskList.innerHTML = ''; | |
| if (tasks.length === 0) { | |
| taskList.innerHTML = '<p class="text-gray-500 text-center py-4">No tasks yet. Add your first task!</p>'; | |
| updateStats(); | |
| return; | |
| } | |
| tasks.forEach((task, index) => { | |
| const taskElement = document.createElement('div'); | |
| taskElement.className = `task-item flex items-center justify-between p-3 rounded-lg ${task.completed ? 'bg-green-50' : 'bg-gray-50'}`; | |
| taskElement.innerHTML = ` | |
| <div class="flex items-center"> | |
| <input | |
| type="checkbox" | |
| ${task.completed ? 'checked' : ''} | |
| data-index="${index}" | |
| class="mr-3 h-5 w-5 text-blue-500 rounded focus:ring-blue-400" | |
| > | |
| <span class="${task.completed ? 'completed' : ''}">${task.text}</span> | |
| </div> | |
| <button | |
| data-index="${index}" | |
| class="delete-btn text-red-500 hover:text-red-700" | |
| > | |
| <i data-feather="trash-2" class="w-5 h-5"></i> | |
| </button> | |
| `; | |
| taskList.appendChild(taskElement); | |
| }); | |
| // Reinitialize Feather icons | |
| feather.replace(); | |
| updateStats(); | |
| saveTasks(); | |
| } | |
| if (tasks.length === 0) { | |
| taskList.innerHTML = '<p class="text-gray-500 text-center py-4">No tasks yet. Add your first task!</p>'; | |
| totalTasksSpan.textContent = 'Total: 0 tasks'; | |
| completedTasksSpan.textContent = 'Completed: 0'; | |
| return; | |
| } | |
| tasks.forEach((task, index) => { | |
| const taskElement = document.createElement('div'); | |
| taskElement.className = `task-item flex items-center justify-between p-3 rounded-lg ${task.completed ? 'bg-green-50' : 'bg-gray-50'}`; | |
| taskElement.innerHTML = ` | |
| <div class="flex items-center"> | |
| <input | |
| type="checkbox" | |
| ${task.completed ? 'checked' : ''} | |
| data-index="${index}" | |
| class="mr-3 h-5 w-5 text-blue-500 rounded focus:ring-blue-400" | |
| > | |
| <span class="${task.completed ? 'completed' : ''}">${task.text}</span> | |
| </div> | |
| <button | |
| data-index="${index}" | |
| class="delete-btn text-red-500 hover:text-red-700" | |
| > | |
| <i data-feather="trash-2" class="w-5 h-5"></i> | |
| </button> | |
| `; | |
| taskList.appendChild(taskElement); | |
| }); | |
| // Reinitialize Feather icons | |
| feather.replace(); | |
| // Update stats | |
| function updateStats() { | |
| const total = tasks.length; | |
| const completed = tasks.filter(task => task.completed).length; | |
| totalTasksSpan.textContent = `Total: ${total} ${total === 1 ? 'task' : 'tasks'}`; | |
| completedTasksSpan.textContent = `Completed: ${completed}`; | |
| } | |
| // Save tasks to localStorage | |
| function saveTasks() { | |
| localStorage.setItem('tasks', JSON.stringify(tasks)); | |
| } | |
| // Load tasks from localStorage | |
| function loadTasks() { | |
| const storedTasks = localStorage.getItem('tasks'); | |
| if (storedTasks) { | |
| tasks = JSON.parse(storedTasks); | |
| } | |
| } | |
| // Add task | |
| taskForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const text = taskInput.value.trim(); | |
| if (text) { | |
| tasks.push({ | |
| text: text, | |
| completed: false, | |
| createdAt: new Date().toISOString() | |
| }); | |
| taskInput.value = ''; | |
| renderTasks(); | |
| } | |
| }); | |
| // Toggle task completion | |
| taskList.addEventListener('change', function(e) { | |
| if (e.target.type === 'checkbox') { | |
| const index = parseInt(e.target.dataset.index); | |
| tasks[index].completed = e.target.checked; | |
| renderTasks(); | |
| } | |
| }); | |
| // Delete task | |
| taskList.addEventListener('click', function(e) { | |
| if (e.target.closest('.delete-btn')) { | |
| const index = parseInt(e.target.closest('.delete-btn').dataset.index); | |
| tasks.splice(index, 1); | |
| renderTasks(); | |
| } | |
| }); | |
| // Initialize the app | |
| loadTasks(); | |
| renderTasks(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
| ======= | |
| // Update stats | |
| function updateStats() { | |
| const total = tasks.length; | |
| const completed = tasks.filter(task => task.completed).length; | |
| totalTasksSpan.textContent = `Total: ${total} ${total === 1 ? 'task' : 'tasks'}`; | |
| completedTasksSpan.textContent = `Completed: ${completed}`; | |
| } | |
| // Save tasks to localStorage | |
| function saveTasks() { | |
| localStorage.setItem('tasks', JSON.stringify(tasks)); | |
| } | |
| // Load tasks from localStorage | |
| function loadTasks() { | |
| const storedTasks = localStorage.getItem('tasks'); | |
| if (storedTasks) { | |
| tasks = JSON.parse(storedTasks); | |
| } | |
| } | |
| // Add task | |
| taskForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const text = taskInput.value.trim(); | |
| if (text) { | |
| tasks.push({ | |
| text: text, | |
| completed: false, | |
| createdAt: new Date().toISOString() | |
| }); | |
| taskInput.value = ''; | |
| renderTasks(); | |
| } | |
| }); | |
| // Toggle task completion | |
| taskList.addEventListener('change', function(e) { | |
| if (e.target.type === 'checkbox') { | |
| const index = parseInt(e.target.dataset.index); | |
| tasks[index].completed = e.target.checked; | |
| renderTasks(); | |
| } | |
| }); | |
| // Delete task | |
| taskList.addEventListener('click', function(e) { | |
| if (e.target.closest('.delete-btn')) { | |
| const index = parseInt(e.target.closest('.delete-btn').dataset.index); | |
| tasks.splice(index, 1); | |
| renderTasks(); | |
| } | |
| }); | |
| // Initialize the app | |
| loadTasks(); | |
| renderTasks(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
| taskForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const text = taskInput.value.trim(); | |
| if (text) { | |
| tasks.push({ | |
| text: text, | |
| completed: false, | |
| createdAt: new Date() | |
| }); | |
| taskInput.value = ''; | |
| renderTasks(); | |
| } | |
| }); | |
| // Toggle task completion | |
| taskList.addEventListener('change', function(e) { | |
| if (e.target.type === 'checkbox') { | |
| const index = e.target.dataset.index; | |
| tasks[index].completed = e.target.checked; | |
| renderTasks(); | |
| } | |
| }); | |
| // Delete task | |
| taskList.addEventListener('click', function(e) { | |
| if (e.target.closest('.delete-btn')) { | |
| const index = e.target.closest('.delete-btn').dataset.index; | |
| tasks.splice(index, 1); | |
| renderTasks(); | |
| } | |
| }); | |
| // Initial render | |
| renderTasks(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |