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