import type { NodeInfo } from "../../../api"; import { NodeHealthDot } from "../../NodeHealthDot"; import { SettingsSelectRow } from "../SettingsSelectRow"; import { SettingsHelpTip } from "../SettingsHelpTip"; import type { SettingsFormState, SetSettingsForm } from "./context"; import { useTranslation } from "react-i18next"; function getNodeStatusLabel(status: "online" | "offline" | "connecting" | "error", t: ReturnType>["t"]): string { if (status === "online") return t("settings.nodeRouting.statusOnline", "Online"); if (status === "connecting") return t("settings.nodeRouting.statusConnecting", "Connecting"); if (status === "error") return t("settings.nodeRouting.statusError", "Error"); return t("settings.nodeRouting.statusOffline", "Offline"); } export interface NodeRoutingSectionProps { form: SettingsFormState; setForm: SetSettingsForm; nodes: NodeInfo[]; } /* FNXC:SettingsScope 2026-07-15-17:35: Both routing settings are project-scoped (DEFAULT_PROJECT_SETTINGS), which is what the note below already tells the operator in prose; the per-row scope badge is the machine-readable form of that same claim. */ export function NodeRoutingSection({ form, setForm, nodes }: NodeRoutingSectionProps) { const { t } = useTranslation("app"); return (<>

{t("settings.nodeRouting.nodeRouting", "Node Routing")}

{t("settings.nodeRouting.configureHowTasksAreRoutedToExecutionNodes", "Configure how tasks are routed to execution nodes.")}

{t("settings.nodeRouting.theseSettingsApplyAtTheProjectLevel", "These settings apply at the project level.")}

{/* FNXC:SettingsStyling 2026-07-15-17:35: This row stays hand-rolled: routing safety requires the live NodeHealthDot for the selected node to render right under the control, and the shared select row renders only label/control/help with no slot for an adjacent status widget. Forcing it onto the primitive would move or drop the health readout, so the dot wins over row uniformity here. FNXC:SettingsHelp 2026-07-16-12:45: Inline help moved behind the shared "?" affordance — operator requirement: no inline description paragraphs in Settings. The tip is a SIBLING of the label (a button inside a label is invalid), so both sit in a `.settings-field-label-row`. The live NodeHealthDot status stays inline: it is state feedback, not help copy. */}
{t("settings.nodeRouting.usedWhenATaskHasNoNodeOverride", "Used when a task has no node override. Node status is shown for safer routing selection. No default — unset (local execution).")}
{(() => { const selectedNode = nodes.find((node) => node.id === form.defaultNodeId); if (!selectedNode) return null; return (
{t("settings.nodeRouting.selectedNode", "Selected node:")}
); })()}
setForm((f) => ({ ...f, unavailableNodePolicy: v as "block" | "fallback-local", } as SettingsFormState))} /> ); } export default NodeRoutingSection;