From e0da93a20a965125ba4cbb46e3e0e179f06c2a08 Mon Sep 17 00:00:00 2001 From: soryu Date: Sun, 18 Jan 2026 18:02:08 +0000 Subject: Improve Mesh Tab: Organize by contract with status filter (#5) * Add status filter toggle to Mesh Tab TaskList component Add a filter toggle at the top of the TaskList that allows filtering by contract status (All, Active, Completed, Archive) with Active as the default. Changes: - Backend: Add contract_status field to TaskSummary struct in models.rs - Backend: Update all SQL queries returning TaskSummary to include c.status as contract_status from the contracts table join - Frontend: Add contractStatus to TaskSummary TypeScript type - Frontend: Add useState for statusFilter with 'active' as default - Frontend: Add filter button group in header area next to '+ New Task' - Frontend: Update groupedTasks useMemo to filter based on contract status - Frontend: Update empty state message to reflect current filter Co-Authored-By: Claude Opus 4.5 * Task completion checkpoint * feat(mesh): show all contract tasks for supervisor tasks When viewing a supervisor task (task.isSupervisor === true), the TaskDetail component now shows all tasks in the contract instead of showing the subtasks tree. Changes: - Add contractTasks prop to TaskDetailProps for passing contract tasks - Add displayTasks computed value that uses contractTasks for supervisors - Change section header from "Subtasks" to "Contract Tasks" for supervisors - Hide the "+ Add Subtask" button for supervisor tasks - Update empty state message for supervisors - Fetch contract tasks in mesh.tsx when viewing a supervisor task - Filter out the supervisor itself from the contract tasks list Co-Authored-By: Claude Opus 4.5 * Task completion checkpoint * Task completion checkpoint --------- Co-authored-by: Claude Opus 4.5 --- makima/frontend/src/components/mesh/TaskList.tsx | 69 +++++++++++++++++++----- 1 file changed, 57 insertions(+), 12 deletions(-) (limited to 'makima/frontend/src/components/mesh/TaskList.tsx') 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('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(); - 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 (
MESH//TASKS
- +
+ {/* Status filter toggle */} +
+ {filterOptions.map((option) => ( + + ))} +
+ +
{totalTasks === 0 ? (
- 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.`}
) : (
-- cgit v1.2.3