my-app-802 / assets /js /app.js
AnkhLP's picture
Upload folder using huggingface_hub
3e266bd verified
class TodoApp {
constructor() {
this.tasks = [];
this.currentFilter = 'all';
this.initElements();
this.initEventListeners();
this.loadTasks();
this.initFireworks();
}
initElements() {
this.taskInput = document.getElementById('taskInput');
this.addBtn = document.getElementById('addBtn');
this.taskList = document.getElementById('taskList');
this.emptyState = document.getElementById('emptyState');
this.clearCompletedBtn = document.getElementById('clearCompleted');
this.totalTasksEl = document.getElementById('totalTasks');
this.completedTasksEl = document.getElementById('completedTasks');
this.filterBtns = document.querySelectorAll('.filter-btn');
}
initEventListeners() {
this.addBtn.addEventListener('click', () => this.addTask());
this.taskInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.addTask();
});
this.clearCompletedBtn.addEventListener('click', () => this.clearCompleted());
this.filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
this.filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
this.currentFilter = btn.dataset.filter;
this.render();
});
});
}
initFireworks() {
const canvas = document.getElementById('fireworks');
this.fireworks = new Fireworks(canvas);
}
loadTasks() {
const stored = localStorage.getItem('tasks');
if (stored) {
this.tasks = JSON.parse(stored);
}
this.render();
}
saveTasks() {
localStorage.setItem('tasks', JSON.stringify(this.tasks));
}
addTask() {
const text = this.taskInput.value.trim();
if (!text) return;
const task = {
id: Date.now(),
text: text,
createdAt: new Date().toISOString(),
completedAt: null,
completed: false
};
this.tasks.unshift(task);
this.taskInput.value = '';
this.saveTasks();
this.render();
}
toggleTask(id) {
const task = this.tasks.find(t => t.id === id);
if (!task) return;
task.completed = !task.completed;
task.completedAt = task.completed ? new Date().toISOString() : null;
if (task.completed) {
this.fireworks.start();
}
this.saveTasks();
this.render();
}
deleteTask(id) {
this.tasks = this.tasks.filter(t => t.id !== id);
this.saveTasks();
this.render();
}
clearCompleted() {
this.tasks = this.tasks.filter(t => !t.completed);
this.saveTasks();
this.render();
}
formatDate(dateString) {
const date = new Date(dateString);
const now = new Date();
const diff = now - date;
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 1) return 'just now';
if (minutes < 60) return `${minutes}m ago`;
if (hours < 24) return `${hours}h ago`;
if (days < 7) return `${days}d ago`;
return date.toLocaleDateString();
}
getFilteredTasks() {
switch (this.currentFilter) {
case 'active':
return this.tasks.filter(t => !t.completed);
case 'completed':
return this.tasks.filter(t => t.completed);
default:
return this.tasks;
}
}
updateStats() {
const total = this.tasks.length;
const completed = this.tasks.filter(t => t.completed).length;
this.totalTasksEl.textContent = `${total} ${total === 1 ? 'task' : 'tasks'}`;
this.completedTasksEl.textContent = `${completed} completed`;
}
render() {
const filteredTasks = this.getFilteredTasks();
this.taskList.innerHTML = '';
this.emptyState.classList.toggle('show', filteredTasks.length === 0);
filteredTasks.forEach(task => {
const taskEl = document.createElement('div');
taskEl.className = `task-item ${task.completed ? 'completed' : ''}`;
taskEl.innerHTML = `
<div class="task-checkbox ${task.completed ? 'checked' : ''}"
onclick="app.toggleTask(${task.id})"></div>
<div class="task-content">
<div class="task-text">${this.escapeHtml(task.text)}</div>
<div class="task-timestamps">
<div class="timestamp">
📅 Created: ${this.formatDate(task.createdAt)}
</div>
${task.completedAt ? `
<div class="timestamp">
✅ Completed: ${this.formatDate(task.completedAt)}
</div>
` : ''}
</div>
</div>
<button class="delete-btn" onclick="app.deleteTask(${task.id})">Delete</button>
`;
this.taskList.appendChild(taskEl);
});
this.updateStats();
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
// Initialize app
const app = new TodoApp();