Files
fusion/packages/dashboard/app/components/ProjectNodeSelector.tsx
Fusion 701feb8ea5 feat(FN-2845): streamline node routing configuration in dashboard
- Add a reusable ProjectNodeSelector component and hook RoutingTab to it for per-task node overrides
- Move default node and unavailable-node policy controls into the dedicated Node Routing settings section
- Add descriptive routing guidance text and project-level note in SettingsModal
- Expand dashboard tests for routing tab behavior and node routing settings UX
2026-04-28 14:25:56 -07:00

60 lines
1.6 KiB
TypeScript

import { useMemo } from "react";
import type { NodeInfo } from "../api";
import "./ProjectNodeSelector.css";
interface ProjectNodeSelectorProps {
projectId: string;
currentNodeId?: string;
onSelect: (nodeId: string | null) => void;
nodes: NodeInfo[];
disabled?: boolean;
}
const STATUS_DOT: Record<NodeInfo["status"], string> = {
online: "🟢",
offline: "🔴",
connecting: "🟡",
error: "🔴",
};
export function ProjectNodeSelector({
projectId,
currentNodeId,
onSelect,
nodes,
disabled = false,
}: ProjectNodeSelectorProps) {
const sortedNodes = useMemo(() => {
return [...nodes].sort((a, b) => a.name.localeCompare(b.name));
}, [nodes]);
const selectedValue = currentNodeId ?? "";
return (
<label className="project-node-selector" htmlFor={`project-node-selector-${projectId}`}>
<span className="project-node-selector__label">Runtime Node</span>
<select
className="select"
id={`project-node-selector-${projectId}`}
value={selectedValue}
onChange={(event) => {
const value = event.target.value;
onSelect(value ? value : null);
}}
disabled={disabled}
>
<option value="">Auto (no assignment)</option>
{sortedNodes.map((node) => (
<option
key={node.id}
value={node.id}
className={node.status === "offline" || node.status === "error" ? "project-node-selector__option--dim" : ""}
>
{STATUS_DOT[node.status]} {node.name} ({node.type})
</option>
))}
</select>
</label>
);
}