diff options
Diffstat (limited to 'makima/frontend/src/components/mesh/TaskList.tsx')
| -rw-r--r-- | makima/frontend/src/components/mesh/TaskList.tsx | 69 |
1 files changed, 57 insertions, 12 deletions
diff --git a/makima/frontend/src/components/mesh/TaskList.tsx b/makima/frontend/src/components/mesh/TaskList.tsx index d013782..f829b29 100644 --- a/makima/frontend/src/components/mesh/TaskList.tsx +++ b/makima/frontend/src/components/mesh/TaskList.tsx @@ -1,5 +1,5 @@ -import { useMemo } from "react"; -import type { TaskSummary, TaskStatus, ContractPhase } from "../../lib/api"; +import { useMemo, useState } from "react"; +import type { TaskSummary, TaskStatus, ContractPhase, ContractStatus } from "../../lib/api"; interface TaskListProps { tasks: TaskSummary[]; @@ -13,9 +13,12 @@ interface GroupedTasks { contractId: string | null; contractName: string | null; contractPhase: ContractPhase | null; + contractStatus: ContractStatus | null; tasks: TaskSummary[]; } +type StatusFilter = 'all' | 'active' | 'completed' | 'archived'; + function formatDate(dateStr: string): string { const date = new Date(dateStr); return date.toLocaleDateString("en-US", { @@ -87,21 +90,36 @@ export function TaskList({ onDelete, onCreate, }: TaskListProps) { - // Group tasks by contract + // Filter state - default to 'active' to show only active contracts + const [statusFilter, setStatusFilter] = useState<StatusFilter>('active'); + + // Group tasks by contract and filter by status const groupedTasks = useMemo(() => { // Separate root tasks (no parent) from subtasks const rootTasks = tasks.filter((t) => !t.parentTaskId); + // Filter tasks based on contract status + const filteredTasks = statusFilter === 'all' + ? rootTasks + : rootTasks.filter((task) => { + // Tasks without a contract go in 'all' only, or treat them as 'active' + if (!task.contractId) { + return statusFilter === 'active'; + } + return task.contractStatus === statusFilter; + }); + // Group by contractId const groups = new Map<string | null, GroupedTasks>(); - for (const task of rootTasks) { + for (const task of filteredTasks) { const key = task.contractId; if (!groups.has(key)) { groups.set(key, { contractId: task.contractId, contractName: task.contractName, contractPhase: task.contractPhase, + contractStatus: task.contractStatus, tasks: [], }); } @@ -132,7 +150,7 @@ export function TaskList({ }); return sorted; - }, [tasks]); + }, [tasks, statusFilter]); if (loading) { return ( @@ -144,24 +162,51 @@ export function TaskList({ const totalTasks = groupedTasks.reduce((sum, g) => sum + g.tasks.length, 0); + const filterOptions: { value: StatusFilter; label: string }[] = [ + { value: 'all', label: 'All' }, + { value: 'active', label: 'Active' }, + { value: 'completed', label: 'Completed' }, + { value: 'archived', label: 'Archive' }, + ]; + return ( <div className="panel h-full flex flex-col"> <div className="flex items-center justify-between p-4 pb-2 border-b border-dashed border-[rgba(117,170,252,0.35)]"> <div className="font-mono text-xs text-[#9bc3ff] tracking-wide uppercase"> MESH//TASKS </div> - <button - onClick={onCreate} - className="px-3 py-1 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] hover:bg-[rgba(117,170,252,0.05)] transition-colors uppercase" - > - + New Task - </button> + <div className="flex items-center gap-2"> + {/* Status filter toggle */} + <div className="flex border border-[rgba(117,170,252,0.25)]"> + {filterOptions.map((option) => ( + <button + key={option.value} + onClick={() => setStatusFilter(option.value)} + className={`px-2 py-1 font-mono text-[10px] uppercase transition-colors ${ + statusFilter === option.value + ? 'bg-[rgba(117,170,252,0.2)] text-[#dbe7ff] border-r border-[rgba(117,170,252,0.25)] last:border-r-0' + : 'text-[#8b949e] hover:text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.05)] border-r border-[rgba(117,170,252,0.25)] last:border-r-0' + }`} + > + {option.label} + </button> + ))} + </div> + <button + onClick={onCreate} + className="px-3 py-1 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] hover:bg-[rgba(117,170,252,0.05)] transition-colors uppercase" + > + + New Task + </button> + </div> </div> <div className="flex-1 overflow-y-auto"> {totalTasks === 0 ? ( <div className="text-center text-[#9bc3ff] text-sm font-mono opacity-60 py-8"> - No tasks yet. Create one to start orchestrating Claude Code instances. + {statusFilter === 'all' + ? 'No tasks yet. Create one to start orchestrating Claude Code instances.' + : `No ${statusFilter} tasks found.`} </div> ) : ( <div> |
