Add project filter, Shared/Assigned views, and task search

- Backend: GET /api/tasks accepts ?search=, ILIKE on title/notes
- Generalized CategoryFilter into a reusable FilterChips component,
  added ProjectFilter alongside it (both now on Board/List/Today)
- New Shared-with-me / Assigned-to-me nav views (FilteredListView),
  surfacing the view=shared/view=assigned params that already existed
  backend-side but had no UI
- Global debounced (300ms) search bar in the layout header, wired into
  Board/List/Today/Dispatch's task fetches via SearchContext
This commit is contained in:
jtricerolph 2026-09-17 09:25:12 +00:00
parent 017f6dfdc1
commit 4eab3c29ed
12 changed files with 227 additions and 53 deletions

View file

@ -74,9 +74,9 @@ export async function taskRoutes(app) {
// ?view=shared = tasks I've been tagged into (not mine, not assigned to me).
// ?view=all = every user's tasks, dispatcher board for the 'reallocate' cap.
// Otherwise defaults to everything I own. ?list_id/?status/?category_id/
// ?project_id narrow further.
// ?project_id/?search narrow further (search matches title or notes, case-insensitive).
app.get('/api/tasks', { preHandler: requireCap('use') }, async (req, reply) => {
const { list_id, view, status, category_id, project_id } = req.query
const { list_id, view, status, category_id, project_id, search } = req.query
const vals = [req.user.email]
let i = 2
const clauses = []
@ -101,6 +101,7 @@ export async function taskRoutes(app) {
if (status) { clauses.push(`t.status = $${i++}`); vals.push(status) }
if (category_id) { clauses.push(`t.category_id = $${i++}`); vals.push(category_id) }
if (project_id) { clauses.push(`t.project_id = $${i++}`); vals.push(project_id) }
if (search) { const idx = i++; clauses.push(`(t.title ILIKE $${idx} OR t.notes ILIKE $${idx})`); vals.push(`%${search}%`) }
if (view === 'today') {
clauses.push(`NOT s.is_done`)
clauses.push(`t.due_at IS NOT NULL`)