Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>TaskFlow Pro - Advanced Task Management</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: #1f2937; | |
| --light: #f3f4f6; | |
| --white: #ffffff; | |
| --gray: #6b7280; | |
| --border: #e5e7eb; | |
| --shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
| --shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1); | |
| --radius: 12px; | |
| --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| color: var(--dark); | |
| transition: var(--transition); | |
| } | |
| body.dark-mode { | |
| --dark: #f3f4f6; | |
| --light: #1f2937; | |
| --white: #111827; | |
| --border: #374151; | |
| --gray: #9ca3af; | |
| background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%); | |
| } | |
| .container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| } | |
| header { | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-radius: var(--radius); | |
| padding: 20px 30px; | |
| margin-bottom: 30px; | |
| box-shadow: var(--shadow-lg); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| gap: 20px; | |
| animation: slideDown 0.5s ease-out; | |
| } | |
| body.dark-mode header { | |
| background: rgba(17, 24, 39, 0.95); | |
| } | |
| .logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| font-size: 24px; | |
| font-weight: bold; | |
| color: var(--primary); | |
| } | |
| .logo i { | |
| font-size: 32px; | |
| animation: pulse 2s infinite; | |
| } | |
| .header-actions { | |
| display: flex; | |
| gap: 15px; | |
| align-items: center; | |
| } | |
| .theme-toggle { | |
| background: var(--primary); | |
| color: white; | |
| border: none; | |
| padding: 10px 15px; | |
| border-radius: 50px; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| font-size: 18px; | |
| } | |
| .theme-toggle:hover { | |
| background: var(--primary-dark); | |
| transform: rotate(180deg); | |
| } | |
| .stats-container { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); | |
| gap: 20px; | |
| margin-bottom: 30px; | |
| } | |
| .stat-card { | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-radius: var(--radius); | |
| padding: 20px; | |
| box-shadow: var(--shadow); | |
| transition: var(--transition); | |
| animation: fadeInUp 0.5s ease-out; | |
| animation-fill-mode: both; | |
| } | |
| body.dark-mode .stat-card { | |
| background: rgba(17, 24, 39, 0.95); | |
| } | |
| .stat-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .stat-card:nth-child(1) { animation-delay: 0.1s; } | |
| .stat-card:nth-child(2) { animation-delay: 0.2s; } | |
| .stat-card:nth-child(3) { animation-delay: 0.3s; } | |
| .stat-card:nth-child(4) { animation-delay: 0.4s; } | |
| .stat-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 10px; | |
| } | |
| .stat-icon { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 20px; | |
| } | |
| .stat-icon.blue { background: rgba(99, 102, 241, 0.1); color: var(--primary); } | |
| .stat-icon.green { background: rgba(16, 185, 129, 0.1); color: var(--success); } | |
| .stat-icon.yellow { background: rgba(245, 158, 11, 0.1); color: var(--warning); } | |
| .stat-icon.red { background: rgba(239, 68, 68, 0.1); color: var(--danger); } | |
| .stat-value { | |
| font-size: 32px; | |
| font-weight: bold; | |
| color: var(--dark); | |
| } | |
| .stat-label { | |
| color: var(--gray); | |
| font-size: 14px; | |
| } | |
| .main-content { | |
| display: grid; | |
| grid-template-columns: 350px 1fr; | |
| gap: 30px; | |
| animation: fadeIn 0.5s ease-out; | |
| } | |
| .sidebar { | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-radius: var(--radius); | |
| padding: 25px; | |
| box-shadow: var(--shadow); | |
| height: fit-content; | |
| position: sticky; | |
| top: 20px; | |
| } | |
| body.dark-mode .sidebar { | |
| background: rgba(17, 24, 39, 0.95); | |
| } | |
| .add-task-form { | |
| margin-bottom: 30px; | |
| } | |
| .form-group { | |
| margin-bottom: 15px; | |
| } | |
| .form-group label { | |
| display: block; | |
| margin-bottom: 5px; | |
| font-weight: 500; | |
| color: var(--dark); | |
| } | |
| .form-control { | |
| width: 100%; | |
| padding: 10px 15px; | |
| border: 2px solid var(--border); | |
| border-radius: 8px; | |
| font-size: 14px; | |
| transition: var(--transition); | |
| background: var(--white); | |
| color: var(--dark); | |
| } | |
| body.dark-mode .form-control { | |
| background: var(--dark); | |
| color: var(--light); | |
| } | |
| .form-control:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); | |
| } | |
| .priority-select { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 10px; | |
| } | |
| .priority-btn { | |
| padding: 8px; | |
| border: 2px solid var(--border); | |
| background: var(--white); | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| font-size: 12px; | |
| font-weight: 500; | |
| } | |
| body.dark-mode .priority-btn { | |
| background: var(--dark); | |
| } | |
| .priority-btn.active.low { | |
| background: var(--success); | |
| color: white; | |
| border-color: var(--success); | |
| } | |
| .priority-btn.active.medium { | |
| background: var(--warning); | |
| color: white; | |
| border-color: var(--warning); | |
| } | |
| .priority-btn.active.high { | |
| background: var(--danger); | |
| color: white; | |
| border-color: var(--danger); | |
| } | |
| .category-select { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 10px; | |
| } | |
| .category-btn { | |
| padding: 8px; | |
| border: 2px solid var(--border); | |
| background: var(--white); | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| font-size: 12px; | |
| } | |
| body.dark-mode .category-btn { | |
| background: var(--dark); | |
| } | |
| .category-btn.active { | |
| background: var(--primary); | |
| color: white; | |
| border-color: var(--primary); | |
| } | |
| .btn-primary { | |
| width: 100%; | |
| padding: 12px; | |
| background: linear-gradient(135deg, var(--primary), var(--secondary)); | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| font-size: 16px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 20px rgba(99, 102, 241, 0.3); | |
| } | |
| .btn-primary::after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 0; | |
| height: 0; | |
| border-radius: 50%; | |
| background: rgba(255, 255, 255, 0.3); | |
| transform: translate(-50%, -50%); | |
| transition: width 0.6s, height 0.6s; | |
| } | |
| .btn-primary:active::after { | |
| width: 300px; | |
| height: 300px; | |
| } | |
| .tasks-container { | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-radius: var(--radius); | |
| padding: 25px; | |
| box-shadow: var(--shadow); | |
| } | |
| body.dark-mode .tasks-container { | |
| background: rgba(17, 24, 39, 0.95); | |
| } | |
| .tasks-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 20px; | |
| flex-wrap: wrap; | |
| gap: 15px; | |
| } | |
| .search-box { | |
| position: relative; | |
| flex: 1; | |
| max-width: 300px; | |
| } | |
| .search-box input { | |
| width: 100%; | |
| padding: 10px 15px 10px 40px; | |
| border: 2px solid var(--border); | |
| border-radius: 25px; | |
| font-size: 14px; | |
| transition: var(--transition); | |
| } | |
| body.dark-mode .search-box input { | |
| background: var(--dark); | |
| color: var(--light); | |
| } | |
| .search-box i { | |
| position: absolute; | |
| left: 15px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| color: var(--gray); | |
| } | |
| .filter-tabs { | |
| display: flex; | |
| gap: 10px; | |
| flex-wrap: wrap; | |
| } | |
| .filter-tab { | |
| padding: 8px 16px; | |
| background: var(--light); | |
| border: none; | |
| border-radius: 20px; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| font-size: 14px; | |
| font-weight: 500; | |
| } | |
| body.dark-mode .filter-tab { | |
| background: var(--dark); | |
| } | |
| .filter-tab.active { | |
| background: var(--primary); | |
| color: white; | |
| } | |
| .filter-tab:hover:not(.active) { | |
| background: var(--border); | |
| } | |
| .tasks-list { | |
| display: grid; | |
| gap: 15px; | |
| } | |
| .task-item { | |
| background: var(--white); | |
| border: 2px solid var(--border); | |
| border-radius: var(--radius); | |
| padding: 15px; | |
| transition: var(--transition); | |
| cursor: move; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| body.dark-mode .task-item { | |
| background: var(--dark); | |
| } | |
| .task-item.dragging { | |
| opacity: 0.5; | |
| transform: scale(0.95); | |
| } | |
| .task-item:hover { | |
| border-color: var(--primary); | |
| box-shadow: var(--shadow); | |
| transform: translateX(5px); | |
| } | |
| .task-item.completed { | |
| opacity: 0.7; | |
| } | |
| .task-item.completed .task-title { | |
| text-decoration: line-through; | |
| color: var(--gray); | |
| } | |
| .task-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: flex-start; | |
| margin-bottom: 10px; | |
| } | |
| .task-title { | |
| font-size: 16px; | |
| font-weight: 600; | |
| color: var(--dark); | |
| flex: 1; | |
| margin-right: 10px; | |
| } | |
| .task-actions { | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .task-btn { | |
| width: 32px; | |
| height: 32px; | |
| border: none; | |
| background: var(--light); | |
| border-radius: 8px; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--gray); | |
| } | |
| body.dark-mode .task-btn { | |
| background: var(--dark); | |
| } | |
| .task-btn:hover { | |
| background: var(--primary); | |
| color: white; | |
| transform: scale(1.1); | |
| } | |
| .task-meta { | |
| display: flex; | |
| gap: 15px; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| } | |
| .task-priority { | |
| padding: 4px 10px; | |
| border-radius: 12px; | |
| font-size: 12px; | |
| font-weight: 500; | |
| } | |
| .priority-low { | |
| background: rgba(16, 185, 129, 0.1); | |
| color: var(--success); | |
| } | |
| .priority-medium { | |
| background: rgba(245, 158, 11, 0.1); | |
| color: var(--warning); | |
| } | |
| .priority-high { | |
| background: rgba(239, 68, 68, 0.1); | |
| color: var(--danger); | |
| } | |
| .task-category { | |
| padding: 4px 10px; | |
| background: rgba(99, 102, 241, 0.1); | |
| color: var(--primary); | |
| border-radius: 12px; | |
| font-size: 12px; | |
| font-weight: 500; | |
| } | |
| .task-date { | |
| display: flex; | |
| align-items: center; | |
| gap: 5px; | |
| color: var(--gray); | |
| font-size: 13px; | |
| } | |
| .progress-bar { | |
| height: 4px; | |
| background: var(--border); | |
| border-radius: 2px; | |
| margin-top: 10px; | |
| overflow: hidden; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: linear-gradient(90deg, var(--primary), var(--secondary)); | |
| border-radius: 2px; | |
| transition: width 0.3s ease; | |
| } | |
| .empty-state { | |
| text-align: center; | |
| padding: 60px 20px; | |
| color: var(--gray); | |
| } | |
| .empty-state i { | |
| font-size: 64px; | |
| margin-bottom: 20px; | |
| opacity: 0.3; | |
| } | |
| .toast { | |
| position: fixed; | |
| bottom: 30px; | |
| right: 30px; | |
| background: var(--white); | |
| padding: 15px 20px; | |
| border-radius: var(--radius); | |
| box-shadow: var(--shadow-lg); | |
| display: flex; | |
| align-items: center; | |
| gap: 15px; | |
| transform: translateX(400px); | |
| transition: transform 0.3s ease; | |
| z-index: 1000; | |
| } | |
| body.dark-mode .toast { | |
| background: var(--dark); | |
| } | |
| .toast.show { | |
| transform: translateX(0); | |
| } | |
| .toast-icon { | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: white; | |
| } | |
| .toast-icon.success { background: var(--success); } | |
| .toast-icon.error { background: var(--danger); } | |
| .toast-icon.info { background: var(--primary); } | |
| .chart-container { | |
| margin-top: 30px; | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-radius: var(--radius); | |
| padding: 25px; | |
| box-shadow: var(--shadow); | |
| } | |
| body.dark-mode .chart-container { | |
| background: rgba(17, 24, 39, 0.95); | |
| } | |
| .chart-header { | |
| font-size: 18px; | |
| font-weight: 600; | |
| margin-bottom: 20px; | |
| color: var(--dark); | |
| } | |
| .chart-bars { | |
| display: flex; | |
| align-items: flex-end; | |
| justify-content: space-around; | |
| height: 200px; | |
| gap: 20px; | |
| } | |
| .chart-bar { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .bar { | |
| width: 100%; | |
| background: linear-gradient(180deg, var(--primary), var(--secondary)); | |
| border-radius: 8px 8px 0 0; | |
| transition: var(--transition); | |
| position: relative; | |
| animation: growBar 1s ease-out; | |
| } | |
| .bar:hover { | |
| transform: scaleY(1.05); | |
| filter: brightness(1.1); | |
| } | |
| .bar-label { | |
| font-size: 12px; | |
| color: var(--gray); | |
| font-weight: 500; | |
| } | |
| .bar-value { | |
| position: absolute; | |
| top: -25px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| font-size: 14px; | |
| font-weight: 600; | |
| color: var(--dark); | |
| } | |
| @keyframes slideDown { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| @keyframes fadeInUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.1); } | |
| } | |
| @keyframes growBar { | |
| from { | |
| height: 0; | |
| } | |
| } | |
| @media (max-width: 968px) { | |
| .main-content { | |
| grid-template-columns: 1fr; | |
| } | |
| .sidebar { | |
| position: static; | |
| } | |
| .stats-container { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| } | |
| @media (max-width: 640px) { | |
| .container { | |
| padding: 10px; | |
| } | |
| header { | |
| padding: 15px 20px; | |
| } | |
| .stats-container { | |
| grid-template-columns: 1fr; | |
| } | |
| .tasks-header { | |
| flex-direction: column; | |
| align-items: stretch; | |
| } | |
| .search-box { | |
| max-width: 100%; | |
| } | |
| .filter-tabs { | |
| justify-content: center; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <div class="logo"> | |
| <i class="fas fa-tasks"></i> | |
| <span>TaskFlow Pro</span> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="text-decoration: none; color: var(--primary); font-size: 14px; margin-left: 10px;"> | |
| Built with anycoder | |
| </a> | |
| </div> | |
| <div class="header-actions"> | |
| <button class="theme-toggle" onclick="toggleTheme()"> | |
| <i class="fas fa-moon" id="themeIcon"></i> | |
| </button> | |
| </div> | |
| </header> | |
| <div class="stats-container"> | |
| <div class="stat-card"> | |
| <div class="stat-header"> | |
| <div> | |
| <div class="stat-value" id="totalTasks">0</div> | |
| <div class="stat-label">Total Tasks</div> | |
| </div> | |
| <div class="stat-icon blue"> | |
| <i class="fas fa-list"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-header"> | |
| <div> | |
| <div class="stat-value" id="completedTasks">0</div> | |
| <div class="stat-label">Completed</div> | |
| </div> | |
| <div class="stat-icon green"> | |
| <i class="fas fa-check-circle"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-header"> | |
| <div> | |
| <div class="stat-value" id="pendingTasks">0</div> | |
| <div class="stat-label">Pending</div> | |
| </div> | |
| <div class="stat-icon yellow"> | |
| <i class="fas fa-clock"></i> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="stat-header"> | |
| <div> | |
| <div class="stat-value" id="completionRate">0%</div> | |
| <div class="stat-label">Completion Rate</div> | |
| </div> | |
| <div class="stat-icon red"> | |
| <i class="fas fa-chart-line"></i> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="main-content"> | |
| <aside class="sidebar"> | |
| <div class="add-task-form"> | |
| <h3 style="margin-bottom: 20px; color: var(--dark);">Add New Task</h3> | |
| <div class="form-group"> | |
| <label>Task Title</label> | |
| <input type="text" class="form-control" id="taskTitle" placeholder="Enter task title..."> | |
| </div> | |
| <div class="form-group"> | |
| <label>Description</label> | |
| <input type="text" class="form-control" id="taskDescription" placeholder="Enter description..."> | |
| </div> | |
| <div class="form-group"> | |
| <label>Priority</label> | |
| <div class="priority-select"> | |
| <button class="priority-btn" data-priority="low" onclick="selectPriority(this)">Low</button> | |
| <button class="priority-btn" data-priority="medium" onclick="selectPriority(this)">Medium</button> | |
| <button class="priority-btn" data-priority="high" onclick="selectPriority(this)">High</button> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label>Category</label> | |
| <div class="category-select"> | |
| <button class="category-btn" data-category="work" onclick="selectCategory(this)">Work</button> | |
| <button class="category-btn" data-category="personal" onclick="selectCategory(this)">Personal</button> | |
| <button class="category-btn" data-category="shopping" onclick="selectCategory(this)">Shopping</button> | |
| <button class="category-btn" data-category="health" onclick="selectCategory(this)">Health</button> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label>Due Date</label> | |
| <input type="date" class="form-control" id="taskDate"> | |
| </div> | |
| <button class="btn-primary" onclick="addTask()"> | |
| <i class="fas fa-plus"></i> Add Task | |
| </button> | |
| </div> | |
| </aside> | |
| <main class="tasks-container"> | |
| <div class="tasks-header"> | |
| <h3 style="color: var(--dark);">Tasks</h3> | |
| <div class="search-box"> | |
| <i class="fas fa-search"></i> | |
| <input type="text" placeholder="Search tasks..." id="searchInput" onkeyup="searchTasks()"> | |
| </div> | |
| <div class="filter-tabs"> | |
| <button class="filter-tab active" onclick="filterTasks('all', this)">All</button> | |
| <button class="filter-tab" onclick="filterTasks('pending', this)">Pending</button> | |
| <button class="filter-tab" onclick="filterTasks('completed', this)">Completed</button> | |
| </div> | |
| </div> | |
| <div class="tasks-list" id="tasksList"> | |
| <div class="empty-state"> | |
| <i class="fas fa-inbox"></i> | |
| <h3>No tasks yet</h3> | |
| <p>Add your first task to get started!</p> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <div class="chart-container"> | |
| <h3 class="chart-header">Task Distribution by Category</h3> | |
| <div class="chart-bars" id="chartBars"> | |
| <!-- Chart bars will be generated dynamically --> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="toast" id="toast"> | |
| <div class="toast-icon" id="toastIcon"> | |
| <i class="fas fa-check"></i> | |
| </div> | |
| <div class="toast-message" id="toastMessage">Task added successfully!</div> | |
| </div> | |
| <script> | |
| // Initialize app | |
| let tasks = JSON.parse(localStorage.getItem('tasks')) || []; | |
| let selectedPriority = 'medium'; | |
| let selectedCategory = 'work'; | |
| let currentFilter = 'all'; | |
| // Initialize theme | |
| if (localStorage.getItem('darkMode') === 'true') { | |
| document.body.classList.add('dark-mode'); | |
| document.getElementById('themeIcon').className = 'fas fa-sun'; | |
| } | |
| // Initialize with sample tasks if empty | |
| if (tasks.length === 0) { | |
| tasks = [ | |
| { | |
| id: Date.now(), | |
| title: 'Welcome to TaskFlow Pro!', | |
| description: 'Start organizing your tasks efficiently', | |
| priority: 'high', | |
| category: 'personal', | |
| date: getDateTomorrow(), | |
| completed: false | |
| }, | |
| { | |
| id: Date.now() + 1, | |
| title: 'Complete project documentation', | |
| description: 'Write comprehensive docs for the new feature', | |
| priority: 'medium', | |
| category: 'work', | |
| date: getDateAfterDays(3), | |
| completed: false | |
| }, | |
| { | |
| id: Date.now() + 2, | |
| title: 'Buy groceries', | |
| description: 'Milk, bread, eggs, and vegetables', | |
| priority: 'low', | |
| category: 'shopping', | |
| date: getDateTomorrow(), | |
| completed: false | |
| } | |
| ]; | |
| saveTasks(); | |
| } | |
| function getDateTomorrow() { | |
| const date = new Date(); | |
| date.setDate(date.getDate() + 1); | |
| return date.toISOString().split('T')[0]; | |
| } | |
| function getDateAfterDays(days) { | |
| const date = new Date(); | |
| date.setDate(date.getDate() + days); | |
| return date.toISOString().split('T')[0]; | |
| } | |
| function toggleTheme() { | |
| document.body.classList.toggle('dark-mode'); | |
| const isDark = document.body.classList.contains('dark-mode'); | |
| localStorage.setItem('darkMode', isDark); | |
| document.getElementById('themeIcon').className = isDark ? 'fas fa-sun' : 'fas fa-moon'; | |
| } | |
| function selectPriority(btn) { | |
| document.querySelectorAll('.priority-btn').forEach(b => b.classList.remove('active', 'low', 'medium', 'high')); | |
| btn.classList.add('active', btn.dataset.priority); | |
| selectedPriority = btn.dataset.priority; | |
| } | |
| function selectCategory(btn) { | |
| document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| selectedCategory = btn.dataset.category; | |
| } | |
| function addTask() { | |
| const title = document.getElementById('taskTitle').value.trim(); | |
| const description = document.getElementById('taskDescription').value.trim(); | |
| const date = document.getElementById('taskDate').value; | |
| if (!title) { | |
| showToast('Please enter a task title', 'error'); | |
| return; | |
| } | |
| const task = { | |
| id: Date.now(), | |
| title, | |
| description, | |
| priority: selectedPriority, | |
| category: selectedCategory, | |
| date: date || getDateTomorrow(), | |
| completed: false | |
| }; | |
| tasks.unshift(task); | |
| saveTasks(); | |
| renderTasks(); | |
| updateStats(); | |
| updateChart(); | |
| clearForm(); | |
| showToast('Task added successfully!', 'success'); | |
| } | |
| function clearForm() { | |
| document.getElementById('taskTitle').value = ''; | |
| document.getElementById('taskDescription').value = ''; | |
| document.getElementById('taskDate').value = ''; | |
| document.querySelectorAll('.priority-btn').forEach(b => b.classList.remove('active', 'low', 'medium', 'high')); | |
| document.querySelectorAll('.category-btn').forEach(b => b.classList.remove('active')); | |
| selectedPriority = 'medium'; | |
| selectedCategory = 'work'; | |
| } | |
| function toggleTask(id) { | |
| const task = tasks.find(t => t.id === id); | |
| if (task) { | |
| task.completed = !task.completed; | |
| saveTasks(); | |
| renderTasks(); | |
| updateStats(); | |
| updateChart(); | |
| showToast(task.completed ? 'Task completed!' : 'Task marked as pending', 'info'); | |
| } | |
| } | |
| function deleteTask(id) { | |
| tasks = tasks.filter(t => t.id !== id); | |
| saveTasks(); | |
| renderTasks(); | |
| updateStats(); | |
| updateChart(); | |
| showToast('Task deleted', 'success'); | |
| } | |
| function editTask(id) { | |
| const task = tasks.find(t => t.id === id); | |
| if (task) { | |
| document.getElementById('taskTitle').value = task.title; | |
| document.getElementById('taskDescription').value = task.description; | |
| document.getElementById('taskDate').value = task.date; | |
| document.querySelectorAll('.priority-btn').forEach(b => { | |
| b.classList.remove('active', 'low', 'medium', 'high'); | |
| if (b.dataset.priority === task.priority) { | |
| b.classList.add('active', task.priority); | |
| selectedPriority = task.priority; | |
| } | |
| }); | |
| document.querySelectorAll('.category-btn').forEach(b => { | |
| b.classList.remove('active'); | |
| if (b.dataset.category === task.category) { | |
| b.classList.add('active'); | |
| selectedCategory = task.category; | |
| } | |
| }); | |
| deleteTask(id); | |
| } | |
| } | |
| function searchTasks() { | |
| const searchTerm = document.getElementById('searchInput').value.toLowerCase(); | |
| const filteredTasks = tasks.filter(task => { | |
| const matchesSearch = task.title.toLowerCase().includes(searchTerm) || | |
| task.description.toLowerCase().includes(searchTerm); | |
| const matchesFilter = currentFilter === 'all' || | |
| (currentFilter === 'completed' && task.completed) || | |
| (currentFilter === 'pending' && !task.completed); | |
| return matchesSearch && matchesFilter; | |
| }); | |
| renderFilteredTasks(filteredTasks); | |
| } | |
| function filterTasks(filter, btn) { | |
| currentFilter = filter; | |
| document.querySelectorAll('.filter-tab').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| searchTasks(); | |
| } | |
| function renderFilteredTasks(filteredTasks) { | |
| const tasksList = document.getElementById('tasksList'); | |
| if (filteredTasks.length === 0) { | |
| tasksList.innerHTML = ` | |
| <div class="empty-state"> | |
| <i class="fas fa-inbox"></i> | |
| <h3>No tasks found</h3> | |
| <p>${currentFilter === 'all' ? 'Add your first task to get started!' : 'No tasks match your filter'}</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| tasksList.innerHTML = filteredTasks.map(task => ` | |
| <div class="task-item ${task.completed ? 'completed' : ''}" draggable="true" ondragstart="drag(event)" ondragover="allowDrop(event)" ondrop="drop(event, ${task.id})"> | |
| <div class="task-header"> | |
| <div class="task-title">${task.title}</div> | |
| <div class="task-actions"> | |
| <button class="task-btn" onclick="toggleTask(${task.id})"> | |
| <i class="fas ${task.completed ? 'fa-undo' : 'fa-check'}"></i> | |
| </button> | |
| <button class="task-btn" onclick="editTask(${task.id})"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| <button class="task-btn" onclick="deleteTask(${task.id})"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </div> | |
| ${task.description ? `<p style="color: var(--gray); font-size: 14px; margin-bottom: 10px;">${task.description}</p>` : ''} | |
| <div class="task-meta"> | |
| <span class="task-priority priority-${task.priority}">${task.priority.toUpperCase()}</span> | |
| <span class="task-category">${task.category}</span> | |
| <div class="task-date"> | |
| <i class="far fa-calendar"></i> | |
| <span>${formatDate(task.date)}</span> | |
| </div> | |
| </div> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" style="width: ${task.completed ? '100' : '0'}%"></div> | |
| </div> | |
| </div> | |
| `).join(''); | |
| } | |
| function renderTasks() { | |
| searchTasks(); | |
| } | |
| 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: 'numeric' }); | |
| } | |
| } | |
| function updateStats() { | |
| const total = tasks.length; | |
| const completed = tasks.filter(t => t.completed).length; | |
| const pending = total - completed; | |
| const rate = total > 0 ? Math.round((completed / total) * 100) : 0; | |
| document.getElementById('totalTasks').textContent = total; | |
| document.getElementById('completedTasks').textContent = completed; | |
| document.getElementById('pendingTasks').textContent = pending; | |
| document.getElementById('completionRate').textContent = rate + '%'; | |
| } | |
| function updateChart() { | |
| const categories = ['work', 'personal', 'shopping', 'health']; | |
| const chartBars = document.getElementById('chartBars'); | |
| const categoryCounts = categories.map(category => | |
| tasks.filter(t => t.category === category).length | |
| ); | |
| const maxCount = Math.max(...categoryCounts, 1); | |
| chartBars.innerHTML = categories.map((category, index) => { | |
| const count = categoryCounts[index]; | |
| const height = (count / maxCount) * 100; | |
| return ` | |
| <div class="chart-bar"> | |
| <div class="bar" style="height: ${height}%"> | |
| <span class="bar-value">${count}</span> | |
| </div> | |
| <span class="bar-label">${category.charAt(0).toUpperCase() + category.slice(1)}</span> | |
| </div> | |
| `; | |
| }).join(''); | |
| } | |
| function saveTasks() { | |
| localStorage.setItem('tasks', |