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 (