to-do-app / index.html
Shinnnlol's picture
remove Made with for your productivity - Follow Up Deployment
1b69fa0 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ZenTask - Personal To-Do App</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>
/* Custom scrollbar */
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #cbd5e0;
border-radius: 10px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #a0aec0;
}
/* Task item animation */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.task-item {
animation: fadeIn 0.3s ease-out forwards;
}
/* Checkbox style */
input[type="checkbox"] {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #4f46e5;
border-radius: 4px;
outline: none;
cursor: pointer;
position: relative;
}
input[type="checkbox"]:checked {
background-color: #4f46e5;
}
input[type="checkbox"]:checked::before {
content: "✓";
position: absolute;
color: white;
font-size: 12px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body class="bg-gradient-to-br from-indigo-50 to-purple-50 min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-3xl">
<!-- Header -->
<header class="mb-8 text-center">
<h1 class="text-4xl font-bold text-indigo-800 mb-2">Shin's Task Manager</h1>
<p class="text-gray-600">Your personal productivity companion</p>
</header>
<!-- Main Content -->
<main class="bg-white rounded-xl shadow-lg overflow-hidden">
<!-- Add Task Form -->
<div class="p-6 border-b border-gray-200">
<form id="task-form" class="flex gap-3">
<input
type="text"
id="task-input"
placeholder="What needs to be done?"
class="flex-1 px-4 py-3 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
required
>
<button
type="submit"
class="bg-indigo-600 hover:bg-indigo-700 text-white px-6 py-3 rounded-lg font-medium transition duration-200 flex items-center gap-2"
>
<i class="fas fa-plus"></i> Add
</button>
</form>
</div>
<!-- Task Filters -->
<div class="px-6 py-3 bg-gray-50 flex justify-between items-center border-b border-gray-200">
<div class="text-sm text-gray-600">
<span id="task-count">0</span> tasks remaining
</div>
<div class="flex gap-2">
<button
id="filter-all"
class="filter-btn active px-3 py-1 rounded-md text-sm font-medium bg-indigo-100 text-indigo-800"
>
All
</button>
<button
id="filter-active"
class="filter-btn px-3 py-1 rounded-md text-sm font-medium text-gray-600 hover:bg-gray-100"
>
Active
</button>
<button
id="filter-completed"
class="filter-btn px-3 py-1 rounded-md text-sm font-medium text-gray-600 hover:bg-gray-100"
>
Completed
</button>
</div>
<button
id="clear-completed"
class="text-sm text-gray-600 hover:text-indigo-600"
>
Clear completed
</button>
</div>
<!-- Task List -->
<div id="task-list" class="max-h-96 overflow-y-auto custom-scrollbar">
<!-- Tasks will be added here dynamically -->
<div class="p-4 text-center text-gray-500" id="empty-state">
<i class="fas fa-tasks text-4xl mb-3 text-gray-300"></i>
<p>No tasks yet. Add one above!</p>
</div>
</div>
</main>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const taskForm = document.getElementById('task-form');
const taskInput = document.getElementById('task-input');
const taskList = document.getElementById('task-list');
const emptyState = document.getElementById('empty-state');
const taskCount = document.getElementById('task-count');
const filterAll = document.getElementById('filter-all');
const filterActive = document.getElementById('filter-active');
const filterCompleted = document.getElementById('filter-completed');
const clearCompleted = document.getElementById('clear-completed');
// State
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
let currentFilter = 'all';
// Initialize
renderTasks();
updateTaskCount();
// Event Listeners
taskForm.addEventListener('submit', addTask);
clearCompleted.addEventListener('click', clearCompletedTasks);
filterAll.addEventListener('click', () => setFilter('all'));
filterActive.addEventListener('click', () => setFilter('active'));
filterCompleted.addEventListener('click', () => setFilter('completed'));
// Functions
function addTask(e) {
e.preventDefault();
const taskText = taskInput.value.trim();
if (!taskText) return;
const newTask = {
id: Date.now(),
text: taskText,
completed: false,
createdAt: new Date().toISOString()
};
tasks.push(newTask);
saveTasks();
renderTasks();
updateTaskCount();
taskInput.value = '';
taskInput.focus();
}
function renderTasks() {
// Clear the task list
taskList.innerHTML = '';
// Filter tasks based on current filter
let filteredTasks = [];
if (currentFilter === 'all') {
filteredTasks = tasks;
} else if (currentFilter === 'active') {
filteredTasks = tasks.filter(task => !task.completed);
} else if (currentFilter === 'completed') {
filteredTasks = tasks.filter(task => task.completed);
}
// Show empty state if no tasks
if (filteredTasks.length === 0) {
emptyState.style.display = 'block';
return;
} else {
emptyState.style.display = 'none';
}
// Render each task
filteredTasks.forEach(task => {
const taskElement = document.createElement('div');
taskElement.className = `task-item p-4 border-b border-gray-200 flex items-center justify-between hover:bg-gray-50 transition duration-150`;
taskElement.dataset.id = task.id;
taskElement.innerHTML = `
<div class="flex items-center gap-3">
<input
type="checkbox"
${task.completed ? 'checked' : ''}
class="task-checkbox"
>
<span class="${task.completed ? 'line-through text-gray-400' : 'text-gray-800'}">
${task.text}
</span>
</div>
<button class="delete-btn text-gray-400 hover:text-red-500 transition duration-150">
<i class="fas fa-trash"></i>
</button>
`;
taskList.appendChild(taskElement);
});
// Add event listeners to new elements
document.querySelectorAll('.task-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', toggleTask);
});
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', deleteTask);
});
}
function toggleTask(e) {
const taskId = parseInt(e.target.closest('.task-item').dataset.id);
const task = tasks.find(task => task.id === taskId);
if (task) {
task.completed = e.target.checked;
saveTasks();
renderTasks();
updateTaskCount();
}
}
function deleteTask(e) {
const taskId = parseInt(e.target.closest('.task-item').dataset.id);
tasks = tasks.filter(task => task.id !== taskId);
saveTasks();
renderTasks();
updateTaskCount();
}
function clearCompletedTasks() {
tasks = tasks.filter(task => !task.completed);
saveTasks();
renderTasks();
updateTaskCount();
}
function updateTaskCount() {
const activeTasks = tasks.filter(task => !task.completed).length;
taskCount.textContent = activeTasks;
}
function setFilter(filter) {
currentFilter = filter;
// Update active filter button
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.remove('active', 'bg-indigo-100', 'text-indigo-800');
btn.classList.add('text-gray-600', 'hover:bg-gray-100');
});
const activeBtn = document.getElementById(`filter-${filter}`);
activeBtn.classList.add('active', 'bg-indigo-100', 'text-indigo-800');
activeBtn.classList.remove('text-gray-600', 'hover:bg-gray-100');
renderTasks();
}
function saveTasks() {
localStorage.setItem('tasks', 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=Shinnnlol/to-do-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>