diff --git a/client/src/components/rack/ModuleBlock.tsx b/client/src/components/rack/ModuleBlock.tsx index 30473a8..702c060 100644 --- a/client/src/components/rack/ModuleBlock.tsx +++ b/client/src/components/rack/ModuleBlock.tsx @@ -54,11 +54,9 @@ export function ModuleBlock({ module }: ModuleBlockProps) { portRows.push(mainPorts.slice(i, i + PORTS_PER_ROW)); } } else if (sidePorts.length > 0) { - // If only special ports exist, create an empty first row to hold them portRows.push([]); } - // Calculate vertical space const availableForPorts = height - 16; const maxRows = Math.max(1, Math.floor(availableForPorts / 14)); const visibleRows = portRows.length > 0 ? portRows.slice(0, maxRows) : []; @@ -174,17 +172,17 @@ export function ModuleBlock({ module }: ModuleBlockProps) { > {/* Port grid — primary face content */} {hasPorts && previewUSize === null ? ( -
+
{visibleRows.map((row, rowIdx) => ( -
- {/* Standard ports */} -
+
+ {/* Standard ports Group */} +
{row.map((port) => { const hasVlan = port.vlans.length > 0; const vlanColor = hasVlan ? port.mode === 'ACCESS' ? port.vlans[0]?.vlan?.color || '#10b981' - : '#8b5cf6' + : '#a78bfa' : '#475569'; const isCablingSource = cablingFromPortId === port.id; @@ -195,32 +193,28 @@ export function ModuleBlock({ module }: ModuleBlockProps) { onPointerDown={(e) => e.stopPropagation()} onClick={(e) => handlePortClick(e, port.id)} aria-label={`Port ${port.portNumber}`} - title={`Port ${port.portNumber}${port.label ? ` · ${port.label}` : ''}${ - hasVlan ? ` (VLAN ${port.vlans.map((v) => v.vlan?.vlanId).filter(Boolean).join(',')})` : '' - }\nShift+Click for settings`} - style={{ backgroundColor: vlanColor, borderColor: 'rgba(0,0,0,0.2)' }} + title={`Port ${port.portNumber}\n${port.portType}${port.label ? ` · ${port.label}` : ''}`} + style={{ backgroundColor: vlanColor }} className={cn( - 'w-2.5 h-2.5 rounded-sm border transition-all shrink-0 hover:scale-110 hover:brightness-125', - isCablingSource && 'ring-2 ring-blue-400 ring-offset-1 ring-offset-slate-900 animate-pulse' + 'w-2 h-2 rounded-full border border-black/20 hover:scale-125 transition-all outline-none', + isCablingSource && 'ring-2 ring-white ring-offset-1 ring-offset-slate-900 animate-pulse' )} /> ); })}
- {/* SFP/WAN group (on first row) */} + {/* SFP/WAN Group (push to right) */} {rowIdx === 0 && sidePorts.length > 0 && ( -
+
{sidePorts.map((port) => { const hasVlan = port.vlans.length > 0; - const isSfp = ['SFP', 'SFP_PLUS', 'QSFP'].includes(port.portType); + const isSfp = port.portType?.includes('SFP'); const isWan = port.portType === 'WAN'; const vlanColor = hasVlan - ? port.mode === 'ACCESS' - ? port.vlans[0]?.vlan?.color || '#10b981' - : '#8b5cf6' - : isWan ? '#3b82f6' : '#64748b'; + ? port.vlans[0]?.vlan?.color || '#3b82f6' + : isWan ? '#2563eb' : '#94a3b8'; const isCablingSource = cablingFromPortId === port.id; @@ -230,16 +224,12 @@ export function ModuleBlock({ module }: ModuleBlockProps) { data-port-id={port.id} onPointerDown={(e) => e.stopPropagation()} onClick={(e) => handlePortClick(e, port.id)} - aria-label={`${port.portType} Port ${port.portNumber}`} - title={`${port.portType} Port ${port.portNumber}${port.label ? ` · ${port.label}` : ''}${ - hasVlan ? ` (VLAN ${port.vlans.map((v) => v.vlan?.vlanId).filter(Boolean).join(',')})` : '' - }\nShift+Click for settings`} - style={{ backgroundColor: vlanColor, borderColor: isSfp ? 'rgba(255,255,255,0.3)' : 'rgba(0,0,0,0.2)' }} + title={`${port.portType} ${port.portNumber}`} + style={{ backgroundColor: vlanColor }} className={cn( - 'w-2.5 h-2.5 border transition-all shrink-0 hover:scale-110 hover:brightness-125', - isSfp ? 'rounded-none rotate-45 scale-[0.85]' : 'rounded-full', - isCablingSource && 'ring-2 ring-blue-400 ring-offset-1 ring-offset-slate-900 animate-pulse', - isWan && 'ring-1 ring-blue-400/50' + 'w-2.5 h-2.5 transition-transform hover:scale-125 border border-black/40', + isSfp ? 'rounded-none rotate-45 scale-75' : 'rounded-full ring-1 ring-white/10', + isCablingSource && 'ring-2 ring-white ring-offset-1 ring-offset-slate-900 animate-pulse' )} /> ); @@ -249,7 +239,7 @@ export function ModuleBlock({ module }: ModuleBlockProps) {
))} {hiddenPortCount > 0 && ( - +{hiddenPortCount} more + +{hiddenPortCount} ports )}
) : ( diff --git a/scripts/check_ports.ts b/scripts/check_ports.ts new file mode 100644 index 0000000..7f33283 --- /dev/null +++ b/scripts/check_ports.ts @@ -0,0 +1,12 @@ + +import { PrismaClient } from '@prisma/client'; +const prisma = new PrismaClient(); + +async function check() { + const modules = await prisma.module.findMany({ + include: { ports: true } + }); + console.log(JSON.stringify(modules, null, 2)); +} + +check().catch(console.error);