feat: add task filtering and search (#16)
This commit is contained in:
107
public/app.js
107
public/app.js
@@ -68,6 +68,112 @@ const COLUMNS = {
|
||||
};
|
||||
|
||||
let wikiPages = [];
|
||||
let allTasks = [];
|
||||
let taskFilters = { search: '', status: '', assignee: '', priority: '' };
|
||||
|
||||
async function loadTasks() {
|
||||
const res = await fetch('/api/tasks');
|
||||
allTasks = await res.json();
|
||||
|
||||
// Populate assignee filter dropdown
|
||||
const assigneeFilter = document.getElementById('filter-assignee');
|
||||
if (assigneeFilter) {
|
||||
const assignees = [...new Set(allTasks.map(t => t.assignee).filter(Boolean))];
|
||||
assigneeFilter.innerHTML = '<option value="">All Assignees</option>' +
|
||||
assignees.map(a => `<option value="${a}">${a}</option>`).join('');
|
||||
}
|
||||
|
||||
applyTaskFilters();
|
||||
}
|
||||
|
||||
function applyTaskFilters() {
|
||||
let filtered = [...allTasks];
|
||||
|
||||
// Search filter
|
||||
if (taskFilters.search) {
|
||||
const search = taskFilters.search.toLowerCase();
|
||||
filtered = filtered.filter(t =>
|
||||
t.title.toLowerCase().includes(search) ||
|
||||
(t.description && t.description.toLowerCase().includes(search))
|
||||
);
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if (taskFilters.status) {
|
||||
filtered = filtered.filter(t => t.status === taskFilters.status);
|
||||
}
|
||||
|
||||
// Assignee filter
|
||||
if (taskFilters.assignee) {
|
||||
filtered = filtered.filter(t => t.assignee === taskFilters.assignee);
|
||||
}
|
||||
|
||||
// Priority filter
|
||||
if (taskFilters.priority) {
|
||||
filtered = filtered.filter(t => t.priority === taskFilters.priority);
|
||||
}
|
||||
|
||||
// Reset columns
|
||||
Object.keys(COLUMNS).forEach((status) => {
|
||||
COLUMNS[status].tasks = [];
|
||||
});
|
||||
|
||||
// Distribute filtered tasks to columns
|
||||
filtered.forEach((task) => {
|
||||
if (COLUMNS[task.status]) {
|
||||
COLUMNS[task.status].tasks.push(task);
|
||||
}
|
||||
});
|
||||
|
||||
renderBoard();
|
||||
}
|
||||
|
||||
function initTaskFilters() {
|
||||
const searchInput = document.getElementById('task-search');
|
||||
const statusFilter = document.getElementById('filter-status');
|
||||
const assigneeFilter = document.getElementById('filter-assignee');
|
||||
const priorityFilter = document.getElementById('filter-priority');
|
||||
const clearBtn = document.getElementById('clear-filters');
|
||||
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
taskFilters.search = e.target.value;
|
||||
applyTaskFilters();
|
||||
});
|
||||
}
|
||||
|
||||
if (statusFilter) {
|
||||
statusFilter.addEventListener('change', (e) => {
|
||||
taskFilters.status = e.target.value;
|
||||
applyTaskFilters();
|
||||
});
|
||||
}
|
||||
|
||||
if (assigneeFilter) {
|
||||
assigneeFilter.addEventListener('change', (e) => {
|
||||
taskFilters.assignee = e.target.value;
|
||||
applyTaskFilters();
|
||||
});
|
||||
}
|
||||
|
||||
if (priorityFilter) {
|
||||
priorityFilter.addEventListener('change', (e) => {
|
||||
taskFilters.priority = e.target.value;
|
||||
applyTaskFilters();
|
||||
});
|
||||
}
|
||||
|
||||
if (clearBtn) {
|
||||
clearBtn.addEventListener('click', () => {
|
||||
taskFilters = { search: '', status: '', assignee: '', priority: '' };
|
||||
if (searchInput) searchInput.value = '';
|
||||
if (statusFilter) statusFilter.value = '';
|
||||
if (assigneeFilter) assigneeFilter.value = '';
|
||||
if (priorityFilter) priorityFilter.value = '';
|
||||
applyTaskFilters();
|
||||
});
|
||||
}
|
||||
}
|
||||
let currentWikiPage = null;
|
||||
let allAgents = [];
|
||||
let usageStats = null;
|
||||
@@ -225,6 +331,7 @@ function initTasksPage() {
|
||||
loadTasks();
|
||||
});
|
||||
|
||||
initTaskFilters();
|
||||
populateAgentDropdown();
|
||||
loadTasks();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user