erp-transportistas-frontend-v2/src/features/tracking/components/ETAProgressBar.tsx

169 lines
5.3 KiB
TypeScript

/**
* ETA Progress Bar
* ERP Transportistas
* Sprint S7 - TASK-007
*
* Visual progress indicator for trip ETA.
*/
import { TipoEventoTracking } from '../types';
interface ETAProgressBarProps {
progreso: number;
etaOriginal: string;
etaActual: string;
ultimoEvento?: TipoEventoTracking;
}
const etapaLabels: Record<TipoEventoTracking, string> = {
[TipoEventoTracking.POSICION]: 'En ruta',
[TipoEventoTracking.SALIDA]: 'Salida',
[TipoEventoTracking.ARRIBO_ORIGEN]: 'En origen',
[TipoEventoTracking.INICIO_CARGA]: 'Cargando',
[TipoEventoTracking.FIN_CARGA]: 'Carga lista',
[TipoEventoTracking.ARRIBO_DESTINO]: 'En destino',
[TipoEventoTracking.INICIO_DESCARGA]: 'Descargando',
[TipoEventoTracking.FIN_DESCARGA]: 'Descarga lista',
[TipoEventoTracking.ENTREGA_POD]: 'Entregado',
[TipoEventoTracking.DESVIO]: 'Desvío',
[TipoEventoTracking.PARADA]: 'Detenido',
[TipoEventoTracking.INCIDENTE]: 'Incidente',
[TipoEventoTracking.GPS_POSICION]: 'En ruta',
[TipoEventoTracking.GEOCERCA_ENTRADA]: 'Entrando zona',
[TipoEventoTracking.GEOCERCA_SALIDA]: 'Saliendo zona',
};
export function ETAProgressBar({
progreso,
etaOriginal,
etaActual,
ultimoEvento,
}: ETAProgressBarProps) {
const etaOriginalDate = new Date(etaOriginal);
const etaActualDate = new Date(etaActual);
const diferenciaMinutos = Math.round(
(etaActualDate.getTime() - etaOriginalDate.getTime()) / 60000
);
const getStatusColor = () => {
if (diferenciaMinutos <= 0) return 'bg-green-500';
if (diferenciaMinutos <= 30) return 'bg-yellow-500';
return 'bg-red-500';
};
const getStatusText = () => {
if (diferenciaMinutos <= 0) return 'A tiempo';
if (diferenciaMinutos <= 30) return `+${diferenciaMinutos} min`;
const horas = Math.floor(diferenciaMinutos / 60);
const mins = diferenciaMinutos % 60;
return horas > 0 ? `+${horas}h ${mins}m` : `+${mins} min`;
};
const milestones = [
{ label: 'Salida', position: 0 },
{ label: 'Carga', position: 25 },
{ label: 'En ruta', position: 50 },
{ label: 'Descarga', position: 75 },
{ label: 'POD', position: 100 },
];
return (
<div className="space-y-2">
{/* Status row */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-700">Progreso:</span>
{ultimoEvento && (
<span className="rounded-full bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-700">
{etapaLabels[ultimoEvento] || ultimoEvento}
</span>
)}
</div>
<div className="flex items-center gap-2">
<span
className={`rounded-full px-2 py-0.5 text-xs font-medium ${
diferenciaMinutos <= 0
? 'bg-green-100 text-green-700'
: diferenciaMinutos <= 30
? 'bg-yellow-100 text-yellow-700'
: 'bg-red-100 text-red-700'
}`}
>
{getStatusText()}
</span>
</div>
</div>
{/* Progress bar */}
<div className="relative">
<div className="h-3 overflow-hidden rounded-full bg-gray-200">
<div
className={`h-full transition-all duration-500 ${getStatusColor()}`}
style={{ width: `${Math.min(progreso, 100)}%` }}
/>
</div>
{/* Milestone markers */}
<div className="absolute inset-0 flex items-center">
{milestones.map((milestone, index) => (
<div
key={milestone.label}
className="absolute flex flex-col items-center"
style={{
left: `${milestone.position}%`,
transform: 'translateX(-50%)',
}}
>
<div
className={`h-3 w-3 rounded-full border-2 ${
progreso >= milestone.position
? 'border-white bg-white'
: 'border-gray-400 bg-gray-200'
}`}
/>
</div>
))}
</div>
</div>
{/* Milestone labels */}
<div className="relative h-4">
{milestones.map((milestone) => (
<div
key={`label-${milestone.label}`}
className="absolute text-xs text-gray-500"
style={{
left: `${milestone.position}%`,
transform: 'translateX(-50%)',
}}
>
{milestone.label}
</div>
))}
</div>
{/* ETA comparison */}
<div className="flex items-center justify-between text-xs text-gray-500">
<div>
<span className="text-gray-400">ETA Original: </span>
<span className="font-medium text-gray-600">
{etaOriginalDate.toLocaleString('es-MX', {
dateStyle: 'short',
timeStyle: 'short',
})}
</span>
</div>
<div>
<span className="text-gray-400">ETA Actual: </span>
<span className={`font-medium ${diferenciaMinutos > 0 ? 'text-red-600' : 'text-green-600'}`}>
{etaActualDate.toLocaleString('es-MX', {
dateStyle: 'short',
timeStyle: 'short',
})}
</span>
</div>
</div>
</div>
);
}