anycoder-b7499a08 / index.html
akhaliq's picture
akhaliq HF Staff
Upload folder using huggingface_hub
47db126 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo Master - Advanced Task Manager</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--primary-light: #818cf8;
--secondary: #ec4899;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--dark: #1e293b;
--light: #f8fafc;
--gray: #64748b;
--gray-light: #e2e8f0;
--white: #ffffff;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
--shadow-lg: 0 20px 25px -5px rgb(0 0 0 / 0.1);
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
--radius: 12px;
--radius-sm: 8px;
}
[data-theme="dark"] {
--light: #0f172a;
--gray-light: #334155;
--white: #1e293b;
--dark: #f8fafc;
--gray: #94a3b8;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
transition: var(--transition);
position: relative;
overflow-x: hidden;
}
.background-shapes {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: -1;
}
.shape {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
animation: float 20s infinite ease-in-out;
}
.shape:nth-child(1) {
width: 300px;
height: 300px;
top: -150px;
left: -150px;
animation-delay: 0s;
}
.shape:nth-child(2) {
width: 200px;
height: 200px;
bottom: -100px;
right: -100px;
animation-delay: 5s;
}
.shape:nth-child(3) {
width: 150px;
height: 150px;
top: 50%;
left: 50%;
animation-delay: 10s;
}
@keyframes float {
0%,
100% {
transform: translate(0, 0) rotate(0deg);
}
33% {
transform: translate(30px, -30px) rotate(120deg);
}
66% {
transform: translate(-20px, 20px) rotate(240deg);
}
}
.container {
width: 100%;
max-width: 900px;
background: var(--white);
border-radius: 24px;
box-shadow: var(--shadow-lg);
overflow: hidden;
animation: slideIn 0.5s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
header {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
color: var(--white);
padding: 30px;
position: relative;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
h1 {
font-size: 2em;
font-weight: 700;
display: flex;
align-items: center;
gap: 12px;
}
.logo {
width: 45px;
height: 45px;
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5em;
}
.header-actions {
display: flex;
gap: 12px;
}
.theme-toggle {
background: rgba(255, 255, 255, 0.2);
border: none;
color: var(--white);
width: 40px;
height: 40px;
border-radius: var(--radius-sm);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: var(--transition);
}
.theme-toggle:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.1);
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
}
.stat-card {
background: rgba(255, 255, 255, 0.2);
padding: 15px;
border-radius: var(--radius-sm);
text-align: center;
backdrop-filter: blur(10px);
transition: var(--transition);
}
.stat-card:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.stat-value {
font-size: 1.8em;
font-weight: 700;
margin-bottom: 5px;
}
.stat-label {
font-size: 0.9em;
opacity: 0.9;
}
.progress-bar {
height: 6px;
background: rgba(255, 255, 255, 0.3);
border-radius: 3px;
overflow: hidden;
margin-top: 20px;
}
.progress-fill {
height: 100%;
background: var(--success);
transition: width 0.5s ease;
border-radius: 3px;
}
.add-todo-section {
padding: 30px;
background: var(--light);
border-bottom: 1px solid var(--gray-light);
}
.add-todo-form {
display: flex;
flex-direction: column;
gap: 15px;
}
.input-group {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.todo-input {
flex: 1;
min-width: 200px;
padding: 12px 16px;
border: 2px solid var(--gray-light);
border-radius: var(--radius-sm);
font-size: 1em;
transition: var(--transition);
background: var(--white);
color: var(--dark);
}
.todo-input:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
.select-input {
padding: 12px 16px;
border: 2px solid var(--gray-light);
border-radius: var(--radius-sm);
font-size: 1em;
background: var(--white);
color: var(--dark);
cursor: pointer;
transition: var(--transition);
}
.select-input:focus {
outline: none;
border-color: var(--primary);
}
.date-input {
padding: 12px 16px;
border: 2px solid var(--gray-light);
border-radius: var(--radius-sm);
font-size: 1em;
background: var(--white);
color: var(--dark);
transition: var(--transition);
}
.add-btn {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
color: var(--white);
border: none;
padding: 12px 24px;
border-radius: var(--radius-sm);
font-size: 1em;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 8px;
}
.add-btn:hover {
transform: translateY(-2px);
box-shadow: var(--shadow);
}
.add-btn:active {
transform: translateY(0);
}
.controls-section {
padding: 20px 30px;
background: var(--white);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
border-bottom: 1px solid var(--gray-light);
}
.search-box {
position: relative;
flex: 1;
max-width: 300px;
}
.search-input {
width: 100%;
padding: 10px 16px 10px 40px;
border: 2px solid var(--gray-light);
border-radius: var(--radius-sm);
font-size: 0.95em;
background: var(--light);
color: var(--dark);
transition: var(--transition);
}
.search-input:focus {
outline: none;
border-color: var(--primary);
background: var(--white);
}
.search-icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: var(--gray);
}
.filter-tabs {
display: flex;
gap: 5px;
background: var(--light);
padding: 4px;
border-radius: var(--radius-sm);
}
.filter-tab {
padding: 8px 16px;
border: none;
background: transparent;
color: var(--gray);
font-size: 0.95em;
font-weight: 500;
cursor: pointer;
border-radius: var(--radius-sm);
transition: var(--transition);
}
.filter-tab.active {
background: var(--white);
color: var(--primary);
box-shadow: var(--shadow-sm);
}
.clear-btn {
background: var(--danger);
color: var(--white);
border: none;
padding: 10px 20px;
border-radius: var(--radius-sm);
font-size: 0.95em;
font-weight: 500;
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 6px;
}
.clear-btn:hover {
background: #dc2626;
transform: translateY(-1px);
}
.todos-section {
padding: 30px;
min-height: 300px;
}
.todos-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.todo-item {
background: var(--white);
border: 2px solid var(--gray-light);
border-radius: var(--radius-sm);
padding: 16px;
display: flex;
align-items: center;
gap: 12px;
transition: var(--transition);
cursor: move;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.todo-item:hover {
border-color: var(--primary-light);
box-shadow: var(--shadow-sm);
transform: translateX(4px);
}
.todo-item.dragging {
opacity: 0.5;
transform: scale(1.05);
}
.todo-item.completed {
opacity: 0.7;
background: var(--light);
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: var(--gray);
}
.todo-checkbox {
width: 24px;
height: 24px;
border: 2px solid var(--primary);
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: var(--transition);
flex-shrink: 0;
}
.todo-checkbox:hover {
background: rgba(99, 102, 241, 0.1);
}
.todo-checkbox.checked {
background: var(--primary);
}
.todo-checkbox.checked::after {
content: '✓';
color: var(--white);
font-weight: bold;
}
.todo-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
.todo-text {
font-size: 1em;
color: var(--dark);
word-break: break-word;
}
.todo-meta {
display: flex;
gap: 12px;
flex-wrap: wrap;
align-items: center;
}
.todo-category,
.todo-priority,
.todo-date {
font-size: 0.85em;
padding: 4px 10px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 4px;
}
.category-work {
background: rgba(99, 102, 241, 0.1);
color: var(--primary);
}
.category-personal {
background: rgba(236, 72, 153, 0.1);
color: var(--secondary);
}
.category-shopping {
background: rgba(245, 158, 11, 0.1);
color: var(--warning);
}
.category-health {
background: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.priority-high {
background: rgba(239, 68, 68, 0.1);
color: var(--danger);
}
.priority-medium {
background: rgba(245, 158, 11, 0.1);
color: var(--warning);
}
.priority-low {
background: rgba(100, 116, 139, 0.1);
color: var(--gray);
}
.todo-date {
background: rgba(100, 116, 139, 0.1);
color: var(--gray);
}
.todo-actions {
display: flex;
gap: 8px;
}
.action-btn {
width: 32px;
height: 32px;
border: none;
background: var(--light);
color: var(--gray);
border-radius: var(--radius-sm);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: var(--transition);
}
.action-btn:hover {
background: var(--primary);
color: var(--white);
transform: scale(1.1);
}
.action-btn.delete:hover {
background: var(--danger);
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--gray);
}
.empty-icon {
font-size: 4em;
margin-bottom: 20px;
opacity: 0.3;
}
.empty-text {
font-size: 1.2em;
margin-bottom: 10px;
}
.empty-subtext {
font-size: 0.95em;
opacity: 0.7;
}
.edit-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
z-index: 1000;
align-items: center;
justify-content: center;
animation: fadeIn 0.3s ease-out;
}
.edit-modal.active {
display: flex;
}
.modal-content {
background: var(--white);
border-radius: var(--radius);
padding: 30px;
width: 90%;
max-width: 500px;
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.modal-header {
margin-bottom: 20px;
}
.modal-title {
font-size: 1.5em;
color: var(--dark);
display: flex;
align-items: center;
gap: 10px;
}
.modal-form {
display: flex;
flex-direction: column;
gap: 15px;
}
.modal-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: 20px;
}
.modal-btn {
padding: 10px 20px;
border: none;
border-radius: var(--radius-sm);
font-size: 1em;
font-weight: 500;
cursor: pointer;
transition: var(--transition);
}
.modal-btn.cancel {
background: var(--gray-light);
color: var(--dark);
}
.modal-btn.save {
background: var(--primary);
color: var(--white);
}
.modal-btn:hover {
transform: translateY(-2px);
}
footer {
padding: 20px;
text-align: center;
background: var(--light);
border-top: 1px solid var(--gray-light);
}
.footer-link {
color: var(--primary);
text-decoration: none;
font-weight: 500;
transition: var(--transition);
}
.footer-link:hover {
color: var(--primary-dark);
text-decoration: underline;
}
@media (max-width: 768px) {
.container {
border-radius: 16px;
}
header {
padding: 20px;
}
h1 {
font-size: 1.5em;
}
.stats {
grid-template-columns: repeat(2, 1fr);
}
.add-todo-section {
padding: 20px;
}
.input-group {
flex-direction: column;
}
.controls-section {
padding: 15px 20px;
}
.filter-tabs {
order: 3;
width: 100%;
}
.filter-tab {
flex: 1;
text-align: center;
}
.todos-section {
padding: 20px;
}
.todo-item {
padding: 12px;
}
.todo-meta {
font-size: 0.8em;
}
}
@media (max-width: 480px) {
body {
padding: 10px;
}
.header-content {
flex-direction: column;
gap: 15px;
align-items: flex-start;
}
.stats {
grid-template-columns: 1fr;
}
.controls-section {
flex-direction: column;
align-items: stretch;
}
.search-box {
max-width: 100%;
}
}
.notification {
position: fixed;
bottom: 20px;
right: 20px;
background: var(--success);
color: var(--white);
padding: 16px 20px;
border-radius: var(--radius-sm);
box-shadow: var(--shadow-lg);
display: flex;
align-items: center;
gap: 10px;
transform: translateX(400px);
transition: transform 0.3s ease-out;
z-index: 2000;
}
.notification.show {
transform: translateX(0);
}
</style>
</head>
<body>
<div class="background-shapes">
<div class="shape"></div>
<div class="shape"></div>
<div class="shape"></div>
</div>
<div class="container">
<header>
<div class="header-content">
<h1>
<div class="logo">
<i class="fas fa-tasks"></i>
</div>
Todo Master
</h1>
<div class="header-actions">
<button class="theme-toggle" onclick="toggleTheme()">
<i class="fas fa-moon" id="theme-icon"></i>
</button>
</div>
</div>
<div class="stats">
<div class="stat-card">
<div class="stat-value" id="total-tasks">0</div>
<div class="stat-label">Total Tasks</div>
</div>
<div class="stat-card">
<div class="stat-value" id="completed-tasks">0</div>
<div class="stat-label">Completed</div>
</div>
<div class="stat-card">
<div class="stat-value" id="pending-tasks">0</div>
<div class="stat-label">Pending</div>
</div>
<div class="stat-card">
<div class="stat-value" id="completion-rate">0%</div>
<div class="stat-label">Completion Rate</div>
</div>
</div>
<div class="progress-bar">
<div class="progress-fill" id="progress-fill"></div>
</div>
</header>
<section class="add-todo-section">
<form class="add-todo-form" onsubmit="addTodo(event)">
<div class="input-group">
<input type="text" class="todo-input" id="todo-input" placeholder="What needs to be done?" required>
<select class="select-input" id="category-select">
<option value="work">Work</option>
<option value="personal">Personal</option>
<option value="shopping">Shopping</option>
<option value="health">Health</option>
</select>
<select class="select-input" id="priority-select">
<option value="low">Low Priority</option>
<option value="medium">Medium Priority</option>
<option value="high">High Priority</option>
</select>
<input type="date" class="date-input" id="due-date">
<button type="submit" class="add-btn">
<i class="fas fa-plus"></i>
Add Task
</button>
</div>
</form>
</section>
<section class="controls-section">
<div class="search-box">
<i class="fas fa-search search-icon"></i>
<input type="text" class="search-input" id="search-input" placeholder="Search tasks..." oninput="searchTodos()">
</div>
<div class="filter-tabs">
<button class="filter-tab active" onclick="filterTodos('all')">All</button>
<button class="filter-tab" onclick="filterTodos('active')">Active</button>
<button class="filter-tab" onclick="filterTodos('completed')">Completed</button>
</div>
<button class="clear-btn" onclick="clearCompleted()">
<i class="fas fa-trash"></i>
Clear Completed
</button>
</section>
<main class="todos-section">
<div class="todos-list" id="todos-list">
<div class="empty-state">
<div class="empty-icon">
<i class="fas fa-clipboard-list"></i>
</div>
<div class="empty-text">No tasks yet</div>
<div class="empty-subtext">Add your first task to get started!</div>
</div>
</div>
</main>
<footer>
<p>Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank"
class="footer-link">anycoder</a></p>
</footer>
</div>
<div class="edit-modal" id="edit-modal">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">
<i class="fas fa-edit"></i>
Edit Task
</h2>
</div>
<form class="modal-form" onsubmit="saveEdit(event)">
<input type="text" class="todo-input" id="edit-input" required>
<select class="select-input" id="edit-category">
<option value="work">Work</option>
<option value="personal">Personal</option>
<option value="shopping">Shopping</option>
<option value="health">Health</option>
</select>
<select class="select-input" id="edit-priority">
<option value="low">Low Priority</option>
<option value="medium">Medium Priority</option>
<option value="high">High Priority</option>
</select>
<input type="date" class="date-input" id="edit-date">
<div class="modal-actions">
<button type="button" class="modal-btn cancel" onclick="closeEditModal()">Cancel</button>
<button type="submit" class="modal-btn save">Save Changes</button>
</div>
</form>
</div>
</div>
<div class="notification" id="notification">
<i class="fas fa-check-circle"></i>
<span id="notification-text">Task added successfully!</span>
</div>
<script>
let todos = JSON.parse(localStorage.getItem('todos')) || [];
let currentFilter = 'all';
let editingId = null;
let draggedItem = null;
// Initialize app
document.addEventListener('DOMContentLoaded', () => {
loadTheme();
renderTodos();
updateStats();
setMinDate();
});
// Theme management
function toggleTheme() {
const html = document.documentElement;
const themeIcon = document.getElementById('theme-icon');
if (html.getAttribute('data-theme') === 'dark') {
html.removeAttribute('data-theme');
themeIcon.className = 'fas fa-moon';
localStorage.setItem('theme', 'light');
} else {
html.setAttribute('data-theme', 'dark');
themeIcon.className = 'fas fa-sun';
localStorage.setItem('theme', 'dark');
}
}
function loadTheme() {
const theme = localStorage.getItem('theme');
const themeIcon = document.getElementById('theme-icon');
if (theme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
themeIcon.className = 'fas fa-sun';
}
}
// Set minimum date to today
function setMinDate() {
const today = new Date().toISOString().split('T')[0];
document.getElementById('due-date').setAttribute('min', today);
document.getElementById('edit-date').setAttribute('min', today);
}
// Add todo
function addTodo(event) {
event.preventDefault();
const input = document.getElementById('todo-input');
const category = document.getElementById('category-select').value;
const priority = document.getElementById('priority-select').value;
const dueDate = document.getElementById('due-date').value;
const todo = {
id: Date.now(),
text: input.value.trim(),
completed: false,
category,
priority,
dueDate,
createdAt: new Date().toISOString()
};
todos.unshift(todo);
saveTodos();
renderTodos();
updateStats();
// Reset form
input.value = '';
document.getElementById('due-date').value = '';
showNotification('Task added successfully!');
}
// Toggle todo completion
function toggleTodo(id) {
const todo = todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
saveTodos();
renderTodos();
updateStats();
if (todo.completed) {
showNotification('Task completed! Great job!');
}
}
}
// Delete todo
function deleteTodo(id) {
todos = todos.filter(t => t.id !== id);
saveTodos();
renderTodos();
updateStats();
showNotification('Task deleted');
}
// Edit todo
function editTodo(id) {
const todo = todos.find(t => t.id === id);
if (todo) {
editingId = id;
document.getElementById('edit-input').value = todo.text;
document.getElementById('edit-category').value = todo.category;
document.getElementById('edit-priority').value = todo.priority;
document.getElementById('edit-date').value = todo.dueDate;
document.getElementById('edit-modal').classList.add('active');
}
}
// Save edit
function saveEdit(event) {
event.preventDefault();
const todo = todos.find(t => t.id === editingId);
if (todo) {
todo.text = document.getElementById('edit-input').value.trim();
todo.category = document.getElementById('edit-category').value;
todo.priority = document.getElementById('edit-priority').value;
todo.dueDate = document.getElementById('edit-date').value;
saveTodos();
renderTodos();
updateStats();
closeEditModal();
showNotification('Task updated successfully!');
}
}
// Close edit modal
function closeEditModal() {
document.getElementById('edit-modal').classList.remove('active');
editingId = null;
}
// Filter todos
function filterTodos(filter) {
currentFilter = filter;
// Update active tab
document.querySelectorAll('.filter-tab').forEach(tab => {
tab.classList.remove('active');
});
event.target.classList.add('active');
renderTodos();
}
// Search todos
function searchTodos() {
renderTodos();
}
// Clear completed todos
function clearCompleted() {
const completedCount = todos.filter(t => t.completed).length;
if (completedCount > 0) {
if (confirm(`Are you sure you want to delete ${completedCount} completed task(s)?`)) {
todos = todos.filter(t => !t.completed);
saveTodos();
renderTodos();
updateStats();
showNotification('Completed tasks cleared');
}
} else {
showNotification('No completed tasks to clear');
}
}
// Render todos
function renderTodos() {
const list = document.getElementById('todos-list');
const searchTerm = document.getElementById('search-input').value.toLowerCase();
let filteredTodos = todos;
// Apply filter
if (currentFilter === 'active') {
filteredTodos = filteredTodos.filter(t => !t.completed);
} else if (currentFilter === 'completed') {
filteredTodos = filteredTodos.filter(t => t.completed);
}
// Apply search
if (searchTerm) {
filteredTodos = filteredTodos.filter(t =>
t.text.toLowerCase().includes(searchTerm)
);
}
if (filteredTodos.length === 0) {
list.innerHTML = `
<div class="empty-state">
<div class="empty-icon">
<i class="fas fa-clipboard-list"></i>
</div>
<div class="empty-text">No tasks found</div>
<div class="empty-subtext">
${searchTerm ? 'Try a different search term' : 'Add a new task to get started!'}
</div>
</div>
`;
return;
}
list.innerHTML = filteredTodos.map(todo => `
<div class="todo-item ${todo.completed ? 'completed' : ''}"
draggable="true"
ondragstart="handleDragStart(event, ${todo.id})"
ondragover="handleDragOver(event)"
ondrop="handleDrop(event, ${todo.id})"
ondragend="handleDragEnd(event)">
<div class="todo-checkbox ${todo.completed ? 'checked' : ''}"
onclick="toggleTodo(${todo.id})"></div>
<div class="todo-content">
<div class="todo-text">${escapeHtml(todo.text)}</div>
<div class="todo-meta">
<span class="todo-category category-${todo.category}">
<i class="fas fa-tag"></i>
${todo.category}
</span>
<span class="todo-priority priority-${todo.priority}">
<i class="fas fa-flag"></i>
${todo.priority}
</span>
${todo.dueDate ? `
<span class="todo-date">
<i class="fas fa-calendar"></i>
${formatDate(todo.dueDate)}
</span>
` : ''}
</div>
</div>
<div class="todo-actions">
<button class="action-btn" onclick="editTodo(${todo.id})">
<i class="fas fa-edit"></i>
</button>
<button class="action-btn delete" onclick="deleteTodo(${todo.id})">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`).join('');
}
// Drag and drop
function handleDragStart(event, id) {
draggedItem = id;
event.target.classList.add('dragging');
}
function handleDragOver(event) {
event.preventDefault();
}
function handleDrop(event, targetId) {
event.preventDefault();
if (draggedItem !== targetId) {
const draggedIndex = todos.findIndex(t => t.id === draggedItem);
const targetIndex = todos.findIndex(t => t.id === targetId);
if (draggedIndex !== -1 && targetIndex !== -1) {
const [removed] = todos.splice(draggedIndex, 1);
todos.splice(targetIndex, 0, removed);
saveTodos();
renderTodos();
}
}
}
function handleDragEnd(event) {
event.target.classList.remove('dragging');
}
// Update statistics
function updateStats() {
const total = todos.length;
const completed = todos.filter(t => t.completed).length;
const pending = total - completed;
const rate = total > 0 ? Math.round((completed / total) * 100) : 0;
document.getElementById('total-tasks').textContent = total;
document.getElementById('completed-tasks').textContent = completed;
document.getElementById('pending-tasks').textContent = pending;
document.getElementById('completion-rate').textContent = rate + '%';
document.getElementById('progress-fill').style.width = rate + '%';
}
// Save to localStorage
function saveTodos() {
localStorage.setItem('todos', JSON.stringify(todos));
}
// Show notification
function showNotification(message) {
const notification = document.getElementById('notification');
const text = document.getElementById('notification-text');
text.textContent = message;
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
}, 3000);
}
// Utility functions
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function formatDate(dateString) {
const date = new Date(dateString);
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
if (date.toDateString() === today.toDateString()) {
return 'Today';
} else if (date.toDateString() === tomorrow.toDateString()) {
return 'Tomorrow';
} else {
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: date.getFullYear() !== today.getFullYear() ? 'numeric' : undefined
});
}
}
// Close modal on escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeEditModal();
}
});
// Close modal on outside click
document.getElementById('edit-modal').addEventListener('click', (e) => {
if (e.target.id === 'edit-modal') {
closeEditModal();
}
});
</script>
</body>
</html>