@@ -254,6 +254,41 @@ export default function RackPlanner() {
254254
255255 // --- Drag and Drop Handlers ---
256256
257+ // Helper: Check if a module can be dropped at targetIndex
258+ const checkCanDrop = (
259+ targetIndex : number ,
260+ moduleUSize : number ,
261+ originalIndex ?: number
262+ ) : boolean => {
263+ // Boundary Check: If top of module goes below index 0 (top of rack)
264+ if ( targetIndex - moduleUSize + 1 < 0 ) {
265+ return false ;
266+ }
267+
268+ // Collision Check
269+ const slotsNeeded = moduleUSize ;
270+ let originalModuleId : string | null = null ;
271+
272+ if ( originalIndex !== undefined && rackSlots [ originalIndex ] ) {
273+ originalModuleId = rackSlots [ originalIndex ] . moduleId ;
274+ }
275+
276+ for ( let i = 0 ; i < slotsNeeded ; i ++ ) {
277+ const slotIndex = targetIndex - i ;
278+ if ( slotIndex < 0 ) return false ;
279+
280+ const slot = rackSlots [ slotIndex ] ;
281+ if ( slot . moduleId !== null ) {
282+ // If it's occupied, check if it's the module we are currently moving
283+ if ( originalModuleId && slot . moduleId === originalModuleId ) {
284+ continue ; // Same module, safe.
285+ }
286+ return false ; // Collision with another module
287+ }
288+ }
289+ return true ;
290+ } ;
291+
257292 const handleDragStart = ( e : React . DragEvent , module : RackModule , originalIndex ?: number ) => {
258293 setDraggedItem ( { module, originalIndex } ) ;
259294 e . dataTransfer . effectAllowed = 'move' ;
@@ -274,17 +309,17 @@ export default function RackPlanner() {
274309 const { module, originalIndex } = draggedItem ;
275310 const slotsNeeded = module . uSize ;
276311
277- // Boundary Check
278- if ( targetIndex - slotsNeeded + 1 < 0 ) {
279- alert ( `Not enough space at top for a ${ slotsNeeded } U module.` ) ;
312+ if ( ! checkCanDrop ( targetIndex , slotsNeeded , originalIndex ) ) {
313+ // Visual feedback is shown during drag, but if they drop anyway, we just cancel.
314+ // Optionally alert if you want, but silent fail is arguably better if UI provides red cue.
280315 setDraggedItem ( null ) ;
281316 return ;
282317 }
283318
284- // Collision Check
319+ // Proceed to place item
285320 const newSlots = [ ...rackSlots ] ;
286321
287- // 1. If moving, clear old position first
322+ // 1. Clear old position if moving
288323 if ( originalIndex !== undefined ) {
289324 const originalId = newSlots [ originalIndex ] . moduleId ;
290325 if ( originalId ) {
@@ -296,22 +331,7 @@ export default function RackPlanner() {
296331 }
297332 }
298333
299- // 2. Check for collisions
300- let collision = false ;
301- for ( let i = 0 ; i < slotsNeeded ; i ++ ) {
302- if ( newSlots [ targetIndex - i ] . moduleId !== null ) {
303- collision = true ;
304- break ;
305- }
306- }
307-
308- if ( collision ) {
309- alert ( 'Space is occupied.' ) ;
310- setDraggedItem ( null ) ;
311- return ;
312- }
313-
314- // 3. Place item
334+ // 2. Place item
315335 const instanceId =
316336 originalIndex !== undefined
317337 ? rackSlots [ originalIndex ! ] . moduleId !
@@ -464,6 +484,8 @@ export default function RackPlanner() {
464484 ) ;
465485 }
466486
487+
488+
467489 return (
468490 < div className = "h-screen w-full overflow-hidden flex flex-col" >
469491 < div className = "flex-1 flex flex-col bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 font-sans transition-colors duration-300" >
@@ -573,7 +595,26 @@ export default function RackPlanner() {
573595 const isModulePart = isOccupied && ! isModuleStart ;
574596
575597 // Drag Over styling
576- const isDragTarget = dragOverIndex === index ;
598+ const uSize = draggedItem ?. module . uSize || 1 ;
599+ const isDragTarget =
600+ dragOverIndex !== null &&
601+ index <= dragOverIndex &&
602+ index > dragOverIndex - uSize ;
603+ // Only show text on the main hover slot
604+ const isMainDragTarget = dragOverIndex === index ;
605+
606+ // Determine validity if it's a drag target
607+ // Use checkCanDrop on the dragOverIndex, not current index (since highlight depends on master drop index)
608+ // But wait, the loop runs for each slot.
609+ // If I am highlighting slots 4 and 5 because dragOverIndex is 5 (and size is 2).
610+ // I want to know if dropping at 5 is valid.
611+
612+ const isValid = dragOverIndex !== null ? checkCanDrop ( dragOverIndex , uSize , draggedItem ?. originalIndex ) : true ;
613+
614+ // Styles
615+ const dragColorClass = isValid
616+ ? 'bg-indigo-500/20 border-indigo-500' // Valid
617+ : 'bg-red-500/20 border-red-500' ; // Invalid
577618
578619 // Calculate height if start
579620 const moduleHeight = isModuleStart
@@ -585,7 +626,7 @@ export default function RackPlanner() {
585626 key = { index }
586627 onDragOver = { ( e ) => handleDragOver ( e , index ) }
587628 onDrop = { ( e ) => handleDrop ( e , index ) }
588- className = { `relative flex w-full transition-colors ${ isDragTarget ? 'bg-indigo-500/20 z-20' : ''
629+ className = { `relative flex w-full transition-colors ${ isDragTarget ? ` ${ dragColorClass } z-20` : ''
589630 } `}
590631 style = { { height : isModulePart ? 0 : moduleHeight } } // Collapse covered slots
591632 >
@@ -733,10 +774,12 @@ export default function RackPlanner() {
733774
734775 { /* Drop Zone Indicator */ }
735776 { isDragTarget && (
736- < div className = "absolute inset-0 border-2 border-indigo-500 bg-indigo-500/10 z-30 pointer-events-none flex items-center justify-center" >
737- < span className = "text-xs font-bold text-indigo-500 bg-white dark:bg-gray-900 px-2 py-1 rounded shadow" >
738- Drop Here ({ draggedItem ?. module . uSize } U)
739- </ span >
777+ < div className = { `absolute inset-0 border-2 ${ isValid ? 'border-indigo-500 bg-indigo-500/10' : 'border-red-500 bg-red-500/10' } z-30 pointer-events-none flex items-center justify-center` } >
778+ { isMainDragTarget && (
779+ < span className = { `text-xs font-bold ${ isValid ? 'text-indigo-500' : 'text-red-500' } bg-white dark:bg-gray-900 px-2 py-1 rounded shadow` } >
780+ { isValid ? `Mount Here (${ uSize } U)` : 'Cannot Mount Here' }
781+ </ span >
782+ ) }
740783 </ div >
741784 ) }
742785 </ div >
0 commit comments