- fix(FN-2729): silence ansi strip lint warning - test(FN-2729): complete Step 10 — cover node health indicators - feat(FN-2729): complete Step 9 — colorize CLI node status output - feat(FN-2729): complete Step 8 — use NodeHealthDot in settings routing status - test(FN-2729): align ListView node override option expectations - feat(FN-2729): complete Step 7 — add bulk node status indicators - fix(engine): prevent auto-merge cooldown loop on unresolvable conflicts - feat(FN-2911): improve chat attachment compose and preview UX - feat(FN-2921): merge fusion/fn-2921 - feat(FN-2909): merge fusion/fn-2909 - feat(FN-2914): merge fusion/fn-2914 - feat(FN-2902): merge fusion/fn-2902 - chore(release): v0.8.3 - Update changeset - fix(tui): swap 3/4 panel hotkeys so they match the help text - fix(tui): always show auth token and report accurate macOS memory usage Fusion-Task-Id: FN-2729
54 lines
1.5 KiB
TypeScript
54 lines
1.5 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;
|
|
}
|
|
|
|
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}
|
|
title={`Status: ${node.status}`}
|
|
className={node.status === "offline" || node.status === "error" ? "project-node-selector__option--dim" : ""}
|
|
>
|
|
{node.name} ({node.type}) — {node.status}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
);
|
|
}
|