Make the Files tree show real file counts, not agent counts
The "Spaces" section badged each department with its agent count, so an org with one CEO and zero files showed "Executive 1". Clicking it filtered to that department's files and revealed nothing — the tree advertised a folder that opened empty. collectDepartments now counts files per department and omits departments with none, so every badge equals what the section contains. Unit-tested for file-based counting and empty omission. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
edb6dded74
commit
08f39b4f3a
4 files changed
+55
-8
No files matched your search
@@ -32,7 +32,7 @@ export function FilesPage({ snapshot, actions }: PageProps) {
|
||||
const [view, setView] = useState<ViewMode>("list");
|
||||
const [scanFilter, setScanFilter] = useState<FileScanState | "all">("all");
|
||||
|
||||
const departments = collectDepartments(agents);
|
||||
const departments = collectDepartments(files, agents);
|
||||
const filesByAgent = collectAgentFiles(files, agents);
|
||||
const sectionFiles = filterFiles(files, section, agents);
|
||||
const visibleFiles = scanFilter === "all" ? sectionFiles : sectionFiles.filter((file) => file.scanState === scanFilter);
|
||||
|
||||
@@ -91,7 +91,7 @@ function TreeItem({
|
||||
}) {
|
||||
return (
|
||||
<button type="button" className={`nav-item ${active ? "active" : ""}`.trim()} onClick={onClick}>
|
||||
<Icon name={icon} size={14} />
|
||||
<span className="icon-box"><Icon name={icon} size={18} /></span>
|
||||
<span>{label}</span>
|
||||
<span className="nav-badge num">{count}</span>
|
||||
</button>
|
||||
|
||||
@@ -23,13 +23,19 @@ export const FILE_COLOR_MAP: Record<string, { color: string; label: string }> =
|
||||
folder: { color: "#a1a1aa", label: "" },
|
||||
};
|
||||
|
||||
export function collectDepartments(agents: AgentRecord[]): Array<{ name: string; count: number }> {
|
||||
const seen = new Map<string, number>();
|
||||
for (const agent of agents) {
|
||||
if (!agent.department) continue;
|
||||
seen.set(agent.department, (seen.get(agent.department) ?? 0) + 1);
|
||||
/** Departments shown in the file tree carry a FILE count, not an agent count:
|
||||
* the badge must equal what opening the section reveals. Departments with no
|
||||
* files are omitted so the tree never advertises a folder that opens empty. */
|
||||
export function collectDepartments(files: FileObjectRecord[], agents: AgentRecord[]): Array<{ name: string; count: number }> {
|
||||
const departmentByAgent = new Map(agents.map((agent) => [agent.id, agent.department]));
|
||||
const counts = new Map<string, number>();
|
||||
for (const file of files) {
|
||||
if (file.ownerType !== "agent" || !file.ownerId) continue;
|
||||
const department = departmentByAgent.get(file.ownerId);
|
||||
if (!department) continue;
|
||||
counts.set(department, (counts.get(department) ?? 0) + 1);
|
||||
}
|
||||
return [...seen.entries()].map(([name, count]) => ({ name, count })).sort((a, b) => a.name.localeCompare(b.name));
|
||||
return [...counts.entries()].map(([name, count]) => ({ name, count })).sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
export function collectAgentFiles(files: FileObjectRecord[], agents: AgentRecord[]): Array<{ agent: AgentRecord; count: number }> {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { collectDepartments } from "../../src/renderer/src/pages/files/file-helpers";
|
||||
import type { AgentRecord, FileObjectRecord } from "../../src/shared/domain";
|
||||
|
||||
function agent(id: string, department: string): AgentRecord {
|
||||
return {
|
||||
id, orgId: "org", name: id, role: "engineer", department,
|
||||
modelProviderId: null, modelId: null, budgetCents: 0, managerAgentId: null,
|
||||
title: id, roleLevel: "ic", modelStrategy: "single", workspaceId: null,
|
||||
heartbeatEnabled: false, heartbeatIntervalSec: 0, lifecycle: "active", createdAt: "2026-01-01",
|
||||
};
|
||||
}
|
||||
|
||||
function file(id: string, ownerType: FileObjectRecord["ownerType"], ownerId: string | null): FileObjectRecord {
|
||||
return {
|
||||
id, orgId: "org", name: id, kind: "doc", sizeBytes: 1, sha256: "x", storagePath: `/tmp/${id}`,
|
||||
ownerType, ownerId, spaceId: null, pinned: false, scanState: "approved", scanLabelsJson: "[]",
|
||||
scannedAt: null, createdAt: "2026-01-01", updatedAt: null,
|
||||
};
|
||||
}
|
||||
|
||||
describe("collectDepartments", () => {
|
||||
test("counts files per department, not agents, and omits empty departments", () => {
|
||||
const agents = [agent("a1", "Engineering"), agent("a2", "Engineering"), agent("a3", "Design"), agent("a4", "Sales")];
|
||||
const files = [
|
||||
file("f1", "agent", "a1"),
|
||||
file("f2", "agent", "a1"),
|
||||
file("f3", "agent", "a3"),
|
||||
file("f4", "employee", "emp1"), // not owned by an agent → ignored
|
||||
];
|
||||
// Engineering has two agents but only a1 holds files (2); Design has 1; Sales has agents but no files.
|
||||
expect(collectDepartments(files, agents)).toEqual([
|
||||
{ name: "Design", count: 1 },
|
||||
{ name: "Engineering", count: 2 },
|
||||
]);
|
||||
});
|
||||
|
||||
test("returns nothing when no agent owns a file", () => {
|
||||
expect(collectDepartments([], [agent("a1", "Engineering")])).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user