207 lines
7.2 KiB
HTML
207 lines
7.2 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Tasks - Raccoon Timekeeper{% endblock %}
|
||
{% block tagline %}Manage your tasks{% endblock %}
|
||
|
||
{% block content %}
|
||
<section class="card">
|
||
<div class="card-header">
|
||
<h2>Your Tasks</h2>
|
||
<button class="btn btn-primary" onclick="openAddTaskModal()">
|
||
<span class="btn-icon">+</span>
|
||
Add Task
|
||
</button>
|
||
</div>
|
||
|
||
<div id="tasksList" class="tasks-list">
|
||
<div class="loading">Loading tasks...</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Add Task Modal -->
|
||
<div id="addTaskModal" class="modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>Add New Task</h3>
|
||
<button class="modal-close" onclick="closeModal('addTaskModal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="form-group">
|
||
<label for="newTaskName">Task Name</label>
|
||
<input type="text" id="newTaskName" placeholder="Enter task name...">
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="closeModal('addTaskModal')">Cancel</button>
|
||
<button class="btn btn-primary" onclick="saveNewTask()">Add Task</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Edit Task Modal -->
|
||
<div id="editTaskModal" class="modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>Edit Task</h3>
|
||
<button class="modal-close" onclick="closeModal('editTaskModal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<input type="hidden" id="editTaskId">
|
||
<div class="form-group">
|
||
<label for="editTaskName">Task Name</label>
|
||
<input type="text" id="editTaskName" placeholder="Enter task name...">
|
||
</div>
|
||
<div class="form-group checkbox-group">
|
||
<label class="checkbox-label">
|
||
<input type="checkbox" id="editTaskActive">
|
||
<span>Active</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-danger" onclick="deleteTask()">Delete</button>
|
||
<button class="btn btn-secondary" onclick="closeModal('editTaskModal')">Cancel</button>
|
||
<button class="btn btn-primary" onclick="saveEditTask()">Save</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', loadTasks);
|
||
|
||
async function loadTasks() {
|
||
try {
|
||
const response = await fetch('/api/tasks?include_inactive=true');
|
||
const tasks = await response.json();
|
||
renderTasks(tasks);
|
||
} catch (error) {
|
||
showToast('Error loading tasks', 'error');
|
||
}
|
||
}
|
||
|
||
function renderTasks(tasks) {
|
||
const container = document.getElementById('tasksList');
|
||
|
||
if (!tasks.length) {
|
||
container.innerHTML = '<div class="empty-state">📋 No tasks yet. Create one!</div>';
|
||
return;
|
||
}
|
||
|
||
let html = '';
|
||
tasks.forEach(task => {
|
||
html += `
|
||
<div class="task-item ${task.active ? '' : 'inactive'}" data-id="${task.id}">
|
||
<div class="task-info">
|
||
<span class="task-name">${task.name}</span>
|
||
${!task.active ? '<span class="task-badge inactive-badge">Inactive</span>' : ''}
|
||
</div>
|
||
<button class="btn btn-icon" onclick="openEditTaskModal(${task.id}, '${task.name.replace(/'/g, "\\'")}', ${task.active})">
|
||
✏️
|
||
</button>
|
||
</div>
|
||
`;
|
||
});
|
||
|
||
container.innerHTML = html;
|
||
}
|
||
|
||
function openAddTaskModal() {
|
||
document.getElementById('addTaskModal').classList.add('active');
|
||
document.getElementById('newTaskName').value = '';
|
||
document.getElementById('newTaskName').focus();
|
||
}
|
||
|
||
function openEditTaskModal(id, name, active) {
|
||
document.getElementById('editTaskId').value = id;
|
||
document.getElementById('editTaskName').value = name;
|
||
document.getElementById('editTaskActive').checked = active;
|
||
document.getElementById('editTaskModal').classList.add('active');
|
||
}
|
||
|
||
function closeModal(modalId) {
|
||
document.getElementById(modalId).classList.remove('active');
|
||
}
|
||
|
||
async function saveNewTask() {
|
||
const name = document.getElementById('newTaskName').value.trim();
|
||
if (!name) { showToast('Enter a task name', 'error'); return; }
|
||
|
||
try {
|
||
const response = await fetch('/api/tasks', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ name })
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showToast('Task added!', 'success');
|
||
closeModal('addTaskModal');
|
||
loadTasks();
|
||
} else {
|
||
showToast(result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showToast('Error adding task', 'error');
|
||
}
|
||
}
|
||
|
||
async function saveEditTask() {
|
||
const id = document.getElementById('editTaskId').value;
|
||
const name = document.getElementById('editTaskName').value.trim();
|
||
const active = document.getElementById('editTaskActive').checked;
|
||
|
||
if (!name) { showToast('Enter a task name', 'error'); return; }
|
||
|
||
try {
|
||
const response = await fetch(`/api/tasks/${id}`, {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ name, active })
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showToast('Task updated!', 'success');
|
||
closeModal('editTaskModal');
|
||
loadTasks();
|
||
} else {
|
||
showToast(result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showToast('Error updating task', 'error');
|
||
}
|
||
}
|
||
|
||
async function deleteTask() {
|
||
if (!confirm('Delete this task? This will also delete all time entries for this task.')) return;
|
||
|
||
const id = document.getElementById('editTaskId').value;
|
||
|
||
try {
|
||
const response = await fetch(`/api/tasks/${id}`, { method: 'DELETE' });
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showToast('Task deleted', 'success');
|
||
closeModal('editTaskModal');
|
||
loadTasks();
|
||
} else {
|
||
showToast(result.message, 'error');
|
||
}
|
||
} catch (error) {
|
||
showToast('Error deleting task', 'error');
|
||
}
|
||
}
|
||
|
||
document.getElementById('newTaskName').addEventListener('keypress', e => {
|
||
if (e.key === 'Enter') { e.preventDefault(); saveNewTask(); }
|
||
});
|
||
document.getElementById('editTaskName').addEventListener('keypress', e => {
|
||
if (e.key === 'Enter') { e.preventDefault(); saveEditTask(); }
|
||
});
|
||
</script>
|
||
{% endblock %}
|