Q162: the workload chart counts every agent and groups the rest into one bar

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-09 03:05:29 -04:00
1 parent 58871fbaed
commit c78045d523
4 files changed
+51 -9

No files matched your search

+1
View File
@@ -54,6 +54,7 @@ export const zh: Record<string, string> = {
"Cancel run": "取消运行",
"Show older runs": "显示更早的运行",
"Target date": "目标日期",
"{n} others": "其余 {n} 人",
"Work accepted (7 days)": "工作验收(近 7 天)",
"{accepted} done · {returned} sent back · sessions: {ok} ended, {failed} failed": "{accepted} 项完成 · {returned} 项退回 · 会话:{ok} 次结束,{failed} 次失败",
"no target date": "未设目标日期",
@@ -1,7 +1,7 @@
import { workloadBars } from "./workload-bars";
import type { UsageStatsRecord } from "../../../../shared/usage-stats";
import { useState, type ReactNode } from "react";
import type { AgentRecord, TaskRecord } from "../../../../shared/domain";
import { TASK_STATUS } from "../../../../shared/domain";
import { t } from "../../i18n/i18n";
const CHART_H = 116;
@@ -22,16 +22,15 @@ function ChartCard({ title, readout, children }: { title: string; readout: strin
);
}
/** Agents beyond this share one bar, so a large team still fits and is still counted. */
const MAX_WORKLOAD_BARS = 8;
export function AgentWorkloadChart({ agents, tasks }: { agents: AgentRecord[]; tasks: TaskRecord[] }) {
const data = agents.slice(0, 8).map((a) => ({
name: a.name.split(/\s+/)[0] ?? a.name,
count: tasks.filter((t) => t.assigneeAgentId === a.id && t.status !== TASK_STATUS.DONE).length,
}));
const total = data.reduce((s, d) => s + d.count, 0);
const { bars: data, total } = workloadBars(agents, tasks, MAX_WORKLOAD_BARS);
const max = Math.max(1, ...data.map((d) => d.count));
const barW = data.length > 0 ? (CHART_W - (data.length - 1) * BAR_GAP) / data.length : 0;
const [hover, setHover] = useState<number | null>(null);
const readout = hover !== null && data[hover] ? `${data[hover]!.name} · ${data[hover]!.count}` : t("{n} open", { n: total });
const readout = hover !== null && data[hover] ? `${data[hover]!.label} · ${data[hover]!.count}` : t("{n} open", { n: total });
return (
<ChartCard title={t("Agent workload")} readout={readout}>
@@ -40,7 +39,7 @@ export function AgentWorkloadChart({ agents, tasks }: { agents: AgentRecord[]; t
const h = (d.count / max) * (CHART_H - 22);
const x = i * (barW + BAR_GAP);
return (
<g key={d.name} onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(null)}>
<g key={d.key} onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(null)}>
<rect x={x} y={0} width={barW} height={CHART_H - 16} fill="transparent" />
<rect
x={x}
@@ -52,7 +51,7 @@ export function AgentWorkloadChart({ agents, tasks }: { agents: AgentRecord[]; t
opacity={hover === null || hover === i ? 0.85 : 0.32}
style={{ transition: "opacity var(--motion-fast) var(--ease-out)" }}
/>
<text x={x + barW / 2} y={CHART_H - 2} textAnchor="middle" fontSize="9" fill="var(--text-3)">{d.name}</text>
<text x={x + barW / 2} y={CHART_H - 2} textAnchor="middle" fontSize="9" fill="var(--text-3)">{d.label}</text>
</g>
);
})}
@@ -0,0 +1,18 @@
import { TASK_STATUS, type AgentRecord, type TaskRecord } from "../../../../shared/domain";
import { t } from "../../i18n/i18n";
/** Open work per agent for the chart: the busiest get their own bar, the rest share one labelled bar, and the total counts everyone. */
export function workloadBars(agents: AgentRecord[], tasks: TaskRecord[], maxBars: number): { bars: Array<{ key: string; label: string; count: number }>; total: number } {
const counts = agents.map((agent) => ({
key: agent.id,
label: agent.name.split(/\s+/)[0] ?? agent.name,
count: tasks.filter((task) => task.assigneeAgentId === agent.id && task.status !== TASK_STATUS.DONE).length,
}));
const total = counts.reduce((sum, item) => sum + item.count, 0);
if (counts.length <= maxBars) return { bars: counts, total };
const ranked = [...counts].sort((a, b) => b.count - a.count);
const shown = ranked.slice(0, maxBars - 1);
const rest = ranked.slice(maxBars - 1);
const others = { key: "others", label: t("{n} others", { n: rest.length }), count: rest.reduce((sum, item) => sum + item.count, 0) };
return { bars: [...shown, others], total };
}
+24
View File
@@ -0,0 +1,24 @@
import { describe, expect, test } from "vitest";
import { workloadBars } from "../../src/renderer/src/pages/home/workload-bars";
import type { AgentRecord, TaskRecord } from "../../src/shared/domain";
const agent = (id: string, name: string) => ({ id, name }) as AgentRecord;
const open = (assignee: string, count: number) => Array.from({ length: count }, () => ({ assigneeAgentId: assignee, status: "in_progress" }) as TaskRecord);
describe("team workload", () => {
test("counts every agent: the busiest show, the rest are one labelled bar, and the total is everyone's", () => {
const agents = Array.from({ length: 10 }, (_, index) => agent(`agt_${index}`, `Agent ${index}`));
const tasks = agents.flatMap((item, index) => open(item.id, index));
const { bars, total } = workloadBars(agents, tasks, 8);
expect(total).toBe(45);
expect(bars.map((bar) => [bar.label, bar.count])).toEqual([
["Agent", 9], ["Agent", 8], ["Agent", 7], ["Agent", 6], ["Agent", 5], ["Agent", 4], ["Agent", 3], ["3 others", 3],
]);
expect(new Set(bars.map((bar) => bar.key)).size).toBe(8);
});
test("a small team shows everyone", () => {
const { bars, total } = workloadBars([agent("a", "Ada Lovelace"), agent("b", "Ben")], [...open("a", 2)], 8);
expect([bars.map((bar) => bar.label), total]).toEqual([["Ada", "Ben"], 2]);
});
});