import { useState, useEffect, type FormEvent } from 'react'; import { toast } from 'sonner'; import type { ModuleType } from '../../types'; import { Modal } from '../ui/Modal'; import { Button } from '../ui/Button'; import { useRackStore } from '../../store/useRackStore'; import { MODULE_TYPE_LABELS, MODULE_TYPE_COLORS, MODULE_U_DEFAULTS, MODULE_PORT_DEFAULTS, } from '../../lib/constants'; import { cn } from '../../lib/utils'; interface AddModuleModalProps { open: boolean; onClose: () => void; rackId: string; uPosition: number; /** Pre-select a type (e.g. from a palette drag) — skips the type picker step. */ initialType?: ModuleType; } const ALL_TYPES: ModuleType[] = [ 'SWITCH', 'AGGREGATE_SWITCH', 'ROUTER', 'FIREWALL', 'PATCH_PANEL', 'MODEM', 'SERVER', 'NAS', 'PDU', 'AP', 'BLANK', 'OTHER', ]; export function AddModuleModal({ open, onClose, rackId, uPosition, initialType }: AddModuleModalProps) { const { addModule } = useRackStore(); const [selectedType, setSelectedType] = useState(initialType ?? null); const [name, setName] = useState(initialType ? MODULE_TYPE_LABELS[initialType] : ''); const [uSize, setUSize] = useState(initialType ? MODULE_U_DEFAULTS[initialType] : 1); const [portCount, setPortCount] = useState(initialType ? MODULE_PORT_DEFAULTS[initialType] : 0); const [ipAddress, setIpAddress] = useState(''); const [manufacturer, setManufacturer] = useState(''); const [model, setModel] = useState(''); const [sfpCount, setSfpCount] = useState(0); const [wanCount, setWanCount] = useState(0); const [loading, setLoading] = useState(false); // Sync state when modal opens with a new initialType (e.g. drag-drop reuse) useEffect(() => { if (open && initialType) { setSelectedType(initialType); setName(MODULE_TYPE_LABELS[initialType]); setUSize(MODULE_U_DEFAULTS[initialType]); setPortCount(MODULE_PORT_DEFAULTS[initialType]); } else if (!open) { reset(); } }, [open, initialType]); // eslint-disable-line react-hooks/exhaustive-deps function handleTypeSelect(type: ModuleType) { setSelectedType(type); setName(MODULE_TYPE_LABELS[type]); setUSize(MODULE_U_DEFAULTS[type]); setPortCount(MODULE_PORT_DEFAULTS[type]); setSfpCount(0); setWanCount(0); } function reset() { setSelectedType(null); setName(''); setUSize(1); setPortCount(0); setIpAddress(''); setManufacturer(''); setModel(''); setSfpCount(0); setWanCount(0); } async function handleSubmit(e: FormEvent) { e.preventDefault(); if (!selectedType || !name.trim()) return; setLoading(true); try { await addModule(rackId, { name: name.trim(), type: selectedType, uPosition, uSize, portCount, sfpCount, wanCount, ipAddress: ipAddress.trim() || undefined, manufacturer: manufacturer.trim() || undefined, model: model.trim() || undefined, }); toast.success(`${name.trim()} added at U${uPosition}`); reset(); onClose(); } catch (err) { toast.error(err instanceof Error ? err.message : 'Failed to add module'); } finally { setLoading(false); } } function handleClose() { if (!loading) { reset(); onClose(); } } return (
{/* Type selector */} {!selectedType ? (

Select device type

{ALL_TYPES.map((type) => { const colors = MODULE_TYPE_COLORS[type]; return ( ); })}
) : ( <>
{MODULE_TYPE_LABELS[selectedType]}
setName(e.target.value)} disabled={loading} 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" />
setUSize(Number(e.target.value))} disabled={loading} className="w-full bg-slate-900 border border-slate-600 rounded-lg px-3 py-2 text-sm text-slate-100 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50" />
setIpAddress(e.target.value)} disabled={loading} placeholder="192.168.1.1" 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" />
setPortCount(Number(e.target.value))} disabled={loading} className="w-full bg-slate-900 border border-slate-600 rounded-lg px-3 py-2 text-sm text-slate-100 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50" />
setSfpCount(Number(e.target.value))} disabled={loading} className="w-full bg-slate-900 border border-slate-600 rounded-lg px-3 py-2 text-sm text-slate-100 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50" />
setWanCount(Number(e.target.value))} disabled={loading} className="w-full bg-slate-900 border border-slate-600 rounded-lg px-3 py-2 text-sm text-slate-100 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50" />
setManufacturer(e.target.value)} disabled={loading} placeholder="Cisco, Ubiquiti…" 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" />
setModel(e.target.value)} disabled={loading} placeholder="SG300-28…" 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" />
)}
); }