Two root-cause bugs fixed:
1. Port <button> elements inside ModuleBlock had pointer-events:auto (browser
default), so document.elementFromPoint() hit them instead of the RackSlot
behind them whenever the cursor was over an occupied slot. Fixed by toggling
body.rack-dragging during any drag, which applies a CSS rule that forces
pointer-events:none !important on .module-block and all descendants.
2. onDragMove pointer-position reconstruction (activatorEvent.clientX + delta.x)
was slightly off because delta is measured from the initial mousedown, not
the activation point. Replaced with a native window pointermove listener
(capture phase) that gives exact clientX/Y — no reconstruction needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Completely removes dnd-kit's useDroppable and collision detection for rack
slot targeting. Uses onDragMove + document.elementFromPoint() with data-rack-id
/ data-u-pos HTML attributes on RackSlot elements to resolve the hovered slot
independently of dnd-kit's SortableContext interference. Adds pointer-events-none
to ModuleBlock when isDragging so the invisible element doesn't block hit testing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Module drag broken:
listeners were on a 12px grip strip only; dragging anywhere else on
the block had no effect. Moved {...listeners} {...attributes} to the
outer container so the whole module face is the drag source.
Port buttons now stop pointerdown propagation so clicking a port does
not accidentally start a drag. Resize handle also stops pointerdown
propagation before forwarding to its own handler.
Removed the now-redundant GripVertical strip.
Delete button covering ports 23-24:
Removed the absolute-positioned Trash2 button from ModuleBlock face.
Delete is now inside ModuleEditPanel with an inline confirm flow:
- 'Delete module' link in the modal footer (left side)
- Clicking shows 'Remove this module? [Delete] [Cancel]' inline
- On confirm: calls API, removeModuleLocal, closes modal
ConfirmDialog import and related state also removed from ModuleBlock.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problems fixed:
- Name label + type badge were eating all horizontal space in 1U modules,
pushing 24 port dots into a cramped overflow that was barely visible
- U_HEIGHT_PX=28 was too tight to show a full port row at all
- Column width (192px) was too narrow to fit 24x10px dots + gaps (286px needed)
Changes:
- U_HEIGHT_PX: 28 -> 44px (enough room for ports + resize handle)
- RackColumn: w-48 (192px) -> w-80 (320px), min-w-[200px] -> min-w-[320px]
- PORTS_PER_ROW = 24 constant added to constants.ts
- ModuleBlock face redesigned:
* Removed name <span> and type <Badge> from the visible face
* Module name + IP now shown as a native title tooltip on hover
* Port dots are the primary face content (24 per row, gap-[3px])
* Multiple rows rendered for multi-U modules (up to available height)
* Hidden port overflow shown as "+N more" below the rows
* Drag handle slimmed to 12px; delete/resize handles unchanged
* Type still communicated via background color
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Drag handle at bottom edge of each module (GripHorizontal icon)
- Pointer capture tracks vertical drag delta → U-size delta
- Clamped to: minimum 1U, rack bounds, first module below
- Shows current U-size label during active resize
- On release: PUT /modules/:id with new uSize (server validates collision)
- Optimistic store update via updateModuleLocal on success
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Complete project scaffold with working auth, REST API, Prisma/SQLite
schema, Docker config, and React frontend for both Rack Planner and
Service Mapper modules. Both server and client pass TypeScript strict
mode with zero errors. Initial migration applied.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>