diff --git a/ondeck/src/app/(dashboard)/tasks/page-client.tsx b/ondeck/src/app/(dashboard)/tasks/page-client.tsx index 950ad71..293b5d2 100644 --- a/ondeck/src/app/(dashboard)/tasks/page-client.tsx +++ b/ondeck/src/app/(dashboard)/tasks/page-client.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState, useCallback } from 'react' +import { useState, useCallback, useEffect } from 'react' import { toast } from 'sonner' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' @@ -22,7 +22,8 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select' -import { CheckSquare, Clock, AlertCircle, MessageSquare, CheckCircle2, RotateCcw, Eye, CalendarRange, ArrowRightLeft } from 'lucide-react' +import { CheckSquare, Clock, AlertCircle, MessageSquare, CheckCircle2, RotateCcw, Eye, CalendarRange, ArrowRightLeft, Search, X, Building2 } from 'lucide-react' +import { Input } from '@/components/ui/input' import { formatDate } from '@/lib/utils' interface TaskUser { displayName: string | null; email: string } @@ -291,14 +292,21 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users } const [transferring, setTransferring] = useState(false) const [viewingUserId, setViewingUserId] = useState(currentUserId) const [loadingTasks, setLoadingTasks] = useState(false) + const [clientFilter, setClientFilter] = useState('') + const [clientSearch, setClientSearch] = useState('') + const [clientOptions, setClientOptions] = useState<{id: string; name: string}[]>([]) + const [clientSearchOpen, setClientSearchOpen] = useState(false) const viewingUser = users.find((u) => u.id === viewingUserId) const isViewingSelf = viewingUserId === currentUserId + const selectedClient = clientOptions.find((c) => c.id === clientFilter) - const fetchForUser = useCallback(async (uid: string) => { + const fetchTasks = useCallback(async (uid: string, cid?: string) => { setLoadingTasks(true) try { - const params = new URLSearchParams({ userId: uid, limit: '100' }) + const params = new URLSearchParams({ limit: '200' }) + params.set('userId', uid) + if (cid) params.set('clientId', cid) const res = await fetch(`/api/tasks?${params}`) const data = await res.json() if (!res.ok) throw new Error(data.error) @@ -312,13 +320,28 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users } const handleUserChange = (uid: string) => { setViewingUserId(uid) - if (uid === currentUserId) { - setTasks(initialTasks) - } else { - fetchForUser(uid) - } + fetchTasks(uid, clientFilter || undefined) } + const handleClientFilterChange = (cid: string) => { + setClientFilter(cid) + fetchTasks(viewingUserId, cid || undefined) + setClientSearchOpen(false) + setClientSearch('') + } + + useEffect(() => { + if (!clientSearch.trim()) { setClientOptions([]); return } + const t = setTimeout(async () => { + try { + const res = await fetch(`/api/clients?search=${encodeURIComponent(clientSearch)}&limit=20`) + const data = await res.json() + setClientOptions((data.clients || []).map((c: any) => ({ id: c.id, name: c.name }))) + } catch {} + }, 250) + return () => clearTimeout(t) + }, [clientSearch]) + const handleTransfer = async () => { if (!transferTo || !visibleTasks.length) return setTransferring(true) @@ -341,7 +364,7 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users } if (viewingUserId === currentUserId) { setTasks((prev) => prev.filter((t) => !visibleTasks.find((v) => v.id === t.id))) } else { - fetchForUser(viewingUserId) + fetchTasks(viewingUserId, clientFilter || undefined) } } catch (err: any) { toast.error(err.message || 'Transfer failed') @@ -437,6 +460,47 @@ export function TasksClient({ initialTasks, currentUserId, isPrivileged, users } )} + {/* Client filter */} +
+
+ + { + setClientSearch(e.target.value) + if (!e.target.value) { setClientFilter(''); fetchTasks(viewingUserId) } + setClientSearchOpen(true) + }} + onFocus={() => { if (!clientFilter) setClientSearchOpen(true) }} + className="pl-9 pr-8" + readOnly={!!clientFilter} + /> + {clientFilter && ( + + )} +
+ {clientSearchOpen && clientOptions.length > 0 && !clientFilter && ( +
+ {clientOptions.map((c) => ( + + ))} +
+ )} +
+ {/* Stats / filter cards */}
{/* Overdue */}