feat(FN-1081): add dashboard nodes management workflow

- Add node API typings and client methods plus a polling useNodes hook with visibility-triggered refresh
- Introduce NodesView with NodeCard, AddNodeModal, and NodeDetailModal to register, inspect, update, health-check, and remove nodes
- Wire the Nodes surface into the app shell/header and add project node assignment UI with project card node badges
- Add unit coverage for useNodes, NodesView, NodeCard, Header, and ProjectCard behavior, plus node-specific dashboard styling
This commit is contained in:
gsxdsm
2026-04-07 23:30:32 -07:00
parent 74cae4f870
commit 075402c11d
17 changed files with 2218 additions and 32 deletions

View File

@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { ProjectCard } from "../ProjectCard";
import type { RegisteredProject, ProjectHealth } from "@fusion/core";
import type { NodeInfo } from "../../api";
// Mock lucide-react to avoid SVG rendering issues in test env
vi.mock("lucide-react", () => ({
@@ -41,6 +42,19 @@ function makeHealth(overrides: Partial<ProjectHealth> = {}): ProjectHealth {
};
}
function makeNode(overrides: Partial<NodeInfo> = {}): NodeInfo {
return {
id: "node_001",
name: "Build Node",
type: "local",
status: "online",
maxConcurrent: 2,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
...overrides,
};
}
const noop = () => {};
describe("ProjectCard", () => {
@@ -60,6 +74,22 @@ describe("ProjectCard", () => {
expect(screen.getByText("/path/to/project")).toBeDefined();
});
it("renders assigned node badge when node is provided", () => {
render(
<ProjectCard
project={makeProject()}
health={makeHealth()}
node={makeNode({ name: "Remote Worker" })}
onSelect={noop}
onPause={noop}
onResume={noop}
onRemove={noop}
/>
);
expect(screen.getByText("on: Remote Worker")).toBeDefined();
});
it("truncates long paths", () => {
const longPath = "/very/long/path/to/the/project/directory/that/needs/truncation";
render(