import { createFileRoute } from '@tanstack/react-router'; import { useState, useCallback, useMemo, useRef, useEffect } from 'react'; import { useFloatingChat } from '@/context/FloatingChatContext'; import { ClipboardCheck, ListTodo, Loader2, CheckCircle2, Plus, Search, } from 'lucide-react'; import { trpc } from '@/lib/trpc'; import { Item, ItemMedia, ItemContent, ItemTitle, ItemDescription } from '@/components/ui/item'; import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from '@/components/ui/empty'; import { NewTaskDialog } from '@/components/tasks/NewTaskDialog'; import { EditTaskDialog } from '@/components/tasks/EditTaskDialog'; import { TaskDetailDialog } from '@/components/tasks/TaskDetailDialog'; import { TaskRow, type TaskItem } from '@/components/tasks/TaskRow'; export const Route = createFileRoute('/tasks')({ component: TasksPage, }); type StatusFilter = 'all' | 'todo' | 'in_progress' | 'done'; type OrderBy = 'dueDate' | 'priority' | 'createdAt'; const ORDER_LABELS: Record = { dueDate: 'Due Date', priority: 'Priority', createdAt: 'Created Date', }; function TasksPage() { // Temporary test: register section for floating AI chat const testRef = useRef(null); const { registerSection, unregisterSection } = useFloatingChat(); useEffect(() => { registerSection({ id: 'test', label: 'Tasks', ref: testRef }); return () => unregisterSection('test'); }, [registerSection, unregisterSection]); const [search, setSearch] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [orderBy, setOrderBy] = useState('dueDate'); const [dialogOpen, setDialogOpen] = useState(false); const [editTask, setEditTask] = useState(null); const [viewTask, setViewTask] = useState(null); const debounceTimer = useMemo(() => ({ id: null as ReturnType | null }), []); const handleSearchChange = useCallback( (value: string) => { setSearch(value); if (debounceTimer.id) clearTimeout(debounceTimer.id); debounceTimer.id = setTimeout(() => setDebouncedSearch(value), 300); }, [debounceTimer], ); const queryInput = useMemo( () => ({ ...(statusFilter !== 'all' ? { status: statusFilter as 'todo' | 'in_progress' | 'done' } : {}), ...(debouncedSearch.trim() ? { search: debouncedSearch.trim() } : {}), orderBy, }), [statusFilter, debouncedSearch, orderBy], ); const { data: allTasks } = trpc.tasks.list.useQuery({}); const { data: filteredTasks } = trpc.tasks.list.useQuery(queryInput); const utils = trpc.useUtils(); const updateTask = trpc.tasks.update.useMutation({ onSuccess: () => { void utils.tasks.list.invalidate(); }, }); const deleteTask = trpc.tasks.delete.useMutation({ onSuccess: () => { void utils.tasks.list.invalidate(); }, }); const tasksList = (filteredTasks ?? []).filter( (t) => !(t.isAiSuggested === 1 && t.isApproved === 0), ); // Compute stats from all tasks (unfiltered) const stats = useMemo(() => { const all = allTasks ?? []; return { total: all.length, todo: all.filter((t) => t.status === 'todo').length, inProgress: all.filter((t) => t.status === 'in_progress').length, completed: all.filter((t) => t.status === 'done').length, }; }, [allTasks]); const handleCheckboxToggle = useCallback( (taskId: string, currentStatus: string | null) => { const nextStatus = currentStatus === 'todo' ? 'in_progress' : currentStatus === 'in_progress' ? 'done' : 'todo'; updateTask.mutate({ id: taskId, status: nextStatus }); }, [updateTask], ); return (
{/* Stat Cards */}
{stats.total} Total Tasks {stats.todo} To Do {stats.inProgress} In Progress {stats.completed} Completed
{/* Search + Order By */}
handleSearchChange(e.target.value)} />
{/* Status Filter Tabs + New Task Button */}
setStatusFilter(v as StatusFilter)}> All To Do In Progress Completed
{/* Task List */}
{tasksList.length === 0 ? ( No tasks found Create a new task to get started or adjust your filters. ) : ( tasksList.map((task) => ( deleteTask.mutate({ id })} onClick={setViewTask} /> )) )}
{ if (!open) setEditTask(null); }} /> { if (!open) setViewTask(null); }} onEdit={(task) => { setViewTask(null); setEditTask(task); }} onDelete={(id) => { deleteTask.mutate({ id }); setViewTask(null); }} />
); }