From b26f88a89e49ee3b70d480a39ea8bdc594f38728 Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 22 Mar 2026 15:11:00 -0500 Subject: [PATCH] fix(rack-planner): compute port data synchronously in PortConfigModal to prevent empty first render --- .../src/components/modals/PortConfigModal.tsx | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/client/src/components/modals/PortConfigModal.tsx b/client/src/components/modals/PortConfigModal.tsx index f86b730..39731eb 100644 --- a/client/src/components/modals/PortConfigModal.tsx +++ b/client/src/components/modals/PortConfigModal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, type FormEvent } from 'react'; +import { useState, useEffect, useMemo, type FormEvent } from 'react'; import { toast } from 'sonner'; import type { Port, Vlan, VlanMode } from '../../types'; import { Modal } from '../ui/Modal'; @@ -14,42 +14,45 @@ interface PortConfigModalProps { } export function PortConfigModal({ portId, open, onClose }: PortConfigModalProps) { - const { racks, fetchRacks } = useRackStore(); - const [port, setPort] = useState(null); + const { racks, fetchRacks, deleteConnection } = useRackStore(); const [vlans, setVlans] = useState([]); + const [loading, setLoading] = useState(false); + const [fetching, setFetching] = useState(false); + + // Synchronously find the port from the global store + const port = useMemo(() => { + for (const rack of racks) { + for (const mod of rack.modules) { + const found = mod.ports.find((p) => p.id === portId); + if (found) return found; + } + } + return null; + }, [racks, portId]); + + // Form state const [label, setLabel] = useState(''); const [mode, setMode] = useState('ACCESS'); const [nativeVlanId, setNativeVlanId] = useState(''); const [taggedVlanIds, setTaggedVlanIds] = useState([]); const [notes, setNotes] = useState(''); - const [loading, setLoading] = useState(false); - const [fetching, setFetching] = useState(false); + // Quick-create VLAN const [newVlanId, setNewVlanId] = useState(''); const [newVlanName, setNewVlanName] = useState(''); const [newVlanColor, setNewVlanColor] = useState('#3b82f6'); const [creatingVlan, setCreatingVlan] = useState(false); - // Find the port from store + // Reset form state when port is found or changed useEffect(() => { - if (!open) return; - let found: Port | undefined; - for (const rack of racks) { - for (const mod of rack.modules) { - found = mod.ports.find((p) => p.id === portId); - if (found) break; - } - if (found) break; + if (port && open) { + setLabel(port.label ?? ''); + setMode(port.mode); + setNativeVlanId(port.nativeVlan?.toString() ?? ''); + setTaggedVlanIds(port.vlans.filter((v) => v.tagged).map((v) => v.vlanId)); + setNotes(port.notes ?? ''); } - if (found) { - setPort(found); - setLabel(found.label ?? ''); - setMode(found.mode); - setNativeVlanId(found.nativeVlan?.toString() ?? ''); - setTaggedVlanIds(found.vlans.filter((v) => v.tagged).map((v) => v.vlanId)); - setNotes(found.notes ?? ''); - } - }, [open, portId, racks]); + }, [port, open]); // Load VLAN list useEffect(() => { @@ -125,7 +128,6 @@ export function PortConfigModal({ portId, open, onClose }: PortConfigModalProps) if (!port) return null; - const { deleteConnection } = useRackStore(); const connections = [...(port.sourceConnections || []), ...(port.targetConnections || [])]; async function handleDisconnect(connId: string) {