deepsite-todo-app / index.html
DoraWill's picture
Add 2 files
b6e0e8b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ZenFocus - Elegant Todo 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">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
}
},
fontFamily: {
sans: ['"Inter"', 'system-ui', '-apple-system', 'sans-serif'],
},
boxShadow: {
'soft': '0 4px 20px -2px rgba(0, 0, 0, 0.08)',
'inner-glow': 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)'
}
}
}
}
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
min-height: 100vh;
}
.task-item {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.task-item:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px -1px rgba(0, 0, 0, 0.05);
}
.task-item:hover .task-actions {
opacity: 1;
}
.task-actions {
opacity: 0;
transition: opacity 0.2s ease;
}
.checkbox-custom {
appearance: none;
-webkit-appearance: none;
width: 18px;
height: 18px;
border: 2px solid #e2e8f0;
border-radius: 4px;
cursor: pointer;
position: relative;
transition: all 0.2s ease;
}
.checkbox-custom:checked {
background-color: #0ea5e9;
border-color: #0ea5e9;
}
.checkbox-custom:checked::after {
content: "";
position: absolute;
left: 5px;
top: 1px;
width: 4px;
height: 8px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.priority-high {
border-left: 3px solid #ef4444;
background-color: rgba(239, 68, 68, 0.03);
}
.priority-medium {
border-left: 3px solid #f59e0b;
background-color: rgba(245, 158, 11, 0.03);
}
.priority-low {
border-left: 3px solid #10b981;
background-color: rgba(16, 185, 129, 0.03);
}
/* Animation for new task */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.task-animate {
animation: fadeIn 0.3s ease-out forwards;
}
/* Pulse animation for empty state */
@keyframes pulse {
0% { opacity: 0.8; }
50% { opacity: 1; }
100% { opacity: 0.8; }
}
.empty-pulse {
animation: pulse 2s ease-in-out infinite;
}
/* Custom scrollbar */
.task-list::-webkit-scrollbar {
width: 6px;
}
.task-list::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 10px;
}
.task-list::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 10px;
}
.task-list::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
/* Floating action button */
.fab {
box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.15);
transition: all 0.2s ease;
}
.fab:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px -2px rgba(0, 0, 0, 0.2);
}
/* Gradient text */
.gradient-text {
background: linear-gradient(90deg, #0ea5e9 0%, #3b82f6 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
/* Ripple effect */
.ripple {
position: relative;
overflow: hidden;
}
.ripple:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
background-image: radial-gradient(circle, #fff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-position: 50%;
transform: scale(10, 10);
opacity: 0;
transition: transform .5s, opacity 1s;
}
.ripple:active:after {
transform: scale(0, 0);
opacity: 0.2;
transition: 0s;
}
/* Shimmer effect */
.shimmer {
position: relative;
overflow: hidden;
}
.shimmer::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
100% {
left: 100%;
}
}
</style>
</head>
<body class="antialiased text-gray-800">
<div class="container mx-auto px-4 py-8 md:py-12 max-w-2xl">
<!-- Header -->
<header class="mb-10">
<div class="text-center mb-6">
<div class="flex justify-center mb-3">
<div class="w-12 h-12 bg-primary-100 rounded-xl flex items-center justify-center shadow-soft">
<i class="fas fa-check-circle text-2xl gradient-text"></i>
</div>
</div>
<h1 class="text-4xl font-extrabold gradient-text mb-1">ZenFocus</h1>
<p class="text-gray-500 text-lg">Organize your day with simplicity</p>
</div>
<div class="relative w-full mb-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-plus-circle text-gray-400"></i>
</div>
<input
type="text"
id="new-task-input"
placeholder="Add a new task..."
class="w-full pl-10 pr-4 py-3 rounded-xl border border-gray-200 bg-white focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent shadow-sm transition duration-200"
autocomplete="off"
>
<div class="absolute inset-y-0 right-0 flex items-center pr-3">
<button
id="add-task-btn"
class="fab p-2 bg-gradient-to-r from-primary-500 to-primary-600 text-white rounded-xl hover:shadow-md"
>
<i class="fas fa-paper-plane text-sm"></i>
</button>
</div>
</div>
<p class="text-xs text-gray-400 pl-3 mt-1">Tip: Press Enter to add quickly</p>
</header>
<!-- Stats Cards -->
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3 md:gap-4 mb-6">
<div class="bg-white p-4 rounded-xl shadow-soft transition hover:shadow-md">
<div class="flex items-center">
<div class="p-2 mr-3 rounded-lg bg-primary-50">
<i class="fas fa-tasks text-primary-500"></i>
</div>
<div>
<p class="text-sm text-gray-500">Total Tasks</p>
<h3 class="text-xl font-semibold" id="total-tasks">0</h3>
</div>
</div>
</div>
<div class="bg-white p-4 rounded-xl shadow-soft transition hover:shadow-md">
<div class="flex items-center">
<div class="p-2 mr-3 rounded-lg bg-green-50">
<i class="fas fa-check-circle text-green-500"></i>
</div>
<div>
<p class="text-sm text-gray-500">Completed</p>
<h3 class="text-xl font-semibold" id="completed-tasks">0</h3>
</div>
</div>
</div>
<div class="bg-white p-4 rounded-xl shadow-soft transition hover:shadow-md">
<div class="flex items-center">
<div class="p-2 mr-3 rounded-lg bg-amber-50">
<i class="fas fa-clock text-amber-500"></i>
</div>
<div>
<p class="text-sm text-gray-500">Pending</p>
<h3 class="text-xl font-semibold" id="pending-tasks">0</h3>
</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="flex flex-wrap justify-between items-center mb-6 bg-white p-4 rounded-xl shadow-soft">
<div class="flex space-x-1 mb-2 sm:mb-0">
<button
id="filter-all"
class="filter-btn active px-3 py-1.5 rounded-lg bg-primary-100 text-primary-800 font-medium text-sm ripple"
>
All
</button>
<button
id="filter-active"
class="filter-btn px-3 py-1.5 rounded-lg hover:bg-gray-50 text-gray-600 text-sm ripple"
>
Active
</button>
<button
id="filter-completed"
class="filter-btn px-3 py-1.5 rounded-lg hover:bg-gray-50 text-gray-600 text-sm ripple"
>
Completed
</button>
</div>
<div class="flex items-center">
<div class="relative">
<select
id="sort-tasks"
class="appearance-none text-sm border border-gray-200 rounded-lg px-3 py-1.5 pr-8 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white"
>
<option value="date">Date Added</option>
<option value="priority">Priority</option>
<option value="name">Name</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500">
<i class="fas fa-chevron-down text-xs"></i>
</div>
</div>
</div>
</div>
<!-- Task List -->
<div class="bg-white rounded-xl shadow-soft overflow-hidden">
<div id="task-list" class="task-list max-h-[480px] overflow-y-auto">
<!-- Empty state -->
<div class="p-8 text-center" id="empty-state">
<div class="mb-4 empty-pulse">
<svg class="mx-auto h-16 w-16 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
</div>
<h3 class="text-lg font-medium text-gray-700 mb-1">No tasks yet</h3>
<p class="text-gray-500">Get started by adding your first task</p>
<div class="mt-4">
<button class="text-sm text-primary-600 hover:text-primary-800 flex items-center justify-center mx-auto" id="sample-task-btn">
<i class="fas fa-magic mr-1.5"></i> Add sample task
</button>
</div>
</div>
</div>
<!-- Footer Actions -->
<div class="border-t border-gray-100 p-4 flex justify-between items-center bg-gray-50 rounded-b-xl">
<div class="text-sm text-gray-600 font-medium" id="remaining-count">0 items left</div>
<button
id="clear-completed"
class="text-sm text-primary-600 hover:text-primary-800 font-medium ripple"
>
<i class="fas fa-broom mr-1"></i> Clear completed
</button>
</div>
</div>
</div>
<!-- Priority Modal -->
<div id="priority-modal" class="fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center hidden z-50 backdrop-blur-sm">
<div class="bg-white rounded-xl p-6 w-full max-w-sm mx-4 shadow-2xl transform transition-all" id="modal-content">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Set Priority</h3>
<div class="space-y-2">
<button
class="priority-option w-full text-left px-4 py-3 rounded-lg border border-red-100 bg-red-50 hover:bg-red-100 transition-colors group ripple"
data-priority="high"
>
<div class="flex items-center">
<div class="w-8 h-8 rounded-lg bg-red-100 flex items-center justify-center mr-3 group-hover:bg-red-200 transition-colors">
<i class="fas fa-exclamation-circle text-red-600 text-sm"></i>
</div>
<div>
<div class="font-medium text-red-800">High Priority</div>
<div class="text-xs text-red-600">Urgent and important</div>
</div>
</div>
</button>
<button
class="priority-option w-full text-left px-4 py-3 rounded-lg border border-amber-100 bg-amber-50 hover:bg-amber-100 transition-colors group ripple"
data-priority="medium"
>
<div class="flex items-center">
<div class="w-8 h-8 rounded-lg bg-amber-100 flex items-center justify-center mr-3 group-hover:bg-amber-200 transition-colors">
<i class="fas fa-exclamation text-amber-600 text-sm"></i>
</div>
<div>
<div class="font-medium text-amber-800">Medium Priority</div>
<div class="text-xs text-amber-600">Important but not urgent</div>
</div>
</div>
</button>
<button
class="priority-option w-full text-left px-4 py-3 rounded-lg border border-green-100 bg-green-50 hover:bg-green-100 transition-colors group ripple"
data-priority="low"
>
<div class="flex items-center">
<div class="w-8 h-8 rounded-lg bg-green-100 flex items-center justify-center mr-3 group-hover:bg-green-200 transition-colors">
<i class="fas fa-arrow-down text-green-600 text-sm"></i>
</div>
<div>
<div class="font-medium text-green-800">Low Priority</div>
<div class="text-xs text-green-600">Neither urgent nor important</div>
</div>
</div>
</button>
</div>
<div class="mt-6 flex justify-end">
<button
id="cancel-priority"
class="px-4 py-2 text-gray-600 hover:text-gray-800 font-medium ripple"
>
Cancel
</button>
</div>
</div>
</div>
<!-- Confirmation Modal -->
<div id="confirm-modal" class="fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center hidden z-50 backdrop-blur-sm">
<div class="bg-white rounded-xl p-6 w-full max-w-sm mx-4 shadow-2xl">
<div class="text-center">
<div class="mx-auto h-12 w-12 rounded-full bg-red-100 flex items-center justify-center mb-4">
<i class="fas fa-exclamation text-red-600"></i>
</div>
<h3 class="text-lg font-medium text-gray-900 mb-2" id="confirm-title">Are you sure?</h3>
<p class="text-sm text-gray-500 mb-4" id="confirm-message">This action cannot be undone.</p>
<div class="flex justify-center space-x-3">
<button
id="confirm-cancel"
class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 font-medium ripple"
>
Cancel
</button>
<button
id="confirm-ok"
class="px-4 py-2 bg-red-600 rounded-lg text-white hover:bg-red-700 font-medium ripple"
>
Confirm
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const taskInput = 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 remainingCount = document.getElementById('remaining-count');
const totalTasksEl = document.getElementById('total-tasks');
const completedTasksEl = document.getElementById('completed-tasks');
const pendingTasksEl = document.getElementById('pending-tasks');
const sortSelect = document.getElementById('sort-tasks');
const priorityModal = document.getElementById('priority-modal');
const priorityOptions = document.querySelectorAll('.priority-option');
const cancelPriority = document.getElementById('cancel-priority');
const sampleTaskBtn = document.getElementById('sample-task-btn');
const confirmModal = document.getElementById('confirm-modal');
const confirmTitle = document.getElementById('confirm-title');
const confirmMessage = document.getElementById('confirm-message');
const confirmCancel = document.getElementById('confirm-cancel');
const confirmOk = document.getElementById('confirm-ok');
// State
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
let currentFilter = 'all';
let currentSort = 'date';
let currentTaskIdForPriority = null;
let confirmCallback = null;
// Initialize
updateTaskList();
updateStats();
// Event Listeners
addTaskBtn.addEventListener('click', addTask);
taskInput.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', () => {
showConfirm(
'Clear completed tasks?',
'This will remove all completed tasks from your list.',
clearCompletedTasks
);
});
sortSelect.addEventListener('change', (e) => {
currentSort = e.target.value;
updateTaskList();
});
cancelPriority.addEventListener('click', () => {
priorityModal.classList.add('hidden');
});
priorityOptions.forEach(option => {
option.addEventListener('click', function() {
const priority = this.getAttribute('data-priority');
setTaskPriority(currentTaskIdForPriority, priority);
priorityModal.classList.add('hidden');
});
});
sampleTaskBtn.addEventListener('click', addSampleTasks);
confirmCancel.addEventListener('click', () => {
confirmModal.classList.add('hidden');
});
confirmOk.addEventListener('click', () => {
if (confirmCallback) confirmCallback();
confirmModal.classList.add('hidden');
});
// Functions
function addTask() {
const taskText = taskInput.value.trim();
if (taskText === '') return;
const newTask = {
id: Date.now(),
text: taskText,
completed: false,
priority: 'medium', // default priority
createdAt: new Date().toISOString()
};
tasks.unshift(newTask);
saveTasks();
taskInput.value = '';
updateTaskList();
updateStats();
taskInput.focus();
// Animate the add button
addTaskBtn.classList.add('animate-ping');
setTimeout(() => {
addTaskBtn.classList.remove('animate-ping');
}, 300);
// Show priority modal for new task after a slight delay
setTimeout(() => {
currentTaskIdForPriority = newTask.id;
showPriorityModal();
}, 50);
}
function addSampleTasks() {
const sampleTasks = [
{ text: "Complete project presentation", priority: "high" },
{ text: "Reply to important emails", priority: "high" },
{ text: "Buy groceries for the week", priority: "medium" },
{ text: "Schedule dentist appointment", priority: "medium" },
{ text: "Read 20 pages of book", priority: "low" },
{ text: "Organize workspace", priority: "low" }
];
sampleTasks.forEach(task => {
const newTask = {
id: Date.now() + Math.random(),
text: task.text,
completed: false,
priority: task.priority,
createdAt: new Date().toISOString()
};
tasks.unshift(newTask);
});
saveTasks();
updateTaskList();
updateStats();
setFilter('all');
// Show shimmer effect on empty state before it disappears
emptyState.classList.add('shimmer');
setTimeout(() => {
emptyState.classList.remove('shimmer');
}, 300);
}
function setTaskPriority(taskId, priority) {
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex !== -1) {
tasks[taskIndex].priority = priority;
saveTasks();
updateTaskList();
// Show priority indicator
const priorityEl = document.querySelector(`.priority-btn[data-id="${taskId}"]`);
if (priorityEl) {
priorityEl.classList.add('animate-bounce');
setTimeout(() => {
priorityEl.classList.remove('animate-bounce');
}, 1000);
}
}
}
function toggleTaskComplete(taskId) {
const task = tasks.find(task => task.id === taskId);
if (task) {
task.completed = !task.completed;
saveTasks();
updateTaskList();
updateStats();
// Play completion sound if completed
if (task.completed) {
playCompletionSound();
}
}
}
function playCompletionSound() {
const audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU' + Array(1e3).join('123'));
audio.volume = 0.2;
audio.play().catch(e => console.log('Sound playback prevented:', e));
}
function deleteTask(taskId) {
showConfirm(
'Delete this task?',
'This task will be permanently removed from your list.',
() => {
tasks = tasks.filter(task => task.id !== taskId);
saveTasks();
updateTaskList();
updateStats();
}
);
}
function editTask(taskId, newText) {
const task = tasks.find(task => task.id === taskId);
if (task && newText.trim() !== '') {
task.text = newText.trim();
saveTasks();
updateTaskList();
}
}
function clearCompletedTasks() {
tasks = tasks.filter(task => !task.completed);
saveTasks();
updateTaskList();
updateStats();
}
function setFilter(filter) {
currentFilter = filter;
// Update active filter button
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.remove('active', 'bg-primary-100', 'text-primary-800');
btn.classList.add('hover:bg-gray-50', 'text-gray-600');
});
const activeBtn = document.getElementById(`filter-${filter}`);
activeBtn.classList.add('active', 'bg-primary-100', 'text-primary-800');
activeBtn.classList.remove('hover:bg-gray-50', 'text-gray-600');
updateTaskList();
}
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
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;
remainingCount.textContent = `${pending} ${pending === 1 ? 'item' : 'items'} left`;
}
function updateTaskList() {
// Filter tasks
let filteredTasks = [...tasks];
if (currentFilter === 'active') {
filteredTasks = filteredTasks.filter(task => !task.completed);
} else if (currentFilter === 'completed') {
filteredTasks = filteredTasks.filter(task => task.completed);
}
// Sort tasks
filteredTasks.sort((a, b) => {
if (currentSort === 'date') {
return new Date(b.createdAt) - new Date(a.createdAt);
} else if (currentSort === 'priority') {
const priorityOrder = { high: 3, medium: 2, low: 1 };
return priorityOrder[b.priority] - priorityOrder[a.priority];
} else if (currentSort === 'name') {
return a.text.localeCompare(b.text);
}
return 0;
});
// Render tasks
if (filteredTasks.length === 0) {
emptyState.classList.remove('hidden');
taskList.innerHTML = '';
taskList.appendChild(emptyState);
} else {
emptyState.classList.add('hidden');
taskList.innerHTML = '';
filteredTasks.forEach((task, index) => {
const taskElement = createTaskElement(task);
taskList.appendChild(taskElement);
// Add slight delay for staggered animation
setTimeout(() => {
taskElement.classList.add('task-animate');
}, index * 50);
});
}
}
function createTaskElement(task) {
const taskElement = document.createElement('div');
taskElement.className = `task-item border-b border-gray-100 last:border-0 ${task.priority}-priority`;
taskElement.innerHTML = `
<div class="p-5 flex items-start group">
<div class="flex items-center h-5 mr-3 mt-0.5">
<input
type="checkbox"
class="checkbox-custom"
${task.completed ? 'checked' : ''}
data-id="${task.id}"
>
</div>
<div class="flex-1 min-w-0">
<div
class="task-text ${task.completed ? 'line-through text-gray-400' : 'text-gray-800'} font-medium"
data-id="${task.id}"
>
${task.text}
</div>
<div class="flex items-center mt-1.5">
<div class="text-xs text-gray-500">
<i class="far fa-clock mr-1"></i>${formatDate(task.createdAt)}
</div>
<span class="ml-2 px-2 py-0.5 rounded-full text-xs font-medium ${getPriorityBadgeClass(task.priority)}">
${getPriorityLabel(task.priority)}
</span>
</div>
</div>
<div class="task-actions flex space-x-1 ml-3">
<button
class="edit-btn p-1.5 rounded-lg text-gray-500 hover:bg-gray-100 hover:text-primary-600 transition-colors ripple"
data-id="${task.id}"
title="Edit task"
>
<i class="fas fa-pencil-alt text-sm"></i>
</button>
<button
class="priority-btn p-1.5 rounded-lg text-gray-500 hover:bg-gray-100 ${getPriorityBtnClass(task.priority)} transition-colors ripple"
data-id="${task.id}"
title="Change priority"
>
<i class="fas fa-flag text-sm"></i>
</button>
<button
class="delete-btn p-1.5 rounded-lg text-gray-500 hover:bg-gray-100 hover:text-red-600 transition-colors ripple"
data-id="${task.id}"
title="Delete task"
>
<i class="fas fa-trash-alt text-sm"></i>
</button>
</div>
</div>
`;
// Add event listeners to the new task
const checkbox = taskElement.querySelector('.checkbox-custom');
const editBtn = taskElement.querySelector('.edit-btn');
const deleteBtn = taskElement.querySelector('.delete-btn');
const priorityBtn = taskElement.querySelector('.priority-btn');
const taskText = taskElement.querySelector('.task-text');
checkbox.addEventListener('change', () => toggleTaskComplete(task.id));
deleteBtn.addEventListener('click', () => deleteTask(task.id));
priorityBtn.addEventListener('click', () => {
currentTaskIdForPriority = task.id;
showPriorityModal();
});
editBtn.addEventListener('click', () => {
const currentText = task.text;
const input = document.createElement('input');
input.type = 'text';
input.value = currentText;
input.className = 'w-full px-2 py-1.5 border border-gray-200 rounded-lg focus:outline-none focus:ring-1 focus:ring-primary-500';
taskText.innerHTML = '';
taskText.appendChild(input);
input.focus();
const handleBlur = () => {
editTask(task.id, input.value);
};
const handleKeyPress = (e) => {
if (e.key === 'Enter') {
editTask(task.id, input.value);
}
};
input.addEventListener('blur', handleBlur);
input.addEventListener('keypress', handleKeyPress);
});
return taskElement;
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric'
});
}
function getPriorityLabel(priority) {
const labels = {
high: 'High',
medium: 'Medium',
low: 'Low'
};
return labels[priority] || priority;
}
function getPriorityBadgeClass(priority) {
switch (priority) {
case 'high': return 'bg-red-100 text-red-800';
case 'medium': return 'bg-amber-100 text-amber-800';
case 'low': return 'bg-green-100 text-green-800';
default: return 'bg-gray-100 text-gray-800';
}
}
function getPriorityBtnClass(priority) {
switch (priority) {
case 'high': return 'hover:text-red-600';
case 'medium': return 'hover:text-amber-600';
case 'low': return 'hover:text-green-600';
default: return '';
}
}
function showConfirm(title, message, callback) {
confirmTitle.textContent = title;
confirmMessage.textContent = message;
confirmCallback = callback;
confirmModal.classList.remove('hidden');
}
function showPriorityModal() {
priorityModal.classList.remove('hidden');
// Add animation to modal
const modalContent = document.getElementById('modal-content');
modalContent.classList.remove('opacity-0', 'scale-95');
modalContent.classList.add('opacity-100', 'scale-100');
}
});
</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=DoraWill/deepsite-todo-app" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>