Overview
Dashboard Backus uses a native HTML5 drag-and-drop system to assign trucks from the queue to operational bays. The system includes real-time validation, visual feedback, and support for moving trucks between bays.
Drag Workflow
Select Truck from Queue
Click and hold any truck card in the bottom queue panel
Drag to Target Bay
Move cursor over the desired bay overlay on the map
Validation Feedback
Bay border changes color:
Green glow : Compatible bay, drop allowed
Red glow : Incompatible bay, drop rejected
Drop to Assign
Release mouse to assign truck to bay (if valid)
Supabase Update
Backend updates bahia_actual and estado in real-time
Implementation
Drag Start Handler
When a user picks up a truck from the queue:
// TarjetaCamion.tsx
const handleDragStart = ( camion : Camion ) => {
if ( ! simulacionActiva ) return ;
setCamionArrastrando ( camion );
};
< div
draggable = { simulacionActiva }
onDragStart = {() => handleDragStart ( camion )}
onDragEnd = {() => setCamionArrastrando ( null )}
className = "cursor-grab active:cursor-grabbing"
>
{ /* Truck card content */ }
</ div >
Drop Zone Configuration
Each bay overlay acts as a drop zone with validation:
// BahiaOverlay.tsx
< div
onDragOver = { e => {
e . preventDefault ();
e . dataTransfer . dropEffect = 'move' ;
setIsDragOver ( true );
}}
onDragLeave = {() => setIsDragOver ( false )}
onDrop = { e => {
e . preventDefault ();
setIsDragOver ( false );
const fromBahia = e . dataTransfer . getData ( 'fromBahia' );
if ( fromBahia ) {
onDropFromBahia ( fromBahia , bahiaId );
} else {
onDrop ( bahiaId );
}
}}
>
{ /* Bay content */ }
</ div >
Validation Logic
Before accepting a drop, the system validates three conditions:
// SimuladorMapa.tsx
const validarAsignacion = (
camion : Camion ,
bahiaId : string
) : true | string => {
// 1. Check if bay is occupied
if ( enProceso [ bahiaId ]) {
return 'Bahía ocupada' ;
}
const bay = BAHIAS_CONFIG [ bahiaId ];
// 2. Check if truck type is allowed
if ( ! bay . camionesPermitidos . includes ( camion . tipoCodigo )) {
return 'Tipo de camión no permitido' ;
}
// 3. Check if product is compatible with operation
const productos = bay . tareas [ camion . operacionCodigo ];
if ( ! productos ?. length ) {
return 'Operación no permitida en esta bahía' ;
}
const ok = productos . some ( p =>
camion . producto . toUpperCase (). includes ( p . toUpperCase ()) ||
p === 'MIXD' ||
p === 'MIXC'
);
if ( ! ok ) {
return 'Producto no admitido en esta bahía' ;
}
return true ;
};
Special Cases:
MIXD (mixed unloading) allows any unloading product
MIXC (mixed loading) allows any loading product
Product matching is case-insensitive and uses substring matching
Drop Handling
Successful Assignment
When validation passes:
const handleDrop = useCallback (( bahiaId : string ) => {
setCamionArrastrando ( null );
if ( ! camionArrastrando ) return ;
const resultado = validarAsignacion ( camionArrastrando , bahiaId );
if ( resultado !== true ) {
notify ( resultado , 'error' );
return ;
}
const bay = BAHIAS_CONFIG [ bahiaId ];
if ( bay . alerta && ! window . confirm ( bay . alerta )) return ;
const camion = camionArrastrando ;
// Remove from queue
setCola ( prev => prev . filter ( c => c . id !== camion . id ));
// Add to bay
setEnProceso ( prev => ({
... prev ,
[bahiaId]: { ... camion , bahiaActual: bahiaId }
}));
// Update Supabase
handleDropBahiaReal ( camion . id_viaje , bay . nombre );
// Update statistics
const turno = getTurnoActual ();
setStats ( prev => ({
... prev ,
total: prev . total + 1 ,
atendidosTurno1: prev . atendidosTurno1 + ( turno === 1 ? 1 : 0 ),
atendidosTurno2: prev . atendidosTurno2 + ( turno === 2 ? 1 : 0 ),
atendidosTurno3: prev . atendidosTurno3 + ( turno === 3 ? 1 : 0 ),
}));
}, [ camionArrastrando , notify ]);
Supabase Backend Update
export async function actualizarBahiaDirecto (
id_viaje : string ,
bahia_actual : string ,
estado : 'Cargando' | 'Descargando'
) : Promise < boolean > {
const payload = { bahia_actual , estado };
const { data , error } = esIdNumerico ( id_viaje )
? await supabase . from ( T_VIAJES ). update ( payload ). eq ( 'id' , Number ( id_viaje )). select ( 'id' )
: await supabase . from ( T_VIAJES ). update ( payload ). eq ( 'id_viaje' , id_viaje ). select ( 'id' );
if ( error ) {
manejarError ( 'actualizarBahiaDirecto' , error );
return false ;
}
return true ;
}
Bay-to-Bay Transfers
Operators can move trucks between bays without returning them to the queue:
const handleDropFromBahia = ( fromBahiaId : string , toBahiaId : string ) => {
const camion = enProceso [ fromBahiaId ];
if ( ! camion ) return ;
const resultado = validarAsignacion (
{ ... camion , bahiaActual: undefined },
toBahiaId
);
if ( resultado !== true ) {
notify ( resultado , 'error' );
return ;
}
if ( enProceso [ toBahiaId ]) {
notify ( 'Bahía destino ocupada' , 'error' );
return ;
}
// Move truck atomically
setEnProceso ( prev => {
const c = { ... prev };
delete c [ fromBahiaId ];
c [ toBahiaId ] = { ... camion , bahiaActual: toBahiaId };
return c ;
});
// Update backend
handleDropBahiaReal ( camion . id_viaje , BAHIAS_CONFIG [ toBahiaId ]. nombre );
notify ( `🔄 ${ camion . placa } → ${ BAHIAS_CONFIG [ toBahiaId ]. nombre } ` , 'info' );
};
Bay-to-bay transfers preserve the truck’s original queue arrival time for accurate traffic light calculations.
Visual Feedback
Drag Over State
Bays change appearance when a truck hovers over them:
let dropColor = darkMode ? 'rgba(15,23,42,0.85)' : 'rgba(255,255,255,0.90)' ;
let borderColor = darkMode ? 'rgba(148,163,184,0.22)' : 'rgba(100,116,139,0.25)' ;
if ( isDragOver && ! ocupada ) {
dropColor = camionArrastrando && validarFn ( camionArrastrando , bahiaId ) === true
? 'rgba(22,163,74,0.52)' // Green for valid
: 'rgba(220,38,38,0.44)' ; // Red for invalid
}
Cursor States
.cursor-grab { cursor : grab ; }
.active \: cursor-grabbing:active { cursor : grabbing ; }
.cursor-copy { cursor : copy ; } /* Used for drop zones */
Truck Types and Compatibility
Each bay has a whitelist of allowed truck types:
export type TipoCamion = 'P' | 'J' | 'B' | 'T' | 'O' ;
export const NOMBRES_TIPO_CAMION : Record < TipoCamion , string > = {
P: 'Parihuelero' , // Pallet truck
J: 'Jumbo' , // Large truck
B: 'Bi-tren' , // Double trailer
T: 'Tolva' , // Hopper truck
O: 'Otros' , // Other types
};
// Example bay configuration
'b10' : {
nombre: 'Bahía 10' ,
camionesPermitidos: [ 'P' ], // Only Parihuelero allowed
tareas: {
D: [ 'PT' , 'PP' , 'MIXD' ],
C: [ 'PP' , 'PT' , 'MIXC' ]
},
alerta: '¡ATENCIÓN! Coordina con T2.' ,
posX: 76.81 ,
posY: 33.62
}
Confirmation Alerts
Some bays require explicit confirmation before assignment:
const bay = BAHIAS_CONFIG [ bahiaId ];
if ( bay . alerta && ! window . confirm ( bay . alerta )) {
return ; // User cancelled
}
Example Alert:
Bahía 10 / Bahía 12
”¡ATENCIÓN! Coordina con T2.”
Requires coordination with the T2 shift supervisor before assignment.
Simulation Mode Auto-Exit
In simulation mode, trucks automatically exit after 8 seconds (real mode: 8 minutes):
if ( config . modo === 'simulacion' ) {
setTimeout (() => {
setEnProceso ( prev => {
if ( ! prev [ bahiaId ]) return prev ;
const tiempoPatio = ( Date . now () - camion . tiempoEntradaPatio ) / 60_000 ;
setStats ( s => ({
... s ,
tiemposTotalPatio: [ ... s . tiemposTotalPatio , tiempoPatio ],
}));
handleMarcarSalidaReal ( camion . id_viaje );
notify ( `✅ Finalizado (auto): ${ camion . placa } ` , 'success' );
const c = { ... prev };
delete c [ bahiaId ];
return c ;
});
}, 8 * factor ); // factor = 1000 (simulation) or 60000 (real)
}
Error Handling
Occupied Bay
Wrong Truck Type
Incompatible Product
if ( enProceso [ bahiaId ]) {
notify ( 'Bahía ocupada' , 'error' );
return ;
}
User sees: Red toast notification “Bahía ocupada”if ( ! bay . camionesPermitidos . includes ( camion . tipoCodigo )) {
notify ( 'Tipo de camión no permitido' , 'error' );
return ;
}
User sees: Red toast notification with specific errorif ( ! productos . some ( p => camion . producto . toUpperCase (). includes ( p ))) {
notify ( 'Producto no admitido en esta bahía' , 'error' );
return ;
}
User sees: Red toast notification explaining the mismatch
Interactive Map Understand the satellite map and bay positioning
Traffic Light System See how queue time affects truck priority