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 && (
-
-
-
-
- | Agent |
- Status |
- Started |
-
- Duration
- |
- Cost |
- Iters |
- Actions |
-
-
-
- {runsQuery.data.map((run) => (
-
- |
-
- {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 ?? '-'} |
|