From d82b3dc61165802b51e357554a75422fb074f100 Mon Sep 17 00:00:00 2001 From: lorentz Date: Wed, 8 Apr 2026 21:23:46 +0000 Subject: [PATCH] feat: sortable/filterable column header on task list - Sort by Client, Date, Type (Group/Policy/Client), Level (priority) - Click to sort asc/desc with arrow indicators - Filter dropdowns for Type and Level with Clear button - Applied after card filter and status tab filter --- .../src/app/(dashboard)/tasks/page-client.tsx | 123 +++++++++++++++++- 1 file changed, 119 insertions(+), 4 deletions(-) diff --git a/ondeck/src/app/(dashboard)/tasks/page-client.tsx b/ondeck/src/app/(dashboard)/tasks/page-client.tsx index a1a09d4..60d3a47 100644 --- a/ondeck/src/app/(dashboard)/tasks/page-client.tsx +++ b/ondeck/src/app/(dashboard)/tasks/page-client.tsx @@ -21,7 +21,7 @@ import { SelectValue, } from '@/components/ui/select' import { UserSelectContent } from '@/components/ui/user-select-content' -import { CheckSquare, Clock, AlertCircle, MessageSquare, CheckCircle2, RotateCcw, Eye, CalendarRange, ArrowRightLeft, Search, X, Building2, Plus, Ban } from 'lucide-react' +import { CheckSquare, Clock, AlertCircle, MessageSquare, CheckCircle2, RotateCcw, Eye, CalendarRange, ArrowRightLeft, Search, X, Building2, Plus, Ban, ArrowUpDown, ArrowUp, ArrowDown, Filter } from 'lucide-react' import { Input } from '@/components/ui/input' import { formatDate } from '@/lib/utils' import { AdditionalServiceModal } from '@/components/tasks/additional-service-modal' @@ -450,6 +450,10 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users } const [clientSearch, setClientSearch] = useState('') const [clientOptions, setClientOptions] = useState<{id: string; name: string}[]>([]) const [clientSearchOpen, setClientSearchOpen] = useState(false) + const [sortKey, setSortKey] = useState<'client' | 'date' | 'type' | 'priority'>('date') + const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc') + const [typeFilter, setTypeFilter] = useState<'all' | 'Group' | 'Policy' | 'Client'>('all') + const [priorityFilter, setPriorityFilter] = useState<'all' | 'HIGH' | 'MEDIUM' | 'LOW'>('all') const viewingUser = users.find((u) => u.id === viewingUserId) const isViewingSelf = viewingUserId === currentUserId @@ -552,11 +556,48 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users } return base } + const taskType = (t: Task): 'Group' | 'Policy' | 'Client' => + t.policyGroup ? 'Group' : t.policy ? 'Policy' : 'Client' + + const priorityOrder: Record = { HIGH: 0, MEDIUM: 1, LOW: 2 } + + const handleSort = (key: typeof sortKey) => { + if (sortKey === key) setSortDir((d) => (d === 'asc' ? 'desc' : 'asc')) + else { setSortKey(key); setSortDir('asc') } + } + const baseVisible = filter === 'assigned' ? tasks.filter((t) => !isTerminal(t.status)) : filter === 'completed' ? tasks.filter((t) => t.status === 'COMPLETED') : tasks - const visibleTasks = applyCardFilter(baseVisible) + const afterCardFilter = applyCardFilter(baseVisible) + + const afterColumnFilters = afterCardFilter.filter((t) => { + if (typeFilter !== 'all' && taskType(t) !== typeFilter) return false + if (priorityFilter !== 'all' && t.priority !== priorityFilter) return false + return true + }) + + const visibleTasks = [...afterColumnFilters].sort((a, b) => { + let cmp = 0 + switch (sortKey) { + case 'client': + cmp = (a.client?.name ?? '').localeCompare(b.client?.name ?? '') + break + case 'date': + cmp = new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime() + break + case 'type': { + const typeOrder: Record = { Group: 0, Policy: 1, Client: 2 } + cmp = (typeOrder[taskType(a)] ?? 3) - (typeOrder[taskType(b)] ?? 3) + break + } + case 'priority': + cmp = (priorityOrder[a.priority] ?? 9) - (priorityOrder[b.priority] ?? 9) + break + } + return sortDir === 'asc' ? cmp : -cmp + }) const handleCardFilter = (f: typeof cardFilter) => { setCardFilter((prev) => prev === f ? null : f) @@ -785,12 +826,86 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users } + + {/* Column sort & filter header */} +
+
+ {/* Sort buttons */} + {([ + ['client', 'Client'], + ['date', 'Date'], + ['type', 'Type'], + ['priority', 'Level'], + ] as const).map(([key, label]) => ( + + ))} + + + + {/* Type filter */} + + + {/* Priority filter */} + + + {(typeFilter !== 'all' || priorityFilter !== 'all') && ( + + )} +
+
+ {visibleTasks.length === 0 ? (
-

No tasks assigned

-

Tasks will appear here when they are assigned to you

+

No tasks found

+

+ {typeFilter !== 'all' || priorityFilter !== 'all' + ? 'Try adjusting the filters above' + : 'Tasks will appear here when they are assigned to you'} +

) : (