xeeshan-s-projects / index.html
Zeeshsan's picture
Add 2 files
6ccd86b verified
<!DOCTYPE html>
<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 &copy; 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>