Phase 3: Agent Cycle Visibility (#461) (#462)
All checks were successful
ci/woodpecker/push/api Pipeline was successful
ci/woodpecker/push/web Pipeline was successful

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #462.
This commit is contained in:
2026-02-23 01:07:29 +00:00
committed by jason.woltje
parent 7581d26567
commit 458cac7cdd
9 changed files with 136 additions and 12 deletions

View File

@@ -53,6 +53,28 @@ export default function DashboardPage(): ReactElement {
};
}, [workspaceId]);
useEffect(() => {
if (!workspaceId) return;
let cancelled = false;
const wsId = workspaceId;
const interval = setInterval(() => {
fetchDashboardSummary(wsId)
.then((summary) => {
if (!cancelled) setData(summary);
})
.catch((err: unknown) => {
console.error("[Dashboard] Refresh failed:", err);
});
}, 30_000);
return (): void => {
cancelled = true;
clearInterval(interval);
};
}, [workspaceId]);
if (isLoading) {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>

View File

@@ -27,6 +27,7 @@ interface AgentNode {
name: string;
task: string;
status: DotVariant;
statusLabel: string;
}
interface OrchestratorSession {
@@ -36,6 +37,7 @@ interface OrchestratorSession {
badge: string;
badgeVariant: BadgeVariant;
duration: string;
progress: number;
agents: AgentNode[];
}
@@ -105,6 +107,7 @@ function mapJobToSession(job: ActiveJob): OrchestratorSession {
name: step.name,
task: `Phase: ${step.phase}`,
status: statusToDotVariant(step.status),
statusLabel: step.status.toLowerCase(),
}));
return {
@@ -114,6 +117,7 @@ function mapJobToSession(job: ActiveJob): OrchestratorSession {
badge: job.status,
badgeVariant: statusToBadgeVariant(job.status),
duration: formatDuration(job.createdAt),
progress: job.progressPercent,
agents,
};
}
@@ -192,6 +196,16 @@ function AgentNodeItem({ agent }: AgentNodeItemProps): ReactElement {
</div>
</div>
<Dot variant={agent.status} />
<span
style={{
fontSize: "0.65rem",
fontFamily: "var(--mono)",
color: "var(--muted)",
textTransform: "uppercase",
}}
>
{agent.statusLabel}
</span>
</div>
);
}
@@ -251,6 +265,27 @@ function OrchCard({ session }: OrchCardProps): ReactElement {
{session.duration}
</span>
</div>
{session.progress > 0 && (
<div
style={{
height: 4,
borderRadius: 2,
background: "var(--border)",
marginBottom: 10,
overflow: "hidden",
}}
>
<div
style={{
height: "100%",
width: `${String(session.progress)}%`,
background: "var(--primary)",
borderRadius: 2,
transition: "width 0.3s ease",
}}
/>
</div>
)}
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
{session.agents.map((agent) => (
<AgentNodeItem key={agent.id} agent={agent} />