diff --git a/client/src/components/rack/ModuleBlock.tsx b/client/src/components/rack/ModuleBlock.tsx index 9d3be26..ca5cc66 100644 --- a/client/src/components/rack/ModuleBlock.tsx +++ b/client/src/components/rack/ModuleBlock.tsx @@ -4,8 +4,7 @@ import { Trash2, GripVertical, GripHorizontal } from 'lucide-react'; import { toast } from 'sonner'; import type { Module } from '../../types'; import { cn } from '../../lib/utils'; -import { MODULE_TYPE_COLORS, MODULE_TYPE_LABELS, U_HEIGHT_PX } from '../../lib/constants'; -import { Badge } from '../ui/Badge'; +import { MODULE_TYPE_COLORS, U_HEIGHT_PX, PORTS_PER_ROW } from '../../lib/constants'; import { ConfirmDialog } from '../ui/ConfirmDialog'; import { ModuleEditPanel } from '../modals/ModuleEditPanel'; import { PortConfigModal } from '../modals/PortConfigModal'; @@ -49,6 +48,18 @@ export function ModuleBlock({ module }: ModuleBlockProps) { const height = displayUSize * U_HEIGHT_PX; const hasPorts = module.ports.length > 0; + // Split ports into rows of PORTS_PER_ROW + const portRows: (typeof module.ports)[] = []; + for (let i = 0; i < module.ports.length; i += PORTS_PER_ROW) { + portRows.push(module.ports.slice(i, i + PORTS_PER_ROW)); + } + // Only show as many rows as fit within the current height + // Each row needs ~14px (10px dot + 4px gap/padding) + const availableForPorts = height - 16; // subtract top padding + resize handle + const maxRows = Math.max(1, Math.floor(availableForPorts / 14)); + const visibleRows = portRows.slice(0, maxRows); + const hiddenPortCount = module.ports.length - visibleRows.flat().length; + // Compute the maximum allowed uSize for this module (rack bounds + collision) const maxResizeU = useCallback((): number => { const rack = racks.find((r) => r.id === module.rackId); @@ -120,7 +131,7 @@ export function ModuleBlock({ module }: ModuleBlockProps) {
setHovered(true)} onMouseLeave={() => setHovered(false)} onClick={() => !isDragging && !isResizing.current && setEditOpen(true)} @@ -136,77 +148,75 @@ export function ModuleBlock({ module }: ModuleBlockProps) { aria-label={`Edit ${module.name}`} onKeyDown={(e) => e.key === 'Enter' && setEditOpen(true)} > - {/* Drag handle */} + {/* Drag handle — slim left strip */}
e.stopPropagation()} aria-label={`Drag ${module.name}`} > - +
- {/* Main content */} -
- {module.name} - - {MODULE_TYPE_LABELS[module.type]} - -
- - {module.ipAddress && ( -
{module.ipAddress}
- )} - - {/* U-size preview label during resize */} - {previewUSize !== null && ( -
- - {previewUSize}U - -
- )} - - {/* Port dots — only if module has ports and enough height */} - {hasPorts && height >= 28 && previewUSize === null && ( + {/* Port grid — primary face content */} + {hasPorts && previewUSize === null ? (
e.stopPropagation()} > - {module.ports.slice(0, 32).map((port) => { - const hasVlan = port.vlans.length > 0; - return ( -
+ ))} + {hiddenPortCount > 0 && ( + +{hiddenPortCount} more )}
+ ) : ( + /* No ports or resizing — show nothing (color communicates type) */ + previewUSize === null && ( +
no ports
+ ) + )} + + {/* Resize preview label */} + {previewUSize !== null && ( +
+ + {previewUSize}U + +
)} {/* Delete button — hover only */} {hovered && previewUSize === null && ( )} @@ -215,9 +225,7 @@ export function ModuleBlock({ module }: ModuleBlockProps) { className={cn( 'absolute bottom-0 left-0 right-0 h-3 flex items-center justify-center z-20', 'cursor-ns-resize touch-none', - hovered || previewUSize !== null - ? 'opacity-100' - : 'opacity-0 hover:opacity-100', + hovered || previewUSize !== null ? 'opacity-100' : 'opacity-0 hover:opacity-100', 'transition-opacity' )} onPointerDown={handleResizePointerDown} @@ -227,7 +235,7 @@ export function ModuleBlock({ module }: ModuleBlockProps) { aria-label="Resize module" title="Drag to resize" > - + diff --git a/client/src/components/rack/RackColumn.tsx b/client/src/components/rack/RackColumn.tsx index b5007f6..58c3ccf 100644 --- a/client/src/components/rack/RackColumn.tsx +++ b/client/src/components/rack/RackColumn.tsx @@ -51,7 +51,7 @@ export function RackColumn({ rack, draggingModuleId }: RackColumnProps) { return ( <> -
+
{/* Rack header — drag handle for reorder */}
{/* Drag handle */} diff --git a/client/src/lib/constants.ts b/client/src/lib/constants.ts index b112da5..3ed6a84 100644 --- a/client/src/lib/constants.ts +++ b/client/src/lib/constants.ts @@ -70,7 +70,10 @@ export const MODULE_TYPE_COLORS: Record