diff --git a/src/db/repositories/runsRepository.ts b/src/db/repositories/runsRepository.ts index 9faba267..3c8b07ab 100644 --- a/src/db/repositories/runsRepository.ts +++ b/src/db/repositories/runsRepository.ts @@ -548,7 +548,7 @@ export async function getRunsByWorkItem(projectId: string, workItemId: string) { .from(agentRuns) .leftJoin(prWorkItems, buildAgentRunWorkItemJoin()) .where(and(eq(agentRuns.projectId, projectId), eq(agentRuns.workItemId, workItemId))) - .orderBy(desc(agentRuns.startedAt)); + .orderBy(asc(agentRuns.startedAt)); } /** @@ -562,5 +562,5 @@ export async function getRunsForPR(projectId: string, prNumber: number) { .from(agentRuns) .leftJoin(prWorkItems, buildAgentRunWorkItemJoin()) .where(and(eq(agentRuns.projectId, projectId), eq(agentRuns.prNumber, prNumber))) - .orderBy(desc(agentRuns.startedAt)); + .orderBy(asc(agentRuns.startedAt)); } diff --git a/web/src/components/projects/project-work-table.tsx b/web/src/components/projects/project-work-table.tsx index 98a18663..8ecfce45 100644 --- a/web/src/components/projects/project-work-table.tsx +++ b/web/src/components/projects/project-work-table.tsx @@ -1,20 +1,7 @@ -import { CancelRunButton } from '@/components/runs/cancel-run-button.js'; -import { LiveDuration } from '@/components/runs/live-duration.js'; -import { RetryRunButton } from '@/components/runs/retry-run-button.js'; -import { RunStatusBadge } from '@/components/runs/run-status-badge.js'; -import { trpc } from '@/lib/trpc.js'; -import { formatCost, formatRelativeTime } from '@/lib/utils.js'; -import { useQuery } from '@tanstack/react-query'; +import { formatCost } from '@/lib/utils.js'; +import { useNavigate } from '@tanstack/react-router'; import { Link } from '@tanstack/react-router'; -import { - Activity, - ChevronDown, - ChevronRight, - ClipboardList, - ExternalLink, - GitPullRequest, -} from 'lucide-react'; -import React, { useEffect, useState } from 'react'; +import { ClipboardList, ExternalLink, GitPullRequest } from 'lucide-react'; interface WorkItem { id: string; @@ -39,101 +26,6 @@ interface ProjectWorkTableProps { onPageChange: (offset: number) => void; } -// ============================================================================ -// ExpandedRunsRow sub-component -// ============================================================================ - -interface ExpandedRunsRowProps { - projectId: string; - prNumber: number | null; - workItemId: string | null; -} - -function ExpandedRunsRow({ projectId, prNumber, workItemId }: ExpandedRunsRowProps) { - const runsQuery = useQuery( - workItemId - ? trpc.workItems.runs.queryOptions({ projectId, workItemId }) - : prNumber !== null - ? trpc.prs.runs.queryOptions({ projectId, prNumber }) - : trpc.workItems.runs.queryOptions({ projectId, workItemId: '' }), - ); - - return ( - - - {runsQuery.isLoading && ( -
Loading runs...
- )} - {runsQuery.isError && ( -
- Failed to load runs: {runsQuery.error.message} -
- )} - {runsQuery.data && runsQuery.data.length === 0 && ( -
No runs found
- )} - {runsQuery.data && runsQuery.data.length > 0 && ( -
- - - - - - - - - - - - - - {runsQuery.data.map((run) => ( - - - - - - - - - - ))} - -
AgentStatusStarted - Duration - CostItersActions
- - {run.agentType} - - - - - {formatRelativeTime(run.startedAt)} - - - - {formatCost(run.costUsd)} - - {run.llmIterations ?? '-'} - - - -
-
- )} - - - ); -} - // ============================================================================ // WorkItemRow sub-component (extracted to reduce complexity) // ============================================================================ @@ -141,21 +33,9 @@ function ExpandedRunsRow({ projectId, prNumber, workItemId }: ExpandedRunsRowPro interface WorkItemRowProps { item: WorkItem; projectId: string; - isExpanded: boolean; - onToggle: (id: string) => void; } -function ItemIcon({ - item, - isExpanded, - canExpand, -}: Pick & { - canExpand: boolean; -}) { - if (canExpand) { - return isExpanded ? : ; - } - +function ItemIcon({ item }: Pick) { if (item.type === 'linked' || item.type === 'work-item') { return ( @@ -260,51 +140,30 @@ function SecondaryItemTitle({ item }: Pick) { ); } -function ActivityLink({ item, projectId }: { item: WorkItem; projectId: string }) { - if (item.runCount === 0) return null; - - if ((item.type === 'work-item' || item.type === 'linked') && item.workItemId) { - return ( - e.stopPropagation()} - title="View all runs for this work item" - className="inline-flex items-center text-muted-foreground hover:text-primary" - > - - - ); - } - - if (item.type === 'pr' && item.prNumber != null) { - return ( - e.stopPropagation()} - title="View all runs for this PR" - className="inline-flex items-center text-muted-foreground hover:text-primary" - > - - - ); - } - - return null; -} - -function WorkItemRow({ item, projectId, isExpanded, onToggle }: WorkItemRowProps) { - const canExpand = item.runCount > 0; +function WorkItemRow({ item, projectId }: WorkItemRowProps) { + const navigate = useNavigate(); + const canNavigate = item.runCount > 0; const handleClick = () => { - if (canExpand) onToggle(item.id); + if (!canNavigate) return; + + if ((item.type === 'work-item' || item.type === 'linked') && item.workItemId) { + navigate({ + to: '/work-items/$projectId/$workItemId', + params: { projectId, workItemId: item.workItemId }, + }); + } else if (item.type === 'pr' && item.prNumber != null) { + navigate({ + to: '/prs/$projectId/$prNumber', + params: { projectId, prNumber: String(item.prNumber) }, + }); + } }; const handleKeyDown = (e: React.KeyboardEvent) => { - if (canExpand && (e.key === 'Enter' || e.key === ' ')) { + if (canNavigate && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); - onToggle(item.id); + handleClick(); } }; @@ -313,11 +172,11 @@ function WorkItemRow({ item, projectId, isExpanded, onToggle }: WorkItemRowProps className="border-b border-border transition-colors hover:bg-muted/30" onClick={handleClick} onKeyDown={handleKeyDown} - style={canExpand ? { cursor: 'pointer' } : undefined} + style={canNavigate ? { cursor: 'pointer' } : undefined} > - {/* Expand chevron / Type icon */} + {/* Type icon */} - + {/* PR title / number + Associated work item (stacked) */} @@ -328,16 +187,13 @@ function WorkItemRow({ item, projectId, isExpanded, onToggle }: WorkItemRowProps - {/* Run count + Activity link */} + {/* Run count */} -
- - {canExpand ? ( - {item.runCount} - ) : ( - item.runCount - )} -
+ {canNavigate ? ( + {item.runCount} + ) : ( + item.runCount + )} {/* Cost */} @@ -364,26 +220,6 @@ export function ProjectWorkTable({ const currentPage = Math.floor(offset / limit) + 1; const pageItems = items.slice(offset, offset + limit); - const [expandedRows, setExpandedRows] = useState>(new Set()); - - // Reset expanded rows when the page changes - // biome-ignore lint/correctness/useExhaustiveDependencies: offset is a prop used as a page-change trigger - useEffect(() => { - setExpandedRows(new Set()); - }, [offset]); - - const toggleRow = (id: string) => { - setExpandedRows((prev) => { - const next = new Set(prev); - if (next.has(id)) { - next.delete(id); - } else { - next.add(id); - } - return next; - }); - }; - return (
@@ -407,21 +243,7 @@ export function ProjectWorkTable({ )} {pageItems.map((item) => ( - - - {expandedRows.has(item.id) && ( - - )} - + ))} diff --git a/web/src/components/runs/work-item-runs-table.tsx b/web/src/components/runs/work-item-runs-table.tsx index f60888de..8c7cc252 100644 --- a/web/src/components/runs/work-item-runs-table.tsx +++ b/web/src/components/runs/work-item-runs-table.tsx @@ -13,6 +13,8 @@ interface WorkItemRun { durationMs: number | null; costUsd: string | null; llmIterations: number | null; + engine: string; + model: string | null; } interface WorkItemRunsTableProps { @@ -45,6 +47,8 @@ export function WorkItemRunsTable({ runs, isLoading, isError, error }: WorkItemR Agent + Engine + Model Status Started Duration @@ -68,6 +72,8 @@ export function WorkItemRunsTable({ runs, isLoading, isError, error }: WorkItemR {run.agentType} + {run.engine} + {run.model ?? '-'}