|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Modern To-Do App</title> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<style> |
|
|
* { |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
box-sizing: border-box; |
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
|
} |
|
|
|
|
|
:root { |
|
|
--primary: #4361ee; |
|
|
--secondary: #3a0ca3; |
|
|
--accent: #7209b7; |
|
|
--light: #f8f9fa; |
|
|
--dark: #212529; |
|
|
--success: #4cc9f0; |
|
|
--danger: #f72585; |
|
|
--warning: #fca311; |
|
|
--gray: #adb5bd; |
|
|
} |
|
|
|
|
|
body { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
min-height: 100vh; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
padding: 20px; |
|
|
} |
|
|
|
|
|
.container { |
|
|
width: 100%; |
|
|
max-width: 500px; |
|
|
background: rgba(255, 255, 255, 0.9); |
|
|
backdrop-filter: blur(10px); |
|
|
border-radius: 20px; |
|
|
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.header { |
|
|
background: var(--primary); |
|
|
color: white; |
|
|
padding: 25px; |
|
|
text-align: center; |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.header h1 { |
|
|
font-size: 28px; |
|
|
font-weight: 700; |
|
|
margin-bottom: 5px; |
|
|
letter-spacing: 1px; |
|
|
} |
|
|
|
|
|
.header p { |
|
|
font-size: 14px; |
|
|
opacity: 0.9; |
|
|
} |
|
|
|
|
|
.built-with { |
|
|
position: absolute; |
|
|
top: 15px; |
|
|
right: 15px; |
|
|
font-size: 12px; |
|
|
background: rgba(255, 255, 255, 0.2); |
|
|
padding: 4px 8px; |
|
|
border-radius: 10px; |
|
|
text-decoration: none; |
|
|
color: white; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.built-with:hover { |
|
|
background: rgba(255, 255, 255, 0.3); |
|
|
transform: translateY(-2px); |
|
|
} |
|
|
|
|
|
.app-body { |
|
|
padding: 25px; |
|
|
} |
|
|
|
|
|
.input-section { |
|
|
display: flex; |
|
|
margin-bottom: 25px; |
|
|
gap: 10px; |
|
|
} |
|
|
|
|
|
.task-input { |
|
|
flex: 1; |
|
|
padding: 15px; |
|
|
border: none; |
|
|
background: var(--light); |
|
|
border-radius: 12px; |
|
|
font-size: 16px; |
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.task-input:focus { |
|
|
outline: none; |
|
|
box-shadow: 0 4px 12px rgba(67, 97, 238, 0.2); |
|
|
transform: translateY(-2px); |
|
|
} |
|
|
|
|
|
.add-btn { |
|
|
background: var(--primary); |
|
|
color: white; |
|
|
border: none; |
|
|
padding: 15px 20px; |
|
|
border-radius: 12px; |
|
|
cursor: pointer; |
|
|
font-size: 16px; |
|
|
font-weight: 600; |
|
|
transition: all 0.3s ease; |
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
|
|
|
.add-btn:hover { |
|
|
background: var(--secondary); |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); |
|
|
} |
|
|
|
|
|
.filter-section { |
|
|
display: flex; |
|
|
gap: 10px; |
|
|
margin-bottom: 20px; |
|
|
overflow-x: auto; |
|
|
padding-bottom: 10px; |
|
|
} |
|
|
|
|
|
.filter-btn { |
|
|
background: var(--light); |
|
|
border: none; |
|
|
padding: 8px 16px; |
|
|
border-radius: 20px; |
|
|
cursor: pointer; |
|
|
font-size: 14px; |
|
|
font-weight: 500; |
|
|
transition: all 0.3s ease; |
|
|
white-space: nowrap; |
|
|
} |
|
|
|
|
|
.filter-btn.active { |
|
|
background: var(--primary); |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.filter-btn:hover:not(.active) { |
|
|
background: #e9ecef; |
|
|
} |
|
|
|
|
|
.stats { |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
margin-bottom: 20px; |
|
|
background: var(--light); |
|
|
padding: 15px; |
|
|
border-radius: 12px; |
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); |
|
|
} |
|
|
|
|
|
.stat-item { |
|
|
text-align: center; |
|
|
} |
|
|
|
|
|
.stat-number { |
|
|
font-size: 24px; |
|
|
font-weight: 700; |
|
|
color: var(--primary); |
|
|
} |
|
|
|
|
|
.stat-label { |
|
|
font-size: 12px; |
|
|
color: var(--gray); |
|
|
margin-top: 5px; |
|
|
} |
|
|
|
|
|
.task-list { |
|
|
list-style: none; |
|
|
max-height: 400px; |
|
|
overflow-y: auto; |
|
|
padding-right: 5px; |
|
|
} |
|
|
|
|
|
.task-list::-webkit-scrollbar { |
|
|
width: 5px; |
|
|
} |
|
|
|
|
|
.task-list::-webkit-scrollbar-track { |
|
|
background: #f1f1f1; |
|
|
border-radius: 10px; |
|
|
} |
|
|
|
|
|
.task-list::-webkit-scrollbar-thumb { |
|
|
background: var(--primary); |
|
|
border-radius: 10px; |
|
|
} |
|
|
|
|
|
.task-item { |
|
|
background: white; |
|
|
margin-bottom: 12px; |
|
|
padding: 18px; |
|
|
border-radius: 12px; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 15px; |
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); |
|
|
transition: all 0.3s ease; |
|
|
animation: fadeIn 0.3s ease; |
|
|
} |
|
|
|
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; transform: translateY(10px); } |
|
|
to { opacity: 1; transform: translateY(0); } |
|
|
} |
|
|
|
|
|
.task-item:hover { |
|
|
transform: translateY(-3px); |
|
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
|
|
|
.task-item.completed { |
|
|
opacity: 0.7; |
|
|
background: #f8f9fa; |
|
|
} |
|
|
|
|
|
.task-checkbox { |
|
|
width: 22px; |
|
|
height: 22px; |
|
|
border-radius: 50%; |
|
|
border: 2px solid var(--gray); |
|
|
cursor: pointer; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.task-checkbox.checked { |
|
|
background: var(--success); |
|
|
border-color: var(--success); |
|
|
} |
|
|
|
|
|
.task-checkbox.checked::after { |
|
|
content: '✓'; |
|
|
color: white; |
|
|
font-size: 14px; |
|
|
font-weight: bold; |
|
|
} |
|
|
|
|
|
.task-text { |
|
|
flex: 1; |
|
|
font-size: 16px; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.task-item.completed .task-text { |
|
|
text-decoration: line-through; |
|
|
color: var(--gray); |
|
|
} |
|
|
|
|
|
.task-actions { |
|
|
display: flex; |
|
|
gap: 10px; |
|
|
} |
|
|
|
|
|
.task-action-btn { |
|
|
background: none; |
|
|
border: none; |
|
|
cursor: pointer; |
|
|
font-size: 16px; |
|
|
transition: all 0.3s ease; |
|
|
width: 32px; |
|
|
height: 32px; |
|
|
border-radius: 50%; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
} |
|
|
|
|
|
.edit-btn { |
|
|
color: var(--primary); |
|
|
} |
|
|
|
|
|
.edit-btn:hover { |
|
|
background: rgba(67, 97, 238, 0.1); |
|
|
} |
|
|
|
|
|
.delete-btn { |
|
|
color: var(--danger); |
|
|
} |
|
|
|
|
|
.delete-btn:hover { |
|
|
background: rgba(247, 37, 133, 0.1); |
|
|
} |
|
|
|
|
|
.empty-state { |
|
|
text-align: center; |
|
|
padding: 40px 20px; |
|
|
color: var(--gray); |
|
|
} |
|
|
|
|
|
.empty-state i { |
|
|
font-size: 50px; |
|
|
margin-bottom: 15px; |
|
|
opacity: 0.5; |
|
|
} |
|
|
|
|
|
.empty-state p { |
|
|
font-size: 16px; |
|
|
} |
|
|
|
|
|
@media (max-width: 480px) { |
|
|
.container { |
|
|
border-radius: 15px; |
|
|
} |
|
|
|
|
|
.header { |
|
|
padding: 20px; |
|
|
} |
|
|
|
|
|
.header h1 { |
|
|
font-size: 24px; |
|
|
} |
|
|
|
|
|
.app-body { |
|
|
padding: 20px; |
|
|
} |
|
|
|
|
|
.input-section { |
|
|
flex-direction: column; |
|
|
} |
|
|
|
|
|
.task-input { |
|
|
margin-bottom: 10px; |
|
|
} |
|
|
|
|
|
.stats { |
|
|
flex-direction: column; |
|
|
gap: 15px; |
|
|
} |
|
|
|
|
|
.task-item { |
|
|
padding: 15px; |
|
|
} |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<div class="header"> |
|
|
<a href="https://huggingface.co/spaces/akhaliq/anycoder" class="built-with">Built with anycoder</a> |
|
|
<h1>Modern To-Do App</h1> |
|
|
<p>Organize your tasks efficiently</p> |
|
|
</div> |
|
|
|
|
|
<div class="app-body"> |
|
|
<div class="input-section"> |
|
|
<input type="text" class="task-input" placeholder="Add a new task..." id="taskInput"> |
|
|
<button class="add-btn" id="addTaskBtn"> |
|
|
<i class="fas fa-plus"></i> Add |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="filter-section"> |
|
|
<button class="filter-btn active" data-filter="all">All</button> |
|
|
<button class="filter-btn" data-filter="active">Active</button> |
|
|
<button class="filter-btn" data-filter="completed">Completed</button> |
|
|
</div> |
|
|
|
|
|
<div class="stats"> |
|
|
<div class="stat-item"> |
|
|
<div class="stat-number" id="totalTasks">0</div> |
|
|
<div class="stat-label">Total</div> |
|
|
</div> |
|
|
<div class="stat-item"> |
|
|
<div class="stat-number" id="activeTasks">0</div> |
|
|
<div class="stat-label">Active</div> |
|
|
</div> |
|
|
<div class="stat-item"> |
|
|
<div class="stat-number" id="completedTasks">0</div> |
|
|
<div class="stat-label">Completed</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<ul class="task-list" id="taskList"> |
|
|
<div class="empty-state"> |
|
|
<i class="fas fa-clipboard-list"></i> |
|
|
<p>No tasks yet. Add a task to get started!</p> |
|
|
</div> |
|
|
</ul> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
const taskInput = document.getElementById('taskInput'); |
|
|
const addTaskBtn = document.getElementById('addTaskBtn'); |
|
|
const taskList = document.getElementById('taskList'); |
|
|
const filterBtns = document.querySelectorAll('.filter-btn'); |
|
|
const totalTasksEl = document.getElementById('totalTasks'); |
|
|
const activeTasksEl = document.getElementById('activeTasks'); |
|
|
const completedTasksEl = document.getElementById('completedTasks'); |
|
|
|
|
|
let tasks = JSON.parse(localStorage.getItem('tasks')) || []; |
|
|
let currentFilter = 'all'; |
|
|
|
|
|
|
|
|
loadTasks(); |
|
|
|
|
|
|
|
|
addTaskBtn.addEventListener('click', addTask); |
|
|
taskInput.addEventListener('keypress', function(e) { |
|
|
if (e.key === 'Enter') { |
|
|
addTask(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
filterBtns.forEach(btn => { |
|
|
btn.addEventListener('click', function() { |
|
|
filterBtns.forEach(b => b.classList.remove('active')); |
|
|
this.classList.add('active'); |
|
|
currentFilter = this.getAttribute('data-filter'); |
|
|
renderTasks(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
function addTask() { |
|
|
const taskText = taskInput.value.trim(); |
|
|
|
|
|
if (taskText === '') { |
|
|
taskInput.focus(); |
|
|
return; |
|
|
} |
|
|
|
|
|
const newTask = { |
|
|
id: Date.now(), |
|
|
text: taskText, |
|
|
completed: false, |
|
|
createdAt: new Date().toISOString() |
|
|
}; |
|
|
|
|
|
tasks.push(newTask); |
|
|
saveTasks(); |
|
|
renderTasks(); |
|
|
|
|
|
taskInput.value = ''; |
|
|
taskInput.focus(); |
|
|
} |
|
|
|
|
|
function toggleTask(id) { |
|
|
tasks = tasks.map(task => { |
|
|
if (task.id === id) { |
|
|
return { ...task, completed: !task.completed }; |
|
|
} |
|
|
return task; |
|
|
}); |
|
|
|
|
|
saveTasks(); |
|
|
renderTasks(); |
|
|
} |
|
|
|
|
|
function editTask(id) { |
|
|
const task = tasks.find(t => t.id === id); |
|
|
const newText = prompt('Edit task:', task.text); |
|
|
|
|
|
if (newText && newText.trim() !== '') { |
|
|
tasks = tasks.map(t => { |
|
|
if (t.id === id) { |
|
|
return { ...t, text: newText.trim() }; |
|
|
} |
|
|
return t; |
|
|
}); |
|
|
|
|
|
saveTasks(); |
|
|
renderTasks(); |
|
|
} |
|
|
} |
|
|
|
|
|
function deleteTask(id) { |
|
|
if (confirm('Are you sure you want to delete this task?')) { |
|
|
tasks = tasks.filter(task => task.id !== id); |
|
|
saveTasks(); |
|
|
renderTasks(); |
|
|
} |
|
|
} |
|
|
|
|
|
function saveTasks() { |
|
|
localStorage.setItem('tasks', JSON.stringify(tasks)); |
|
|
updateStats(); |
|
|
} |
|
|
|
|
|
function loadTasks() { |
|
|
renderTasks(); |
|
|
updateStats(); |
|
|
} |
|
|
|
|
|
function renderTasks() { |
|
|
let filteredTasks = tasks; |
|
|
|
|
|
if (currentFilter === 'active') { |
|
|
filteredTasks = tasks.filter(task => !task.completed); |
|
|
} else if (currentFilter === 'completed') { |
|
|
filteredTasks = tasks.filter(task => task.completed); |
|
|
} |
|
|
|
|
|
if (filteredTasks.length === 0) { |
|
|
taskList.innerHTML = ` |
|
|
<div class="empty-state"> |
|
|
<i class="fas fa-clipboard-list"></i> |
|
|
<p>No ${currentFilter !== 'all' ? currentFilter : ''} tasks yet.</p> |
|
|
</div> |
|
|
`; |
|
|
return; |
|
|
} |
|
|
|
|
|
taskList.innerHTML = ''; |
|
|
|
|
|
filteredTasks.forEach(task => { |
|
|
const taskItem = document.createElement('li'); |
|
|
taskItem.className = `task-item ${task.completed ? 'completed' : ''}`; |
|
|
|
|
|
taskItem.innerHTML = ` |
|
|
<div class="task-checkbox ${task.completed ? 'checked' : ''}" data-id="${task.id}"></div> |
|
|
<div class="task-text">${task.text}</div> |
|
|
<div class="task-actions"> |
|
|
<button class="task-action-btn edit-btn" data-id="${task.id}"> |
|
|
<i class="fas fa-edit"></i> |
|
|
</button> |
|
|
<button class="task-action-btn delete-btn" data-id="${task.id}"> |
|
|
<i class="fas fa-trash"></i> |
|
|
</button> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
taskList.appendChild(taskItem); |
|
|
|
|
|
|
|
|
const checkbox = taskItem.querySelector('.task-checkbox'); |
|
|
const editBtn = taskItem.querySelector('.edit-btn'); |
|
|
const deleteBtn = taskItem.querySelector('.delete-btn'); |
|
|
|
|
|
checkbox.addEventListener('click', () => toggleTask(task.id)); |
|
|
editBtn.addEventListener('click', () => editTask(task.id)); |
|
|
deleteBtn.addEventListener('click', () => deleteTask(task.id)); |
|
|
}); |
|
|
} |
|
|
|
|
|
function updateStats() { |
|
|
const totalTasks = tasks.length; |
|
|
const completedTasks = tasks.filter(task => task.completed).length; |
|
|
const activeTasks = totalTasks - completedTasks; |
|
|
|
|
|
totalTasksEl.textContent = totalTasks; |
|
|
activeTasksEl.textContent = activeTasks; |
|
|
completedTasksEl.textContent = completedTasks; |
|
|
} |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |