Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>XTasks - Ultimate Task Manager</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); | |
| body { | |
| font-family: 'Poppins', sans-serif; | |
| background-color: #f8fafc; | |
| } | |
| .task-item:hover .task-actions { | |
| opacity: 1; | |
| } | |
| .task-actions { | |
| opacity: 0; | |
| transition: opacity 0.2s ease; | |
| } | |
| .completed { | |
| position: relative; | |
| } | |
| .completed::after { | |
| content: ""; | |
| position: absolute; | |
| left: 0; | |
| top: 50%; | |
| width: 100%; | |
| height: 1px; | |
| background: #94a3b8; | |
| transform: translateY(-50%); | |
| } | |
| /* Custom scrollbar */ | |
| .task-list::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .task-list::-webkit-scrollbar-track { | |
| background: #f1f5f9; | |
| } | |
| .task-list::-webkit-scrollbar-thumb { | |
| background: #cbd5e1; | |
| border-radius: 3px; | |
| } | |
| .task-list::-webkit-scrollbar-thumb:hover { | |
| background: #94a3b8; | |
| } | |
| /* Animation for new task */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .animate-fade-in { | |
| animation: fadeIn 0.3s ease-out forwards; | |
| } | |
| /* Pulse animation for empty state */ | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| } | |
| .animate-pulse-slow { | |
| animation: pulse 2s infinite; | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 max-w-3xl"> | |
| <!-- Header --> | |
| <header class="mb-8 text-center"> | |
| <div class="flex items-center justify-center mb-2"> | |
| <i class="fas fa-check-circle text-indigo-600 text-4xl mr-3"></i> | |
| <h1 class="text-4xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">XTasks</h1> | |
| </div> | |
| <p class="text-slate-600">Your ultimate productivity companion</p> | |
| </header> | |
| <!-- Stats Cards --> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8"> | |
| <div class="bg-white rounded-lg shadow p-4 border-l-4 border-indigo-500"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-indigo-100 text-indigo-600 mr-4"> | |
| <i class="fas fa-tasks"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-slate-500">Total Tasks</p> | |
| <h3 class="text-xl font-semibold" id="total-tasks">0</h3> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow p-4 border-l-4 border-green-500"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-green-100 text-green-600 mr-4"> | |
| <i class="fas fa-check"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-slate-500">Completed</p> | |
| <h3 class="text-xl font-semibold" id="completed-tasks">0</h3> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow p-4 border-l-4 border-blue-500"> | |
| <div class="flex items-center"> | |
| <div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4"> | |
| <i class="fas fa-clock"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-slate-500">Pending</p> | |
| <h3 class="text-xl font-semibold" id="pending-tasks">0</h3> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Add Task Form --> | |
| <div class="bg-white rounded-xl shadow-lg mb-8 p-6"> | |
| <div class="flex items-center"> | |
| <input | |
| type="text" | |
| id="new-task-input" | |
| placeholder="What needs to be done?" | |
| class="flex-1 px-4 py-3 rounded-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" | |
| autocomplete="off" | |
| > | |
| <button | |
| id="add-task-btn" | |
| class="ml-4 px-6 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition duration-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" | |
| > | |
| <i class="fas fa-plus mr-2"></i> Add Task | |
| </button> | |
| </div> | |
| <div class="mt-4 flex items-center text-sm text-slate-500"> | |
| <i class="fas fa-info-circle mr-2 text-indigo-500"></i> | |
| <span>Press Enter to add a task quickly</span> | |
| </div> | |
| </div> | |
| <!-- Task Filters --> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-slate-800">My Tasks</h2> | |
| <div class="flex space-x-2"> | |
| <button | |
| id="filter-all" | |
| class="px-3 py-1 text-sm rounded-md bg-indigo-100 text-indigo-700 font-medium" | |
| > | |
| All | |
| </button> | |
| <button | |
| id="filter-active" | |
| class="px-3 py-1 text-sm rounded-md bg-slate-100 text-slate-700 font-medium hover:bg-slate-200" | |
| > | |
| Active | |
| </button> | |
| <button | |
| id="filter-completed" | |
| class="px-3 py-1 text-sm rounded-md bg-slate-100 text-slate-700 font-medium hover:bg-slate-200" | |
| > | |
| Completed | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Task List --> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden"> | |
| <div id="task-list" class="task-list max-h-96 overflow-y-auto"> | |
| <!-- Empty state --> | |
| <div id="empty-state" class="p-8 text-center"> | |
| <div class="animate-pulse-slow inline-block mb-4"> | |
| <i class="fas fa-clipboard-list text-5xl text-slate-300"></i> | |
| </div> | |
| <h3 class="text-lg font-medium text-slate-700 mb-1">No tasks yet</h3> | |
| <p class="text-slate-500">Add your first task to get started!</p> | |
| </div> | |
| <!-- Tasks will be added here dynamically --> | |
| </div> | |
| <!-- Task Summary --> | |
| <div class="border-t border-slate-200 p-4 text-sm text-slate-500 flex justify-between items-center"> | |
| <span id="task-summary">0 items left</span> | |
| <button | |
| id="clear-completed" | |
| class="text-slate-500 hover:text-slate-700 hover:underline" | |
| > | |
| Clear completed | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Footer --> | |
| <footer class="mt-12 text-center text-sm text-slate-500"> | |
| <p>XTasks © 2023 - Made with <i class="fas fa-heart text-red-500"></i> for productive people</p> | |
| </footer> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // DOM Elements | |
| const newTaskInput = document.getElementById('new-task-input'); | |
| const addTaskBtn = document.getElementById('add-task-btn'); | |
| const taskList = document.getElementById('task-list'); | |
| const emptyState = document.getElementById('empty-state'); | |
| const filterAll = document.getElementById('filter-all'); | |
| const filterActive = document.getElementById('filter-active'); | |
| const filterCompleted = document.getElementById('filter-completed'); | |
| const clearCompleted = document.getElementById('clear-completed'); | |
| const taskSummary = document.getElementById('task-summary'); | |
| const totalTasksEl = document.getElementById('total-tasks'); | |
| const completedTasksEl = document.getElementById('completed-tasks'); | |
| const pendingTasksEl = document.getElementById('pending-tasks'); | |
| // State | |
| let tasks = JSON.parse(localStorage.getItem('xtasks')) || []; | |
| let currentFilter = 'all'; | |
| // Initialize | |
| renderTasks(); | |
| updateStats(); | |
| // Event Listeners | |
| addTaskBtn.addEventListener('click', addTask); | |
| newTaskInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| addTask(); | |
| } | |
| }); | |
| filterAll.addEventListener('click', () => setFilter('all')); | |
| filterActive.addEventListener('click', () => setFilter('active')); | |
| filterCompleted.addEventListener('click', () => setFilter('completed')); | |
| clearCompleted.addEventListener('click', clearCompletedTasks); | |
| // Functions | |
| function addTask() { | |
| const taskText = newTaskInput.value.trim(); | |
| if (taskText) { | |
| const newTask = { | |
| id: Date.now(), | |
| text: taskText, | |
| completed: false, | |
| createdAt: new Date().toISOString() | |
| }; | |
| tasks.unshift(newTask); | |
| saveTasks(); | |
| renderTasks(); | |
| newTaskInput.value = ''; | |
| newTaskInput.focus(); | |
| updateStats(); | |
| } | |
| } | |
| function renderTasks() { | |
| if (tasks.length === 0) { | |
| emptyState.style.display = 'block'; | |
| taskList.innerHTML = ''; | |
| taskList.appendChild(emptyState); | |
| return; | |
| } | |
| emptyState.style.display = 'none'; | |
| // Filter tasks based on current filter | |
| let filteredTasks = tasks; | |
| if (currentFilter === 'active') { | |
| filteredTasks = tasks.filter(task => !task.completed); | |
| } else if (currentFilter === 'completed') { | |
| filteredTasks = tasks.filter(task => task.completed); | |
| } | |
| if (filteredTasks.length === 0) { | |
| const noTasksMsg = document.createElement('div'); | |
| noTasksMsg.className = 'p-8 text-center'; | |
| noTasksMsg.innerHTML = ` | |
| <div class="inline-block mb-4"> | |
| <i class="fas fa-search text-5xl text-slate-300"></i> | |
| </div> | |
| <h3 class="text-lg font-medium text-slate-700 mb-1">No ${currentFilter} tasks</h3> | |
| <p class="text-slate-500">${currentFilter === 'all' ? 'Add your first task!' : `No ${currentFilter} tasks found`}</p> | |
| `; | |
| taskList.innerHTML = ''; | |
| taskList.appendChild(noTasksMsg); | |
| return; | |
| } | |
| taskList.innerHTML = ''; | |
| filteredTasks.forEach((task, index) => { | |
| const taskItem = document.createElement('div'); | |
| taskItem.className = `task-item animate-fade-in border-b border-slate-200 last:border-b-0 p-4 flex items-center ${task.completed ? 'opacity-75' : ''}`; | |
| taskItem.dataset.id = task.id; | |
| taskItem.innerHTML = ` | |
| <div class="flex items-center flex-1"> | |
| <button class="complete-btn mr-3 w-6 h-6 rounded-full border-2 ${task.completed ? 'border-green-500 bg-green-500 text-white' : 'border-slate-300'} flex items-center justify-center"> | |
| ${task.completed ? '<i class="fas fa-check text-xs"></i>' : ''} | |
| </button> | |
| <div class="flex-1 ${task.completed ? 'completed text-slate-500' : 'text-slate-800'}">${task.text}</div> | |
| <div class="task-actions flex space-x-2"> | |
| <button class="edit-btn p-1 text-slate-400 hover:text-indigo-500"> | |
| <i class="fas fa-pencil-alt"></i> | |
| </button> | |
| <button class="delete-btn p-1 text-slate-400 hover:text-red-500"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| taskList.appendChild(taskItem); | |
| // Add event listeners to the new elements | |
| const completeBtn = taskItem.querySelector('.complete-btn'); | |
| const editBtn = taskItem.querySelector('.edit-btn'); | |
| const deleteBtn = taskItem.querySelector('.delete-btn'); | |
| completeBtn.addEventListener('click', () => toggleComplete(task.id)); | |
| editBtn.addEventListener('click', () => editTask(task.id)); | |
| deleteBtn.addEventListener('click', () => deleteTask(task.id)); | |
| }); | |
| updateTaskSummary(); | |
| } | |
| function toggleComplete(taskId) { | |
| const taskIndex = tasks.findIndex(task => task.id == taskId); | |
| if (taskIndex !== -1) { | |
| tasks[taskIndex].completed = !tasks[taskIndex].completed; | |
| saveTasks(); | |
| renderTasks(); | |
| updateStats(); | |
| } | |
| } | |
| function editTask(taskId) { | |
| const task = tasks.find(task => task.id == taskId); | |
| if (!task) return; | |
| const taskItem = document.querySelector(`.task-item[data-id="${taskId}"]`); | |
| const taskTextElement = taskItem.querySelector('div > div'); | |
| const input = document.createElement('input'); | |
| input.type = 'text'; | |
| input.value = task.text; | |
| input.className = 'flex-1 px-2 py-1 border border-slate-300 rounded focus:outline-none focus:ring-1 focus:ring-indigo-500'; | |
| taskTextElement.innerHTML = ''; | |
| taskTextElement.appendChild(input); | |
| input.focus(); | |
| function saveEdit() { | |
| const newText = input.value.trim(); | |
| if (newText) { | |
| task.text = newText; | |
| saveTasks(); | |
| renderTasks(); | |
| } else { | |
| deleteTask(taskId); | |
| } | |
| } | |
| input.addEventListener('blur', saveEdit); | |
| input.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| saveEdit(); | |
| } | |
| }); | |
| } | |
| function deleteTask(taskId) { | |
| tasks = tasks.filter(task => task.id != taskId); | |
| saveTasks(); | |
| renderTasks(); | |
| updateStats(); | |
| } | |
| function clearCompletedTasks() { | |
| tasks = tasks.filter(task => !task.completed); | |
| saveTasks(); | |
| renderTasks(); | |
| updateStats(); | |
| } | |
| function setFilter(filter) { | |
| currentFilter = filter; | |
| // Update active filter button | |
| filterAll.className = 'px-3 py-1 text-sm rounded-md bg-slate-100 text-slate-700 font-medium hover:bg-slate-200'; | |
| filterActive.className = 'px-3 py-1 text-sm rounded-md bg-slate-100 text-slate-700 font-medium hover:bg-slate-200'; | |
| filterCompleted.className = 'px-3 py-1 text-sm rounded-md bg-slate-100 text-slate-700 font-medium hover:bg-slate-200'; | |
| if (filter === 'all') { | |
| filterAll.className = 'px-3 py-1 text-sm rounded-md bg-indigo-100 text-indigo-700 font-medium'; | |
| } else if (filter === 'active') { | |
| filterActive.className = 'px-3 py-1 text-sm rounded-md bg-indigo-100 text-indigo-700 font-medium'; | |
| } else { | |
| filterCompleted.className = 'px-3 py-1 text-sm rounded-md bg-indigo-100 text-indigo-700 font-medium'; | |
| } | |
| renderTasks(); | |
| } | |
| function updateTaskSummary() { | |
| const activeTasks = tasks.filter(task => !task.completed).length; | |
| taskSummary.textContent = `${activeTasks} ${activeTasks === 1 ? 'item' : 'items'} left`; | |
| } | |
| function updateStats() { | |
| const total = tasks.length; | |
| const completed = tasks.filter(task => task.completed).length; | |
| const pending = total - completed; | |
| totalTasksEl.textContent = total; | |
| completedTasksEl.textContent = completed; | |
| pendingTasksEl.textContent = pending; | |
| } | |
| function saveTasks() { | |
| localStorage.setItem('xtasks', JSON.stringify(tasks)); | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Zeeshsan/xeeshan-s-projects" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |