| <!DOCTYPE html> |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>My To-Do List</title> |
| <style> |
| body { |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; |
| background-color: #f4f4f9; |
| color: #333; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| min-height: 100vh; |
| margin: 0; |
| padding: 20px 0; |
| } |
| #app-container { |
| background: #fff; |
| padding: 25px 40px; |
| border-radius: 10px; |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); |
| width: 100%; |
| max-width: 500px; |
| box-sizing: border-box; |
| } |
| h1 { |
| color: #444; |
| text-align: center; |
| margin-top: 0; |
| margin-bottom: 20px; |
| font-weight: 600; |
| } |
| #task-input-container { |
| display: flex; |
| gap: 10px; |
| margin-bottom: 20px; |
| } |
| #task-input { |
| flex-grow: 1; |
| padding: 12px; |
| border: 1px solid #ddd; |
| border-radius: 5px; |
| font-size: 16px; |
| } |
| #add-task-btn { |
| padding: 12px 20px; |
| background-color: #007bff; |
| color: white; |
| border: none; |
| border-radius: 5px; |
| cursor: pointer; |
| font-size: 16px; |
| font-weight: 500; |
| transition: background-color 0.3s; |
| } |
| #add-task-btn:hover { |
| background-color: #0056b3; |
| } |
| #task-list { |
| list-style: none; |
| padding: 0; |
| margin: 0; |
| } |
| .task-item { |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| padding: 12px 0; |
| border-bottom: 1px solid #eee; |
| font-size: 16px; |
| } |
| .task-item:last-child { |
| border-bottom: none; |
| } |
| .task-text-content { |
| cursor: pointer; |
| flex-grow: 1; |
| padding-right: 10px; |
| overflow-wrap: break-word; |
| word-break: break-word; |
| } |
| .task-item.completed .task-text-content { |
| text-decoration: line-through; |
| color: #aaa; |
| } |
| .task-actions { |
| display: flex; |
| align-items: center; |
| flex-shrink: 0; |
| } |
| .emoji-toggle { |
| background: transparent; |
| border: 1px solid transparent; |
| border-radius: 4px; |
| padding: 4px 6px; |
| cursor: pointer; |
| margin-left: 5px; |
| font-size: 18px; |
| opacity: 0.4; |
| transition: opacity 0.2s, border-color 0.2s; |
| line-height: 1; |
| } |
| .emoji-toggle:hover { |
| opacity: 0.7; |
| } |
| .emoji-toggle.active { |
| opacity: 1; |
| border: 1px solid #007bff; |
| } |
| .delete-btn { |
| background: #dc3545; |
| color: white; |
| border: none; |
| border-radius: 50%; |
| width: 28px; |
| height: 28px; |
| cursor: pointer; |
| font-size: 16px; |
| line-height: 28px; |
| text-align: center; |
| transition: background-color 0.3s; |
| margin-left: 8px; |
| flex-shrink: 0; |
| } |
| .delete-btn:hover { |
| background: #c82333; |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <div id="app-container"> |
| <h1>To-Do List ✅</h1> |
| <div id="task-input-container"> |
| <input type="text" id="task-input" placeholder="Add a new task..."> |
| <button id="add-task-btn">Add</button> |
| </div> |
| <ul id="task-list"></ul> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', () => { |
| const taskInput = document.getElementById('task-input'); |
| const addTaskBtn = document.getElementById('add-task-btn'); |
| const taskList = document.getElementById('task-list'); |
| |
| const initialRawTasks = [ |
| { text: "Write the Unit Test", completed: true }, |
| { text: "Check the PR", completed: true }, |
| { text: "Debug the GPU trace", completed: true } |
| ]; |
| |
| |
| const ensureTaskProperties = (task) => { |
| return { |
| text: task.text, |
| completed: !!task.completed, |
| isCode: !!task.isCode, |
| isAdmin: !!task.isAdmin, |
| isClient: !!task.isClient, |
| }; |
| }; |
| |
| |
| let tasks = []; |
| const storedTasks = localStorage.getItem('tasks'); |
| if (storedTasks) { |
| tasks = JSON.parse(storedTasks).map(ensureTaskProperties); |
| } else { |
| tasks = initialRawTasks.map(ensureTaskProperties); |
| } |
| if (tasks.length === 0 && initialRawTasks.length > 0 && !storedTasks) { |
| tasks = initialRawTasks.map(ensureTaskProperties); |
| } |
| |
| |
| |
| const saveTasks = () => { |
| localStorage.setItem('tasks', JSON.stringify(tasks)); |
| }; |
| |
| |
| const renderTasks = () => { |
| |
| tasks.sort((a, b) => { |
| |
| if (a.isClient && !b.isClient) return -1; |
| if (!a.isClient && b.isClient) return 1; |
| |
| |
| if (a.isCode && !b.isCode) return -1; |
| if (!a.isCode && b.isCode) return 1; |
| |
| |
| if (!a.completed && b.completed) return -1; |
| if (a.completed && !b.completed) return 1; |
| |
| return 0; |
| }); |
| |
| taskList.innerHTML = ''; |
| tasks.forEach((task, index) => { |
| const li = document.createElement('li'); |
| li.className = 'task-item'; |
| if (task.completed) { |
| li.classList.add('completed'); |
| } |
| |
| |
| const taskTextContentEl = document.createElement('span'); |
| taskTextContentEl.className = 'task-text-content'; |
| let emojisPrefix = ''; |
| if (task.isClient) emojisPrefix += '👤 '; |
| if (task.isCode) emojisPrefix += '💻 '; |
| if (task.isAdmin) emojisPrefix += '⚙️ '; |
| taskTextContentEl.textContent = emojisPrefix + task.text; |
| taskTextContentEl.addEventListener('click', () => toggleTaskCompletion(index)); |
| |
| |
| const actionsContainer = document.createElement('div'); |
| actionsContainer.className = 'task-actions'; |
| |
| |
| const categories = [ |
| { type: 'client', emoji: '👤', prop: 'isClient', title: 'Client Task' }, |
| { type: 'code', emoji: '💻', prop: 'isCode', title: 'Code Task' }, |
| { type: 'admin', emoji: '⚙️', prop: 'isAdmin', title: 'Admin Task' } |
| ]; |
| |
| categories.forEach(cat => { |
| const toggleBtn = document.createElement('button'); |
| toggleBtn.className = 'emoji-toggle'; |
| toggleBtn.textContent = cat.emoji; |
| toggleBtn.title = `Toggle ${cat.title}`; |
| toggleBtn.dataset.type = cat.type; |
| if (task[cat.prop]) { |
| toggleBtn.classList.add('active'); |
| } |
| toggleBtn.addEventListener('click', (e) => { |
| e.stopPropagation(); |
| toggleEmojiCategory(index, cat.prop); |
| }); |
| actionsContainer.appendChild(toggleBtn); |
| }); |
| |
| |
| const deleteBtn = document.createElement('button'); |
| deleteBtn.textContent = '×'; |
| deleteBtn.className = 'delete-btn'; |
| deleteBtn.title = "Delete Task"; |
| deleteBtn.addEventListener('click', (e) => { |
| e.stopPropagation(); |
| deleteTask(index); |
| }); |
| |
| actionsContainer.appendChild(deleteBtn); |
| |
| li.appendChild(taskTextContentEl); |
| li.appendChild(actionsContainer); |
| taskList.appendChild(li); |
| }); |
| }; |
| |
| |
| const addTask = () => { |
| const taskText = taskInput.value.trim(); |
| if (taskText !== '') { |
| |
| tasks.unshift(ensureTaskProperties({ text: taskText, completed: false })); |
| taskInput.value = ''; |
| saveTasks(); |
| renderTasks(); |
| } |
| }; |
| |
| |
| const deleteTask = (index) => { |
| tasks.splice(index, 1); |
| saveTasks(); |
| renderTasks(); |
| }; |
| |
| |
| const toggleTaskCompletion = (index) => { |
| tasks[index].completed = !tasks[index].completed; |
| saveTasks(); |
| renderTasks(); |
| }; |
| |
| |
| const toggleEmojiCategory = (index, categoryProp) => { |
| tasks[index][categoryProp] = !tasks[index][categoryProp]; |
| saveTasks(); |
| renderTasks(); |
| }; |
| |
| |
| addTaskBtn.addEventListener('click', addTask); |
| |
| |
| taskInput.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') { |
| addTask(); |
| } |
| }); |
| |
| |
| renderTasks(); |
| }); |
| </script> |
|
|
| </body> |
| </html> |