fix(rack-planner): compute port data synchronously in PortConfigModal to prevent empty first render

This commit is contained in:
2026-03-22 15:11:00 -05:00
parent 5de001c630
commit b26f88a89e

View File

@@ -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<Port | null>(null);
const { racks, fetchRacks, deleteConnection } = useRackStore();
const [vlans, setVlans] = useState<Vlan[]>([]);
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<VlanMode>('ACCESS');
const [nativeVlanId, setNativeVlanId] = useState<string>('');
const [taggedVlanIds, setTaggedVlanIds] = useState<string[]>([]);
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 (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) break;
}
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) {