diff --git a/web/src/components/board/KanbanBoard.tsx b/web/src/components/board/KanbanBoard.tsx index 7a91c6ad..113cb845 100644 --- a/web/src/components/board/KanbanBoard.tsx +++ b/web/src/components/board/KanbanBoard.tsx @@ -80,6 +80,21 @@ export function KanbanBoard() { setDetailOpen(true); }, []); + // Listen for open-task events from dashboard drill-downs + useEffect(() => { + const handler = (e: Event) => { + const taskId = (e as CustomEvent).detail?.taskId; + if (!taskId) return; + const task = tasks?.find((t) => t.id === taskId); + if (task) { + setSelectedTask(task); + setDetailOpen(true); + } + }; + window.addEventListener('open-task', handler); + return () => window.removeEventListener('open-task', handler); + }, [tasks]); + const updateTask = useUpdateTask(); const reorderTasks = useReorderTasks(); diff --git a/web/src/components/dashboard/ErrorsDrillDown.tsx b/web/src/components/dashboard/ErrorsDrillDown.tsx index 44a13ece..ca742a43 100644 --- a/web/src/components/dashboard/ErrorsDrillDown.tsx +++ b/web/src/components/dashboard/ErrorsDrillDown.tsx @@ -37,17 +37,16 @@ export function ErrorsDrillDown({ period, project, onTaskClick }: ErrorsDrillDow {/* Summary */}
- {failedRuns.length} failed run(s) in the {period === '24h' ? 'last 24 hours' : 'last 7 days'} + + {failedRuns.length} failed run(s) in the{' '} + {period === '24h' ? 'last 24 hours' : 'last 7 days'} +
{/* Failed Runs List */}
{failedRuns.map((run, index) => ( - + ))}
@@ -71,28 +70,24 @@ function FailedRunRow({ run, onTaskClick }: FailedRunRowProps) { const canNavigate = run.taskId && onTaskClick; const content = ( -
+
- - {run.taskId || 'Unknown task'} - - {canNavigate && ( - - )} + {run.taskId || 'Unknown task'} + {canNavigate && }
- + {run.errorMessage && ( -

- {run.errorMessage} -

+

{run.errorMessage}

)} - +
@@ -111,7 +106,7 @@ function FailedRunRow({ run, onTaskClick }: FailedRunRowProps) { )}
- +
{date.toLocaleDateString()}
{date.toLocaleTimeString()}
@@ -124,7 +119,7 @@ function FailedRunRow({ run, onTaskClick }: FailedRunRowProps) { return ( diff --git a/web/src/components/dashboard/TasksDrillDown.tsx b/web/src/components/dashboard/TasksDrillDown.tsx index e4726e43..3bc6127b 100644 --- a/web/src/components/dashboard/TasksDrillDown.tsx +++ b/web/src/components/dashboard/TasksDrillDown.tsx @@ -13,67 +13,74 @@ interface TasksDrillDownProps { onTaskClick?: (taskId: string) => void; } -const statusConfig: Record = { - 'todo': { - icon: , +const statusConfig: Record< + TaskStatus, + { + icon: React.ReactNode; + color: string; + label: string; + } +> = { + todo: { + icon: , color: 'text-muted-foreground', - label: 'To Do' + label: 'To Do', }, - 'in-progress': { - icon: , + 'in-progress': { + icon: , color: 'text-blue-500', - label: 'In Progress' + label: 'In Progress', }, - 'blocked': { - icon: , + blocked: { + icon: , color: 'text-red-500', - label: 'Blocked' + label: 'Blocked', }, - 'done': { - icon: , + done: { + icon: , color: 'text-green-500', - label: 'Done' + label: 'Done', }, }; -export function TasksDrillDown({ project, statusFilter = 'all', onTaskClick }: TasksDrillDownProps) { +export function TasksDrillDown({ + project, + statusFilter = 'all', + onTaskClick, +}: TasksDrillDownProps) { const { data: tasks, isLoading } = useTasks(); const { data: projects = [] } = useProjects(); const filteredTasks = useMemo(() => { if (!tasks) return []; - + let filtered = tasks; - + // Filter by project if (project) { - filtered = filtered.filter(t => t.project === project); + filtered = filtered.filter((t) => t.project === project); } - + // Filter by status if (statusFilter !== 'all') { - filtered = filtered.filter(t => t.status === statusFilter); + filtered = filtered.filter((t) => t.status === statusFilter); } - + // Sort by updated date (most recent first) - return [...filtered].sort((a, b) => - new Date(b.updated).getTime() - new Date(a.updated).getTime() + return [...filtered].sort( + (a, b) => new Date(b.updated).getTime() - new Date(a.updated).getTime() ); }, [tasks, project, statusFilter]); // Group by status for summary const statusCounts = useMemo(() => { const counts: Record = { - 'todo': 0, + todo: 0, 'in-progress': 0, - 'blocked': 0, - 'done': 0, + blocked: 0, + done: 0, }; - filteredTasks.forEach(t => counts[t.status]++); + filteredTasks.forEach((t) => counts[t.status]++); return counts; }, [filteredTasks]); @@ -109,14 +116,12 @@ export function TasksDrillDown({ project, statusFilter = 'all', onTaskClick }: T {/* Task List */}
{filteredTasks.length === 0 ? ( -
- No tasks found -
+
No tasks found
) : ( filteredTasks.map((task) => ( - onTaskClick?.(task.id)} /> @@ -127,25 +132,31 @@ export function TasksDrillDown({ project, statusFilter = 'all', onTaskClick }: T ); } -function TaskRow({ task, projects = [], onClick }: { task: Task; projects?: Array<{ id: string; label: string }>; onClick?: () => void }) { +function TaskRow({ + task, + projects = [], + onClick, +}: { + task: Task; + projects?: Array<{ id: string; label: string }>; + onClick?: () => void; +}) { const config = statusConfig[task.status]; - const projectLabel = task.project - ? projects.find(p => p.id === task.project)?.label || task.project + const projectLabel = task.project + ? projects.find((p) => p.id === task.project)?.label || task.project : null; - + return (