feat(FN-2903): merge fusion/fn-2903

This commit is contained in:
gsxdsm
2026-04-29 12:31:09 -07:00
parent 8e3288d94e
commit 40e36f00ec
3 changed files with 63 additions and 5 deletions

View File

@@ -258,6 +258,11 @@
background: var(--card-hover);
}
.agent-tree__node--active,
.agent-tree__node--running {
background: var(--state-active-bg);
}
.agent-tree__indent--0 { padding-left: 0; }
.agent-tree__indent--1 { padding-left: var(--space-xl); }
.agent-tree__indent--2 { padding-left: calc(var(--space-xl) * 2); }
@@ -657,14 +662,20 @@
.agent-board-card--idle { border-top-color: var(--state-idle-border); }
.agent-board-card--active,
.agent-board-card--running { border-top-color: var(--state-active-border); }
.agent-board-card--running {
border-top-color: var(--state-active-border);
background: var(--state-active-bg);
}
.agent-board-card--paused { border-top-color: var(--state-paused-border); }
.agent-board-card--error,
.agent-board-card--terminated { border-top-color: var(--state-error-border); }
.agent-card--idle { border-left-color: var(--state-idle-border); }
.agent-card--active,
.agent-card--running { border-left-color: var(--state-active-border); }
.agent-card--running {
border-left-color: var(--state-active-border);
background: var(--state-active-bg);
}
.agent-card--paused { border-left-color: var(--state-paused-border); }
.agent-card--error,
.agent-card--terminated { border-left-color: var(--state-error-border); }
@@ -722,6 +733,11 @@
outline: none;
}
.org-chart-node-card--active,
.org-chart-node-card--running {
background: var(--state-active-bg);
}
.org-chart-node__header {
display: flex;
align-items: center;

View File

@@ -63,7 +63,10 @@ function getStateBadgeClass(state: AgentState): string {
}
}
function getStateCardClass(prefix: "agent-card" | "agent-board-card", state: AgentState): string {
function getStateCardClass(
prefix: "agent-card" | "agent-board-card" | "agent-tree__node" | "org-chart-node-card",
state: AgentState,
): string {
switch (state) {
case "running":
return `${prefix}--running`;
@@ -106,11 +109,12 @@ function AgentTreeNode({
const expanded = isExpanded(agent.id);
const health = getHealthStatus(agent);
const stateBadgeClass = getStateBadgeClass(agent.state);
const stateNodeClass = getStateCardClass("agent-tree__node", agent.state);
return (
<>
<div
className={`agent-tree__node${agent.reportsTo ? " agent-is-child" : ""} agent-tree__indent--${Math.min(depth, 4)}`}
className={`${stateNodeClass}${agent.reportsTo ? " agent-is-child" : ""} agent-tree__indent--${Math.min(depth, 4)}`}
>
<button
className={`agent-tree__toggle${childCount === 0 ? " agent-tree__toggle--leaf" : ""}`}
@@ -193,11 +197,12 @@ function OrgChartNode({
const { agent, children } = node;
const health = getHealthStatus(agent);
const stateBadgeClass = getStateBadgeClass(agent.state);
const stateNodeClass = getStateCardClass("org-chart-node-card", agent.state);
return (
<div className={`org-chart-node${children.length > 0 ? " org-chart-node--has-children" : ""}`}>
<div
className="org-chart-node-card"
className={stateNodeClass}
onClick={() => onSelect(agent.id)}
role="button"
tabIndex={0}

View File

@@ -342,6 +342,43 @@ describe("AgentsView", () => {
});
});
it.each(["active", "running"] as const)("applies active highlight state classes across views for %s agents", async (state) => {
const highlightAgent: Agent = {
id: `agent-highlight-${state}`,
name: `Highlight ${state}`,
role: "executor",
state,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
metadata: {},
};
mockFetchAgents.mockResolvedValue([highlightAgent]);
mockFetchAgentStats.mockResolvedValue({ total: 1, byState: { [state]: 1 }, byRole: { executor: 1 } });
mockFetchOrgTree.mockResolvedValue([{ agent: highlightAgent, children: [] }]);
render(<AgentsView addToast={mockAddToast} />);
await waitFor(() => {
expect(document.querySelector(`.agent-card--${state}`)).toBeTruthy();
});
fireEvent.click(screen.getByRole("button", { name: "Board view" }));
await waitFor(() => {
expect(document.querySelector(`.agent-board-card--${state}`)).toBeTruthy();
});
fireEvent.click(screen.getByRole("button", { name: "Tree view" }));
await waitFor(() => {
expect(document.querySelector(`.agent-tree__node--${state}`)).toBeTruthy();
});
fireEvent.click(screen.getByRole("button", { name: "Org Chart view" }));
await waitFor(() => {
expect(document.querySelector(`.org-chart-node-card--${state}`)).toBeTruthy();
});
});
it("shows terminated agents when explicitly filtered", async () => {
render(<AgentsView addToast={mockAddToast} />);
await openControlsPanel();