diff --git a/.claude/settings.local.json b/.claude/settings.local.json index b684854..5ab0ae4 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -8,7 +8,8 @@ "Bash(npx tsc:*)", "Bash(git commit:*)", "Bash(npm uninstall:*)", - "Bash(git add:*)" + "Bash(git add:*)", + "Bash(engine response\" error at migrate/startup time:*)" ] } } diff --git a/client/src/components/mapper/NodeEditModal.tsx b/client/src/components/mapper/NodeEditModal.tsx index 3a8797c..b24cd3c 100644 --- a/client/src/components/mapper/NodeEditModal.tsx +++ b/client/src/components/mapper/NodeEditModal.tsx @@ -25,7 +25,8 @@ export interface NodeEditModalProps { initialLabel: string; initialColor?: string; initialModuleId?: string | null; - onSaved: (updated: { label: string; color: string; moduleId: string | null }) => void; + initialMetadata?: string | null; + onSaved: (updated: { label: string; color: string; moduleId: string | null; metadata?: string }) => void; } export function NodeEditModal({ @@ -35,12 +36,15 @@ export function NodeEditModal({ initialLabel, initialColor, initialModuleId, + initialMetadata, onSaved, }: NodeEditModalProps) { const { racks } = useRackStore(); const [label, setLabel] = useState(initialLabel); const [color, setColor] = useState(initialColor ?? '#3b82f6'); const [moduleId, setModuleId] = useState(initialModuleId ?? ''); + const [ipAddress, setIpAddress] = useState(''); + const [port, setPort] = useState(''); const [loading, setLoading] = useState(false); useEffect(() => { @@ -48,20 +52,50 @@ export function NodeEditModal({ setLabel(initialLabel); setColor(initialColor ?? '#3b82f6'); setModuleId(initialModuleId ?? ''); + + if (initialMetadata) { + try { + const parsed = JSON.parse(initialMetadata); + setIpAddress(parsed.ipAddress || ''); + setPort(parsed.port || ''); + } catch { + setIpAddress(''); + setPort(''); + } + } else { + setIpAddress(''); + setPort(''); + } } - }, [open, initialLabel, initialColor, initialModuleId]); + }, [open, initialLabel, initialColor, initialModuleId, initialMetadata]); async function handleSubmit(e: FormEvent) { e.preventDefault(); if (!label.trim()) return; setLoading(true); try { + let metaObj: Record = {}; + if (initialMetadata) { + try { + metaObj = JSON.parse(initialMetadata); + } catch { /* ignore */ } + } + + if (ipAddress.trim()) metaObj.ipAddress = ipAddress.trim(); + else delete metaObj.ipAddress; + + if (port.trim()) metaObj.port = port.trim(); + else delete metaObj.port; + + const metadataString = Object.keys(metaObj).length > 0 ? JSON.stringify(metaObj) : ''; + await apiClient.nodes.update(nodeId, { label: label.trim(), color, moduleId: moduleId || null, + metadata: metadataString, }); - onSaved({ label: label.trim(), color, moduleId: moduleId || null }); + onSaved({ label: label.trim(), color, moduleId: moduleId || null, metadata: metadataString }); toast.success('Node updated'); onClose(); } catch (err) { @@ -139,6 +173,30 @@ export function NodeEditModal({ + {/* Logical Address */} +
+
+ + setIpAddress(e.target.value)} + disabled={loading} + placeholder="e.g. 10.0.0.5" + className="w-full bg-slate-900 border border-slate-600 rounded-lg px-3 py-2 text-sm text-slate-100 placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 font-mono" + /> +
+
+ + setPort(e.target.value)} + disabled={loading} + placeholder="e.g. 443" + className="w-full bg-slate-900 border border-slate-600 rounded-lg px-3 py-2 text-sm text-slate-100 placeholder:text-slate-600 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 font-mono" + /> +
+
+