anycoder-8605f0aa / index.html
akhaliq's picture
akhaliq HF Staff
Upload folder using huggingface_hub
e83c351 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TaskFlow - Modern Todo 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;
}
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--secondary: #8b5cf6;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--dark: #1e293b;
--light: #f8fafc;
--text-primary: #1e293b;
--text-secondary: #64748b;
--border: #e2e8f0;
--shadow: rgba(0, 0, 0, 0.1);
--gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: var(--text-primary);
transition: var(--transition);
}
body.dark-mode {
--light: #0f172a;
--text-primary: #f1f5f9;
--text-secondary: #94a3b8;
--border: #334155;
--shadow: rgba(0, 0, 0, 0.3);
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 25px;
margin-bottom: 30px;
box-shadow: 0 10px 40px var(--shadow);
animation: slideDown 0.5s ease-out;
}
body.dark-mode header {
background: rgba(30, 41, 59, 0.95);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 20px;
}
.logo {
display: flex;
align-items: center;
gap: 15px;
font-size: 28px;
font-weight: 800;
background: var(--gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.logo i {
font-size: 32px;
-webkit-text-fill-color: var(--primary);
}
.header-actions {
display: flex;
gap: 15px;
align-items: center;
}
.theme-toggle {
background: var(--light);
border: 2px solid var(--border);
border-radius: 50px;
padding: 10px 15px;
cursor: pointer;
transition: var(--transition);
font-size: 20px;
}
.theme-toggle:hover {
transform: scale(1.1);
box-shadow: 0 5px 15px var(--shadow);
}
.main-content {
display: grid;
grid-template-columns: 1fr 350px;
gap: 30px;
animation: fadeIn 0.7s ease-out;
}
.todo-section {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 40px var(--shadow);
}
body.dark-mode .todo-section {
background: rgba(30, 41, 59, 0.95);
}
.add-todo-form {
display: flex;
gap: 10px;
margin-bottom: 30px;
}
.input-group {
flex: 1;
display: flex;
flex-direction: column;
gap: 5px;
}
.input-wrapper {
position: relative;
flex: 1;
}
.todo-input {
width: 100%;
padding: 15px 20px;
border: 2px solid var(--border);
border-radius: 15px;
font-size: 16px;
transition: var(--transition);
background: var(--light);
color: var(--text-primary);
}
.todo-input:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
.input-options {
display: flex;
gap: 10px;
margin-top: 10px;
flex-wrap: wrap;
}
.option-select {
padding: 8px 12px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--light);
color: var(--text-primary);
font-size: 14px;
cursor: pointer;
transition: var(--transition);
}
.option-select:focus {
outline: none;
border-color: var(--primary);
}
.add-btn {
padding: 15px 30px;
background: var(--gradient);
color: white;
border: none;
border-radius: 15px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 8px;
align-self: flex-end;
}
.add-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px var(--shadow);
}
.filters {
display: flex;
gap: 10px;
margin-bottom: 25px;
flex-wrap: wrap;
}
.filter-btn {
padding: 10px 20px;
background: var(--light);
border: 2px solid var(--border);
border-radius: 12px;
cursor: pointer;
transition: var(--transition);
font-weight: 500;
color: var(--text-secondary);
}
.filter-btn.active {
background: var(--primary);
color: white;
border-color: var(--primary);
}
.filter-btn:hover:not(.active) {
border-color: var(--primary);
color: var(--primary);
}
.search-box {
position: relative;
flex: 1;
max-width: 300px;
}
.search-input {
width: 100%;
padding: 10px 15px 10px 40px;
border: 2px solid var(--border);
border-radius: 12px;
background: var(--light);
color: var(--text-primary);
}
.search-input:focus {
outline: none;
border-color: var(--primary);
}
.search-icon {
position: absolute;
left: 15px;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
}
.todo-list {
list-style: none;
max-height: 500px;
overflow-y: auto;
padding-right: 10px;
}
.todo-list::-webkit-scrollbar {
width: 8px;
}
.todo-list::-webkit-scrollbar-track {
background: var(--light);
border-radius: 10px;
}
.todo-list::-webkit-scrollbar-thumb {
background: var(--primary);
border-radius: 10px;
}
.todo-item {
background: var(--light);
border: 2px solid var(--border);
border-radius: 15px;
padding: 20px;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 15px;
transition: var(--transition);
cursor: move;
animation: slideIn 0.3s ease-out;
}
.todo-item:hover {
transform: translateX(5px);
box-shadow: 0 5px 15px var(--shadow);
}
.todo-item.completed {
opacity: 0.7;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: var(--text-secondary);
}
.todo-item.dragging {
opacity: 0.5;
transform: rotate(2deg);
}
.checkbox-wrapper {
position: relative;
}
.todo-checkbox {
width: 24px;
height: 24px;
cursor: pointer;
accent-color: var(--success);
}
.todo-content {
flex: 1;
}
.todo-text {
font-size: 16px;
color: var(--text-primary);
margin-bottom: 8px;
}
.todo-meta {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.todo-category {
padding: 4px 10px;
background: var(--primary);
color: white;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
}
.todo-priority {
padding: 4px 10px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
}
.priority-high {
background: var(--danger);
color: white;
}
.priority-medium {
background: var(--warning);
color: white;
}
.priority-low {
background: var(--success);
color: white;
}
.todo-date {
padding: 4px 10px;
background: var(--light);
border: 1px solid var(--border);
border-radius: 20px;
font-size: 12px;
color: var(--text-secondary);
display: flex;
align-items: center;
gap: 5px;
}
.todo-actions {
display: flex;
gap: 10px;
}
.action-btn {
width: 35px;
height: 35px;
border: none;
background: var(--light);
border-radius: 10px;
cursor: pointer;
transition: var(--transition);
color: var(--text-secondary);
display: flex;
align-items: center;
justify-content: center;
}
.action-btn:hover {
transform: scale(1.1);
}
.action-btn.edit:hover {
color: var(--primary);
background: rgba(99, 102, 241, 0.1);
}
.action-btn.delete:hover {
color: var(--danger);
background: rgba(239, 68, 68, 0.1);
}
.stats-section {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 40px var(--shadow);
}
body.dark-mode .stats-section {
background: rgba(30, 41, 59, 0.95);
}
.stats-header {
font-size: 20px;
font-weight: 700;
margin-bottom: 25px;
color: var(--text-primary);
}
.stat-card {
background: var(--light);
border-radius: 15px;
padding: 20px;
margin-bottom: 15px;
border: 2px solid var(--border);
transition: var(--transition);
}
.stat-card:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px var(--shadow);
}
.stat-label {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 8px;
}
.stat-value {
font-size: 32px;
font-weight: 700;
background: var(--gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.progress-bar {
width: 100%;
height: 10px;
background: var(--border);
border-radius: 10px;
overflow: hidden;
margin-top: 15px;
}
.progress-fill {
height: 100%;
background: var(--gradient);
transition: width 0.5s ease;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-secondary);
}
.empty-state i {
font-size: 64px;
margin-bottom: 20px;
opacity: 0.5;
}
.empty-state h3 {
font-size: 24px;
margin-bottom: 10px;
color: var(--text-primary);
}
.export-import {
display: flex;
gap: 10px;
margin-top: 20px;
}
.export-btn,
.import-btn {
flex: 1;
padding: 12px;
background: var(--light);
border: 2px solid var(--border);
border-radius: 12px;
cursor: pointer;
transition: var(--transition);
font-weight: 500;
color: var(--text-primary);
}
.export-btn:hover,
.import-btn:hover {
border-color: var(--primary);
color: var(--primary);
}
.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;
animation: fadeIn 0.3s ease;
}
.modal.active {
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background: var(--light);
border-radius: 20px;
padding: 30px;
max-width: 500px;
width: 90%;
animation: slideUp 0.3s ease;
}
.modal-header {
font-size: 24px;
font-weight: 700;
margin-bottom: 20px;
color: var(--text-primary);
}
.modal-body {
margin-bottom: 20px;
}
.modal-input {
width: 100%;
padding: 12px;
border: 2px solid var(--border);
border-radius: 12px;
background: var(--light);
color: var(--text-primary);
margin-bottom: 15px;
}
.modal-input:focus {
outline: none;
border-color: var(--primary);
}
.modal-footer {
display: flex;
gap: 10px;
justify-content: flex-end;
}
.modal-btn {
padding: 10px 20px;
border: none;
border-radius: 10px;
cursor: pointer;
font-weight: 500;
transition: var(--transition);
}
.modal-btn.cancel {
background: var(--border);
color: var(--text-primary);
}
.modal-btn.save {
background: var(--primary);
color: white;
}
.modal-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px var(--shadow);
}
.keyboard-shortcuts {
position: fixed;
bottom: 20px;
right: 20px;
background: var(--light);
padding: 15px;
border-radius: 15px;
border: 2px solid var(--border);
font-size: 12px;
color: var(--text-secondary);
box-shadow: 0 5px 15px var(--shadow);
max-width: 200px;
}
.shortcut-item {
margin-bottom: 8px;
display: flex;
justify-content: space-between;
}
.shortcut-key {
background: var(--border);
padding: 2px 6px;
border-radius: 4px;
font-weight: 600;
color: var(--text-primary);
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
}
.add-todo-form {
flex-direction: column;
}
.add-btn {
width: 100%;
justify-content: center;
}
.header-content {
flex-direction: column;
text-align: center;
}
.keyboard-shortcuts {
display: none;
}
.filters {
justify-content: center;
}
.search-box {
max-width: 100%;
}
}
.built-with {
position: fixed;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
color: white;
font-size: 12px;
opacity: 0.8;
z-index: 100;
}
.built-with a {
color: white;
text-decoration: none;
font-weight: 600;
}
.built-with a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<header>
<div class="header-content">
<div class="logo">
<i class="fas fa-tasks"></i>
TaskFlow
</div>
<div class="header-actions">
<button class="theme-toggle" onclick="toggleTheme()">
<i class="fas fa-moon"></i>
</button>
</div>
</div>
</header>
<main class="main-content">
<section class="todo-section">
<form class="add-todo-form" onsubmit="addTodo(event)">
<div class="input-group">
<div class="input-wrapper">
<input type="text" class="todo-input" id="todoInput" placeholder="What needs to be done?" required>
</div>
<div class="input-options">
<select class="option-select" id="categorySelect">
<option value="personal">Personal</option>
<option value="work">Work</option>
<option value="shopping">Shopping</option>
<option value="health">Health</option>
<option value="other">Other</option>
</select>
<select class="option-select" id="prioritySelect">
<option value="low">Low Priority</option>
<option value="medium">Medium Priority</option>
<option value="high">High Priority</option>
</select>
<input type="date" class="option-select" id="dueDateSelect">
</div>
</div>
<button type="submit" class="add-btn">
<i class="fas fa-plus"></i>
Add Task
</button>
</form>
<div class="filters">
<button class="filter-btn active" onclick="filterTodos('all')">All Tasks</button>
<button class="filter-btn" onclick="filterTodos('active')">Active</button>
<button class="filter-btn" onclick="filterTodos('completed')">Completed</button>
<div class="search-box">
<i class="fas fa-search search-icon"></i>
<input type="text" class="search-input" id="searchInput" placeholder="Search tasks..." oninput="searchTodos()">
</div>
</div>
<ul class="todo-list" id="todoList">
<!-- Todos will be dynamically added here -->
</ul>
<div class="empty-state" id="emptyState" style="display: none;">
<i class="fas fa-clipboard-list"></i>
<h3>No tasks yet</h3>
<p>Add your first task to get started!</p>
</div>
</section>
<aside class="stats-section">
<h2 class="stats-header">Statistics</h2>
<div class="stat-card">
<div class="stat-label">Total Tasks</div>
<div class="stat-value" id="totalTasks">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Completed</div>
<div class="stat-value" id="completedTasks">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Active</div>
<div class="stat-value" id="activeTasks">0</div>
</div>
<div class="stat-card">
<div class="stat-label">Completion Rate</div>
<div class="stat-value" id="completionRate">0%</div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill" style="width: 0%"></div>
</div>
</div>
<div class="export-import">
<button class="export-btn" onclick="exportTodos()">
<i class="fas fa-download"></i> Export
</button>
<button class="import-btn" onclick="document.getElementById('importFile').click()">
<i class="fas fa-upload"></i> Import
</button>
<input type="file" id="importFile" style="display: none;" accept=".json" onchange="importTodos(event)">
</div>
</aside>
</main>
</div>
<div class="keyboard-shortcuts">
<div style="font-weight: 600; margin-bottom: 10px;">Shortcuts</div>
<div class="shortcut-item">
<span>New Task</span>
<span class="shortcut-key">Ctrl+N</span>
</div>
<div class="shortcut-item">
<span>Search</span>
<span class="shortcut-key">Ctrl+F</span>
</div>
<div class="shortcut-item">
<span>Clear All</span>
<span class="shortcut-key">Ctrl+D</span>
</div>
</div>
<div class="built-with">
Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
</div>
<div class="modal" id="editModal">
<div class="modal-content">
<div class="modal-header">Edit Task</div>
<div class="modal-body">
<input type="text" class="modal-input" id="editInput" placeholder="Task text">
<select class="modal-input" id="editCategory">
<option value="personal">Personal</option>
<option value="work">Work</option>
<option value="shopping">Shopping</option>
<option value="health">Health</option>
<option value="other">Other</option>
</select>
<select class="modal-input" id="editPriority">
<option value="low">Low Priority</option>
<option value="medium">Medium Priority</option>
<option value="high">High Priority</option>
</select>
<input type="date" class="modal-input" id="editDueDate">
</div>
<div class="modal-footer">
<button class="modal-btn cancel" onclick="closeEditModal()">Cancel</button>
<button class="modal-btn save" onclick="saveEdit()">Save</button>
</div>
</div>
</div>
<script>
let todos = JSON.parse(localStorage.getItem('todos')) || [];
let currentFilter = 'all';
let editingTodoId = null;
let draggedItem = null;
function initializeApp() {
renderTodos();
updateStats();
setupKeyboardShortcuts();
setupDragAndDrop();
// Check for dark mode preference
if (localStorage.getItem('darkMode') === 'true') {
document.body.classList.add('dark-mode');
document.querySelector('.theme-toggle i').classList.replace('fa-moon', 'fa-sun');
}
}
function addTodo(event) {
event.preventDefault();
const input = document.getElementById('todoInput');
const category = document.getElementById('categorySelect').value;
const priority = document.getElementById('prioritySelect').value;
const dueDate = document.getElementById('dueDateSelect').value;
const todo = {
id: Date.now(),
text: input.value,
completed: false,
category: category,
priority: priority,
dueDate: dueDate,
createdAt: new Date().toISOString()
};
todos.unshift(todo);
saveTodos();
renderTodos();
updateStats();
// Reset form
input.value = '';
document.getElementById('dueDateSelect').value = '';
// Animation
showNotification('Task added successfully!');
}
function toggleTodo(id) {
const todo = todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
saveTodos();
renderTodos();
updateStats();
}
}
function deleteTodo(id) {
todos = todos.filter(t => t.id !== id);
saveTodos();
renderTodos();
updateStats();
showNotification('Task deleted!');
}
function editTodo(id) {
const todo = todos.find(t => t.id === id);
if (todo) {
editingTodoId = id;
document.getElementById('editInput').value = todo.text;
document.getElementById('editCategory').value = todo.category;
document.getElementById('editPriority').value = todo.priority;
document.getElementById('editDueDate').value = todo.dueDate || '';
document.getElementById('editModal').classList.add('active');
}
}
function closeEditModal() {
document.getElementById('editModal').classList.remove('active');
editingTodoId = null;
}
function saveEdit() {
const todo = todos.find(t => t.id === editingTodoId);
if (todo) {
todo.text = document.getElementById('editInput').value;
todo.category = document.getElementById('editCategory').value;
todo.priority = document.getElementById('editPriority').value;
todo.dueDate = document.getElementById('editDueDate').value;
saveTodos();
renderTodos();
updateStats();
closeEditModal();
showNotification('Task updated!');
}
}
function filterTodos(filter) {
currentFilter = filter;
// Update active button
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
renderTodos();
}
function searchTodos() {
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
renderTodos(searchTerm);
}
function renderTodos(searchTerm = '') {
const todoList = document.getElementById('todoList');
const emptyState = document.getElementById('emptyState');
let filteredTodos = todos;
// Apply filter
if (currentFilter === 'active') {
filteredTodos = todos.filter(t => !t.completed);
} else if (currentFilter === 'completed') {
filteredTodos = todos.filter(t => t.completed);
}
// Apply search
if (searchTerm) {
filteredTodos = filteredTodos.filter(t =>
t.text.toLowerCase().includes(searchTerm) ||
t.category.toLowerCase().includes(searchTerm)
);
}
if (filteredTodos.length === 0) {
todoList.style.display = 'none';
emptyState.style.display = 'block';
} else {
todoList.style.display = 'block';
emptyState.style.display = 'none';
todoList.innerHTML = filteredTodos.map(todo => `
<li class="todo-item ${todo.completed ? 'completed' : ''}" draggable="true" data-id="${todo.id}">
<div class="checkbox-wrapper">
<input type="checkbox" class="todo-checkbox"
${todo.completed ? 'checked' : ''}
onchange="toggleTodo(${todo.id})">
</div>
<div class="todo-content">
<div class="todo-text">${escapeHtml(todo.text)}</div>
<div class="todo-meta">
<span class="todo-category">${todo.category}</span>
<span class="todo-priority priority-${todo.priority}">${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 edit" 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>
</li>
`).join('');
}
setupDragAndDrop();
}
function setupDragAndDrop() {
const items = document.querySelectorAll('.todo-item');
items.forEach(item => {
item.addEventListener('dragstart', handleDragStart);
item.addEventListener('dragend', handleDragEnd);
item.addEventListener('dragover', handleDragOver);
item.addEventListener('drop', handleDrop);
});
}
function handleDragStart(e) {
draggedItem = this;
this.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
}
function handleDragEnd(e) {
this.classList.remove('dragging');
draggedItem = null;
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'move';
const afterElement = getDragAfterElement(document.getElementById('todoList'), e.clientY);
if (afterElement == null) {
document.getElementById('todoList').appendChild(draggedItem);
} else {
document.getElementById('todoList').insertBefore(draggedItem, afterElement);
}
return false;
}
function handleDrop(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
// Update todos order
const newOrder = Array.from(document.querySelectorAll('.todo-item')).map(item =>
parseInt(item.dataset.id)
);
const reorderedTodos = [];
newOrder.forEach(id => {
const todo = todos.find(t => t.id === id);
if (todo) reorderedTodos.push(todo);
});
todos = reorderedTodos;
saveTodos();
return false;
}
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.todo-item:not(.dragging)')];
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}
function updateStats() {
const total = todos.length;
const completed = todos.filter(t => t.completed).length;
const active = total - completed;
const rate = total > 0 ? Math.round((completed / total) * 100) : 0;
document.getElementById('totalTasks').textContent = total;
document.getElementById('completedTasks').textContent = completed;
document.getElementById('activeTasks').textContent = active;
document.getElementById('completionRate').textContent = rate + '%';
document.getElementById('progressFill').style.width = rate + '%';
}
function toggleTheme() {
document.body.classList.toggle('dark-mode');
const isDark = document.body.classList.contains('dark-mode');
localStorage.setItem('darkMode', isDark);
const icon = document.querySelector('.theme-toggle i');
if (isDark) {
icon.classList.replace('fa-moon', 'fa-sun');
} else {
icon.classList.replace('fa-sun', 'fa-moon');
}
}
function exportTodos() {
const dataStr = JSON.stringify(todos, null, 2);
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
const exportFileDefaultName = `todos_${new Date().toISOString().split('T')[0]}.json`;
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
showNotification('Tasks exported!');
}
function importTodos(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
try {
todos = JSON.parse(e.target.result);
saveTodos();
renderTodos();
updateStats();
showNotification('Tasks imported successfully!');
} catch (error) {
showNotification('Error importing file!', 'error');
}
};
reader.readAsText(file);
}
}
function saveTodos() {
localStorage.setItem('todos', JSON.stringify(todos));
}
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}
function formatDate(dateStr) {
const date = new Date(dateStr);
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' });
}
}
function showNotification(message, type = 'success') {
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
background: ${type === 'success' ? 'var(--success)' : 'var(--danger)'};
color: white;
border-radius: 10px;
box-shadow: 0 5px 15px var(--shadow);
z-index: 2000;
animation: slideIn 0.3s ease;
`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'fadeOut 0.3s ease';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
function setupKeyboardShortcuts() {
document.addEventListener('keydown', (e) => {
// Ctrl+N - New task
if (e.ctrlKey && e.key === 'n') {
e.preventDefault();
document.getElementById('todoInput').focus();
}
// Ctrl+F - Search
if (e.ctrlKey && e.key === 'f') {
e.preventDefault();
document.getElementById('searchInput').focus();
}
// Ctrl+D - Clear completed
if (e.ctrlKey && e.key === 'd') {
e.preventDefault();
todos = todos.filter(t => !t.completed);
saveTodos();
renderTodos();
updateStats();
showNotification('Completed tasks cleared!');
}
// Escape - Close modal
if (e.key === 'Escape') {
closeEditModal();
}
});
}
// Initialize app on load
document.addEventListener('DOMContentLoaded', initializeApp);
</script>
</body>
</html>