anycoder-ab5b78bc / index.html
akhaliq's picture
akhaliq HF Staff
Upload folder using huggingface_hub
c2c74f9 verified
raw
history blame
38.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FlowTask - Advanced Todo Manager</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--apple-blue: #007AFF;
--apple-blue-dark: #0051D5;
--apple-green: #34C759;
--apple-red: #FF3B30;
--apple-orange: #FF9500;
--apple-yellow: #FFCC00;
--apple-purple: #AF52DE;
--apple-gray: #8E8E93;
--apple-gray-light: #C7C7CC;
--apple-gray-ultralight: #F2F2F7;
--apple-background: #FFFFFF;
--apple-surface: #F9F9F9;
--apple-border: rgba(0, 0, 0, 0.1);
--apple-text-primary: #000000;
--apple-text-secondary: #3C3C43;
--apple-text-tertiary: #3C3C4399;
--shadow-light: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-medium: 0 4px 6px rgba(0, 0, 0, 0.07);
--shadow-heavy: 0 10px 15px rgba(0, 0, 0, 0.1);
--radius-small: 8px;
--radius-medium: 12px;
--radius-large: 20px;
--radius-xl: 28px;
--transition-fast: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
--transition-medium: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
[data-theme="dark"] {
--apple-blue: #0A84FF;
--apple-blue-dark: #409CFF;
--apple-green: #30D158;
--apple-red: #FF453A;
--apple-orange: #FF9F0A;
--apple-yellow: #FFD60A;
--apple-purple: #BF5AF2;
--apple-gray: #8E8E93;
--apple-gray-light: #48484A;
--apple-gray-ultralight: #1C1C1E;
--apple-background: #000000;
--apple-surface: #1C1C1E;
--apple-border: rgba(255, 255, 255, 0.08);
--apple-text-primary: #FFFFFF;
--apple-text-secondary: #EBEBF5;
--apple-text-tertiary: #EBEBF599;
--shadow-light: 0 1px 3px rgba(0, 0, 0, 0.3);
--shadow-medium: 0 4px 6px rgba(0, 0, 0, 0.2);
--shadow-heavy: 0 10px 15px rgba(0, 0, 0, 0.3);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', system-ui, sans-serif;
background: var(--apple-surface);
min-height: 100vh;
color: var(--apple-text-primary);
transition: background-color var(--transition-medium);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.apple-bg {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, var(--apple-surface) 0%, var(--apple-background) 100%);
z-index: -1;
}
[data-theme="dark"] .apple-bg {
background: linear-gradient(135deg, #000000 0%, #1C1C1E 100%);
}
.apple-blur {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(ellipse at top, rgba(0, 122, 255, 0.1) 0%, transparent 50%);
z-index: -1;
opacity: 0.5;
}
[data-theme="dark"] .apple-blur {
background: radial-gradient(ellipse at top, rgba(10, 132, 255, 0.15) 0%, transparent 50%);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
text-align: center;
padding: 40px 0;
position: relative;
}
.header-content {
display: inline-block;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
background: rgba(255, 255, 255, 0.7);
border-radius: var(--radius-xl);
padding: 30px 50px;
border: 1px solid var(--apple-border);
box-shadow: var(--shadow-medium);
}
[data-theme="dark"] .header-content {
background: rgba(28, 28, 30, 0.7);
}
h1 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 8px;
letter-spacing: -0.02em;
background: linear-gradient(135deg, var(--apple-blue) 0%, var(--apple-purple) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
font-size: 1rem;
color: var(--apple-text-secondary);
font-weight: 400;
margin-bottom: 20px;
}
.header-actions {
display: flex;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
}
.apple-btn {
background: var(--apple-background);
border: 1px solid var(--apple-border);
color: var(--apple-text-primary);
padding: 10px 20px;
border-radius: var(--radius-large);
cursor: pointer;
transition: all var(--transition-fast);
font-size: 0.9rem;
font-weight: 500;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
display: inline-flex;
align-items: center;
gap: 8px;
}
.apple-btn:hover {
background: var(--apple-gray-ultralight);
transform: scale(1.02);
box-shadow: var(--shadow-medium);
}
[data-theme="dark"] .apple-btn:hover {
background: var(--apple-gray-light);
}
.main-content {
display: grid;
grid-template-columns: 1fr 380px;
gap: 30px;
margin-top: 40px;
}
@media (max-width: 1024px) {
.main-content {
grid-template-columns: 1fr;
}
}
.apple-card {
background: var(--apple-background);
border-radius: var(--radius-large);
padding: 30px;
box-shadow: var(--shadow-light);
border: 1px solid var(--apple-border);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.input-group {
display: flex;
gap: 12px;
margin-bottom: 24px;
}
.apple-input {
flex: 1;
padding: 14px 20px;
border: 1px solid var(--apple-border);
border-radius: var(--radius-medium);
font-size: 1rem;
transition: all var(--transition-fast);
background: var(--apple-surface);
color: var(--apple-text-primary);
}
.apple-input:focus {
outline: none;
border-color: var(--apple-blue);
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
}
.apple-input::placeholder {
color: var(--apple-text-tertiary);
}
.input-options {
display: flex;
gap: 12px;
margin-bottom: 24px;
flex-wrap: wrap;
}
.apple-select {
padding: 10px 16px;
border: 1px solid var(--apple-border);
border-radius: var(--radius-small);
background: var(--apple-surface);
color: var(--apple-text-primary);
cursor: pointer;
transition: all var(--transition-fast);
font-size: 0.9rem;
}
.apple-select:focus {
outline: none;
border-color: var(--apple-blue);
}
.apple-btn-primary {
background: var(--apple-blue);
color: white;
border: none;
padding: 14px 28px;
border-radius: var(--radius-medium);
font-weight: 600;
transition: all var(--transition-fast);
display: inline-flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.apple-btn-primary:hover {
background: var(--apple-blue-dark);
transform: translateY(-1px);
box-shadow: var(--shadow-medium);
}
.apple-search {
position: relative;
margin-bottom: 24px;
}
.apple-search-input {
width: 100%;
padding: 12px 45px 12px 20px;
border: 1px solid var(--apple-border);
border-radius: var(--radius-medium);
background: var(--apple-surface);
color: var(--apple-text-primary);
transition: all var(--transition-fast);
}
.apple-search-input:focus {
outline: none;
border-color: var(--apple-blue);
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
}
.apple-search-icon {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
color: var(--apple-gray);
pointer-events: none;
}
.apple-tabs {
display: flex;
gap: 8px;
margin-bottom: 24px;
padding: 4px;
background: var(--apple-surface);
border-radius: var(--radius-medium);
}
.apple-tab {
flex: 1;
padding: 10px 16px;
background: transparent;
border: none;
color: var(--apple-text-secondary);
cursor: pointer;
transition: all var(--transition-fast);
font-weight: 500;
border-radius: var(--radius-small);
font-size: 0.9rem;
}
.apple-tab:hover {
background: var(--apple-gray-ultralight);
}
[data-theme="dark"] .apple-tab:hover {
background: var(--apple-gray-light);
}
.apple-tab.active {
background: var(--apple-background);
color: var(--apple-blue);
box-shadow: var(--shadow-light);
}
.todo-list {
list-style: none;
max-height: 500px;
overflow-y: auto;
padding-right: 5px;
}
.todo-list::-webkit-scrollbar {
width: 6px;
}
.todo-list::-webkit-scrollbar-track {
background: transparent;
}
.todo-list::-webkit-scrollbar-thumb {
background: var(--apple-gray-light);
border-radius: 10px;
}
.todo-item {
background: var(--apple-surface);
border: 1px solid var(--apple-border);
border-radius: var(--radius-medium);
padding: 16px;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 16px;
transition: all var(--transition-fast);
cursor: move;
position: relative;
}
.todo-item:hover {
transform: translateX(4px);
box-shadow: var(--shadow-medium);
border-color: var(--apple-gray-light);
}
.todo-item.dragging {
opacity: 0.5;
transform: rotate(2deg);
}
.todo-item.completed {
opacity: 0.6;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: var(--apple-text-tertiary);
}
.apple-checkbox {
width: 22px;
height: 22px;
cursor: pointer;
accent-color: var(--apple-blue);
}
.todo-content {
flex: 1;
}
.todo-text {
font-size: 1rem;
margin-bottom: 6px;
color: var(--apple-text-primary);
font-weight: 400;
}
.todo-meta {
display: flex;
gap: 10px;
align-items: center;
font-size: 0.85rem;
color: var(--apple-text-tertiary);
}
.priority-badge {
padding: 3px 10px;
border-radius: var(--radius-small);
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.priority-high {
background: rgba(255, 59, 48, 0.15);
color: var(--apple-red);
}
.priority-medium {
background: rgba(255, 149, 0, 0.15);
color: var(--apple-orange);
}
.priority-low {
background: rgba(0, 122, 255, 0.15);
color: var(--apple-blue);
}
.category-tag {
padding: 3px 10px;
border-radius: var(--radius-small);
font-size: 0.75rem;
background: rgba(175, 82, 222, 0.15);
color: var(--apple-purple);
font-weight: 500;
}
.todo-actions {
display: flex;
gap: 8px;
}
.action-btn {
width: 36px;
height: 36px;
border: none;
background: transparent;
color: var(--apple-gray);
cursor: pointer;
border-radius: var(--radius-small);
transition: all var(--transition-fast);
display: flex;
align-items: center;
justify-content: center;
}
.action-btn:hover {
background: var(--apple-gray-ultralight);
color: var(--apple-blue);
}
[data-theme="dark"] .action-btn:hover {
background: var(--apple-gray-light);
}
.action-btn.delete:hover {
color: var(--apple-red);
}
.sidebar {
display: flex;
flex-direction: column;
gap: 20px;
}
.stats-card {
background: var(--apple-background);
border-radius: var(--radius-large);
padding: 28px;
box-shadow: var(--shadow-light);
border: 1px solid var(--apple-border);
}
.stats-title {
font-size: 1.2rem;
font-weight: 600;
margin-bottom: 20px;
color: var(--apple-text-primary);
display: flex;
align-items: center;
gap: 8px;
}
.stat-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid var(--apple-border);
}
.stat-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.stat-label {
color: var(--apple-text-secondary);
font-size: 0.9rem;
font-weight: 400;
}
.stat-value {
font-size: 1.8rem;
font-weight: 700;
color: var(--apple-blue);
}
.progress-bar {
width: 100%;
height: 6px;
background: var(--apple-gray-ultralight);
border-radius: 10px;
overflow: hidden;
margin-top: 10px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--apple-blue) 0%, var(--apple-purple) 100%);
border-radius: 10px;
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
.apple-btn-danger {
background: var(--apple-red);
color: white;
border: none;
padding: 14px 28px;
border-radius: var(--radius-medium);
font-weight: 600;
transition: all var(--transition-fast);
display: inline-flex;
align-items: center;
gap: 8px;
cursor: pointer;
width: 100%;
justify-content: center;
}
.apple-btn-danger:hover {
background: #E6362F;
transform: translateY(-1px);
box-shadow: var(--shadow-medium);
}
.empty-state {
text-align: center;
padding: 80px 20px;
color: var(--apple-text-tertiary);
}
.empty-icon {
font-size: 4rem;
margin-bottom: 20px;
opacity: 0.3;
color: var(--apple-gray);
}
.empty-state h3 {
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 8px;
color: var(--apple-text-secondary);
}
.apple-toast {
position: fixed;
bottom: 30px;
right: 30px;
background: var(--apple-background);
padding: 16px 24px;
border-radius: var(--radius-large);
box-shadow: var(--shadow-heavy);
display: flex;
align-items: center;
gap: 12px;
transform: translateX(400px);
transition: transform var(--transition-medium);
z-index: 1000;
border: 1px solid var(--apple-border);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.apple-toast.show {
transform: translateX(0);
}
.toast-icon {
font-size: 1.3rem;
}
.apple-toast.success .toast-icon {
color: var(--apple-green);
}
.apple-toast.error .toast-icon {
color: var(--apple-red);
}
.apple-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
display: none;
align-items: center;
justify-content: center;
z-index: 2000;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.apple-modal.active {
display: flex;
}
.modal-content {
background: var(--apple-background);
border-radius: var(--radius-xl);
padding: 32px;
max-width: 500px;
width: 90%;
box-shadow: var(--shadow-heavy);
border: 1px solid var(--apple-border);
animation: modalSlideIn var(--transition-medium);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.modal-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--apple-text-primary);
}
.modal-close {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--apple-gray);
transition: color var(--transition-fast);
width: 32px;
height: 32px;
border-radius: var(--radius-small);
display: flex;
align-items: center;
justify-content: center;
}
.modal-close:hover {
color: var(--apple-red);
background: var(--apple-gray-ultralight);
}
[data-theme="dark"] .modal-close:hover {
background: var(--apple-gray-light);
}
.modal-body {
margin-bottom: 24px;
}
.modal-input {
width: 100%;
padding: 14px;
border: 1px solid var(--apple-border);
border-radius: var(--radius-medium);
background: var(--apple-surface);
color: var(--apple-text-primary);
margin-bottom: 16px;
transition: all var(--transition-fast);
}
.modal-input:focus {
outline: none;
border-color: var(--apple-blue);
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
}
.apple-btn-secondary {
background: var(--apple-gray-ultralight);
color: var(--apple-text-primary);
border: 1px solid var(--apple-border);
padding: 12px 24px;
border-radius: var(--radius-medium);
font-weight: 500;
transition: all var(--transition-fast);
cursor: pointer;
}
.apple-btn-secondary:hover {
background: var(--apple-gray-light);
color: var(--apple-text-primary);
}
[data-theme="dark"] .apple-btn-secondary {
background: var(--apple-gray-light);
}
[data-theme="dark"] .apple-btn-secondary:hover {
background: var(--apple-gray);
}
.built-with {
color: var(--apple-text-secondary);
text-decoration: none;
transition: all var(--transition-fast);
display: inline-block;
margin-top: 12px;
font-size: 0.9rem;
opacity: 0.8;
}
.built-with:hover {
opacity: 1;
color: var(--apple-blue);
}
@keyframes modalSlideIn {
from {
opacity: 0;
transform: scale(0.95) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
@media (max-width: 640px) {
h1 {
font-size: 2rem;
}
.header-content {
padding: 20px 30px;
}
.input-group {
flex-direction: column;
}
.apple-tabs {
overflow-x: auto;
}
.todo-item {
flex-wrap: wrap;
}
.todo-actions {
width: 100%;
justify-content: flex-end;
}
.apple-toast {
right: 20px;
left: 20px;
bottom: 20px;
}
}
/* Animations */
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-10px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
</style>
</head>
<body>
<div class="apple-bg"></div>
<div class="apple-blur"></div>
<header>
<div class="header-content">
<h1>FlowTask</h1>
<p class="subtitle">Organize your life with elegance</p>
<div class="header-actions">
<button class="apple-btn" onclick="toggleTheme()">
<i class="fas fa-moon"></i> <span id="themeText">Dark</span>
</button>
<button class="apple-btn" onclick="exportTodos()">
<i class="fas fa-download"></i> Export
</button>
<button class="apple-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>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">
Built with anycoder
</a>
</div>
</header>
<div class="container">
<div class="main-content">
<section class="apple-card">
<div class="input-group">
<input type="text" class="apple-input" id="todoInput" placeholder="What needs to be done today?" onkeypress="handleKeyPress(event)">
<button class="apple-btn-primary" onclick="addTodo()">
<i class="fas fa-plus"></i> Add Task
</button>
</div>
<div class="input-options">
<select class="apple-select" id="prioritySelect">
<option value="low">Low</option>
<option value="medium" selected>Medium</option>
<option value="high">High</option>
</select>
<select class="apple-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="education">Education</option>
<option value="other">Other</option>
</select>
<input type="date" class="apple-select" id="dueDateInput">
</div>
<div class="apple-search">
<input type="text" class="apple-search-input" id="searchInput" placeholder="Search tasks..." oninput="searchTodos()">
<i class="fas fa-search apple-search-icon"></i>
</div>
<div class="apple-tabs">
<button class="apple-tab active" onclick="filterTodos('all')">All Tasks</button>
<button class="apple-tab" onclick="filterTodos('active')">Active</button>
<button class="apple-tab" onclick="filterTodos('completed')">Completed</button>
<button class="apple-tab" onclick="filterTodos('today')">Due Today</button>
</div>
<ul class="todo-list" id="todoList"></ul>
<div class="empty-state" id="emptyState" style="display: none;">
<div class="empty-icon">
<i class="fas fa-clipboard-list"></i>
</div>
<h3>No tasks yet</h3>
<p>Start adding tasks to organize your day!</p>
</div>
</section>
<aside class="sidebar">
<div class="stats-card">
<h3 class="stats-title">📊 Statistics</h3>
<div class="stat-item">
<span class="stat-label">Total Tasks</span>
<span class="stat-value" id="totalTasks">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Completed</span>
<span class="stat-value" id="completedTasks">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Active</span>
<span class="stat-value" id="activeTasks">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Progress</span>
<div style="flex: 1; margin-left: 20px;">
<div class="progress-bar">
<div class="progress-fill" id="progressFill" style="width: 0%"></div>
</div>
</div>
</div>
</div>
<div class="stats-card">
<h3 class="stats-title">🏆 Productivity</h3>
<div class="stat-item">
<span class="stat-label">Tasks Completed Today</span>
<span class="stat-value" id="todayCompleted">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Streak Days</span>
<span class="stat-value" id="streakDays">0</span>
</div>
<div class="stat-item">
<span class="stat-label">Completion Rate</span>
<span class="stat-value" id="completionRate">0%</span>
</div>
</div>
<button class="apple-btn-danger" onclick="clearCompleted()">
<i class="fas fa-trash"></i> Clear Completed
</button>
</aside>
</div>
</div>
<div class="apple-toast" id="toast">
<i class="toast-icon fas"></i>
<span id="toastMessage"></span>
</div>
<div class="apple-modal" id="editModal">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Edit Task</h2>
<button class="modal-close" onclick="closeEditModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<input type="text" class="modal-input" id="editInput" placeholder="Task description">
<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>
<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="education">Education</option>
<option value="other">Other</option>
</select>
<input type="date" class="modal-input" id="editDueDate">
</div>
<div class="modal-footer">
<button class="apple-btn-secondary" onclick="closeEditModal()">Cancel</button>
<button class="apple-btn-primary" onclick="saveEdit()">
<i class="fas fa-save"></i> Save Changes
</button>
</div>
</div>
</div>
<script>
let todos = JSON.parse(localStorage.getItem('todos')) || [];
let currentFilter = 'all';
let editingTodo = null;
let draggedItem = null;
// Initialize
document.addEventListener('DOMContentLoaded', () => {
renderTodos();
updateStats();
// Set today's date as default
const today = new Date().toISOString().split('T')[0];
document.getElementById('dueDateInput').value = today;
// Check for saved theme
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
updateThemeButton(savedTheme);
});
function addTodo() {
const input = document.getElementById('todoInput');
const text = input.value.trim();
if (text === '') {
showToast('Please enter a task', 'error');
return;
}
const todo = {
id: Date.now(),
text: text,
completed: false,
priority: document.getElementById('prioritySelect').value,
category: document.getElementById('categorySelect').value,
dueDate: document.getElementById('dueDateInput').value,
createdAt: new Date().toISOString(),
completedAt: null
};
todos.unshift(todo);
saveTodos();
renderTodos();
updateStats();
input.value = '';
showToast('Task added successfully!', 'success');
// Reset date to today
const today = new Date().toISOString().split('T')[0];
document.getElementById('dueDateInput').value = today;
}
function handleKeyPress(event) {
if (event.key === 'Enter') {
addTodo();
}
}
function toggleTodo(id) {
const todo = todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
todo.completedAt = todo.completed ? new Date().toISOString() : null;
saveTodos();
renderTodos();
updateStats();
if (todo.completed) {
showToast('Task completed! 🎉', 'success');
updateStreak();
}
}
}
function deleteTodo(id) {
todos = todos.filter(t => t.id !== id);
saveTodos();
renderTodos();
updateStats();
showToast('Task deleted', 'success');
}
function editTodo(id) {
const todo = todos.find(t => t.id === id);
if (todo) {
editingTodo = todo;
document.getElementById('editInput').value = todo.text;
document.getElementById('editPriority').value = todo.priority;
document.getElementById('editCategory').value = todo.category;
document.getElementById('editDueDate').value = todo.dueDate;
document.getElementById('editModal').classList.add('active');
}
}
function saveEdit() {
if (editingTodo) {
editingTodo.text = document.getElementById('editInput').value;
editingTodo.priority = document.getElementById('editPriority').value;
editingTodo.category = document.getElementById('editCategory').value;
editingTodo.dueDate = document.getElementById('editDueDate').value;
saveTodos();
renderTodos();
updateStats();
closeEditModal();
showToast('Task updated successfully!', 'success');
}
}
function closeEditModal() {
document.getElementById('editModal').classList.remove('active');
editingTodo = null;
}
function filterTodos(filter) {
currentFilter = filter;
// Update active tab
document.querySelectorAll('.apple-tab').forEach(tab => {
tab.classList.remove('active');
});
event.target.classList.add('active');
renderTodos();
}
function searchTodos() {
renderTodos();
}
function renderTodos() {
const todoList = document.getElementById('todoList');
const emptyState = document.getElementById('emptyState');
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
let filteredTodos = todos;
// Apply filter
if (currentFilter === 'active') {
filteredTodos = todos.filter(t => !t.completed);
} else if (currentFilter === 'completed') {
filteredTodos = todos.filter(t => t.completed);
} else if (currentFilter === 'today') {
const today = new Date().toISOString().split('T')[0];
filteredTodos = todos.filter(t => t.dueDate === today);
}
// 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';
return;
}
todoList.style.display = 'block';
emptyState.style.display = 'none';
todoList.innerHTML = filteredTodos.map(todo => {
const isOverdue = new Date(todo.dueDate) < new Date() && !todo.completed;
const dueDateFormatted = new Date(todo.dueDate).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric'
});
return `
<li class="todo-item ${todo.completed ? 'completed' : ''}"
draggable="true"
ondragstart="handleDragStart(event, ${todo.id})"
ondragover="handleDragOver(event)"
ondrop="handleDrop(event, ${todo.id})"
ondragend="handleDragEnd(event)">
<input type="checkbox"
class="apple-checkbox"
${todo.completed ? 'checked' : ''}
onchange="toggleTodo(${todo.id})">
<div class="todo-content">
<div class="todo-text">${todo.text}</div>
<div class="todo-meta">
<span class="priority-badge priority-${todo.priority}">
${todo.priority}
</span>
<span class="category-tag">
<i class="fas fa-tag"></i> ${todo.category}
</span>
<span style="color: ${isOverdue ? 'var(--apple-red)' : 'var(--apple-text-tertiary)'}">
<i class="fas fa-calendar"></i> ${dueDateFormatted}
${isOverdue ? ' (Overdue)' : ''}
</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>
</li>
`;
}).join('');
}
function handleDragStart(event, id) {
draggedItem = id;
event.dataTransfer.effectAllowed = 'move';
event.target.classList.add('dragging');
}
function handleDragOver(event) {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}
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');
draggedItem = null;
}
function clearCompleted() {
const completedCount = todos.filter(t => t.completed).length;
if (completedCount === 0) {
showToast('No completed tasks to clear', 'error');
return;
}
todos = todos.filter(t => !t.completed);
saveTodos();
renderTodos();
updateStats();
showToast(`Cleared ${completedCount} completed tasks`, 'success');
}
function updateStats() {
const total = todos.length;
const completed = todos.filter(t => t.completed).length;
const active = total - completed;
const today = new Date().toISOString().split('T')[0];
const todayCompleted = todos.filter(t =>
t.completed && t.completedAt && t.completedAt.split('T')[0] === today
).length;
document.getElementById('totalTasks').textContent = total;
document.getElementById('completedTasks').textContent = completed;
document.getElementById('activeTasks').textContent = active;
document.getElementById('todayCompleted').textContent = todayCompleted;
const progress = total > 0 ? (completed / total * 100) : 0;
document.getElementById('progressFill').style.width = `${progress}%`;
const completionRate = total > 0 ? Math.round(progress) : 0;
document.getElementById('completionRate').textContent = `${completionRate}%`;
updateStreak();
}
function updateStreak() {
// Simple streak calculation - can be enhanced
const today = new Date();
let streak = 0;
for (let i = 0; i < 30; i++) {
const checkDate = new Date(today);
checkDate.setDate(today.getDate() - i);
const dateStr = checkDate.toISOString().split('T')[0];
const hasCompletedOnDate = todos.some(t =>
t.completedAt && t.completedAt.split('T')[0] === dateStr
);
if (hasCompletedOnDate) {
streak++;
} else if (i > 0) {
break;
}
}
document.getElementById('streakDays').textContent = streak;
}
function toggleTheme() {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
updateThemeButton(newTheme);
}
function updateThemeButton(theme) {
const themeText = document.getElementById('themeText');
const themeIcon = document.querySelector('.apple-btn i');
if (theme === 'dark') {
themeText.textContent = 'Light';
themeIcon.className = 'fas fa-sun';
} else {
themeText.textContent = 'Dark';
themeIcon.className = 'fas fa-moon';
}
}
function exportTodos() {
const dataStr = JSON.stringify(todos, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `todos-${new Date().toISOString().split('T')[0]}.json`;
link.click();
URL.revokeObjectURL(url);
showToast('Tasks exported successfully!', 'success');
}
function importTodos(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const imported = JSON.parse(e.target.result);
todos = [...todos, ...imported];
saveTodos();
renderTodos();
updateStats();
showToast('Tasks imported successfully!', 'success');
} catch (error) {
showToast('Invalid file format', 'error');
}
};
reader.readAsText(file);
event.target.value = '';
}
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
const toastIcon = toast.querySelector('.toast-icon');
toastMessage.textContent = message;
toast.className = `apple-toast ${type} show`;
if (type === 'success') {
toastIcon.className = 'toast-icon fas fa-check-circle';
} else if (type === 'error') {
toastIcon.className = 'toast-icon fas fa-exclamation-circle';
}
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
function saveTodos() {
localStorage.setItem('todos', JSON.stringify(todos));
}
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.ctrlKey || e.metaKey) {
switch(e.key) {
case 'k':
e.preventDefault();
document.getElementById('searchInput').focus();
break;
case 'n':
e.preventDefault();
document.getElementById('todoInput').focus();
break;
case 'd':
e.preventDefault();
toggleTheme();
break;
}
}
});
</