[SPRINT-0] fix: Resolve all TypeScript compilation errors in frontend
- Add @types/node for vite.config.ts __dirname support - Extend OrdenTransporte interface with alias properties - Add missing enum states to OTStatusBadge - Fix EventosList type references and property access - Fix GeocercasList property names (activo -> activa) - Fix useTrackingWebSocket NodeJS.Timeout type - Remove unused imports across components Build now passes: 191 modules transformed successfully Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b3982844f8
commit
0bc16b52bf
18
package-lock.json
generated
18
package-lock.json
generated
@ -26,6 +26,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/leaflet": "^1.9.8",
|
||||
"@types/node": "^25.2.0",
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||
@ -1505,6 +1506,16 @@
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz",
|
||||
"integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
||||
@ -6695,6 +6706,13 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||
|
||||
35
package.json
35
package.json
@ -15,37 +15,38 @@
|
||||
"test:cov": "vitest run --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.3.2",
|
||||
"@tanstack/react-query": "^5.8.4",
|
||||
"axios": "^1.7.7",
|
||||
"clsx": "^2.0.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"lucide-react": "^0.460.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"zustand": "^5.0.1",
|
||||
"axios": "^1.7.7",
|
||||
"@tanstack/react-query": "^5.8.4",
|
||||
"lucide-react": "^0.460.0",
|
||||
"date-fns": "^2.30.0",
|
||||
"clsx": "^2.0.0",
|
||||
"react-hook-form": "^7.48.2",
|
||||
"@hookform/resolvers": "^3.3.2",
|
||||
"react-leaflet": "^4.2.1",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"zod": "^3.22.4",
|
||||
"leaflet": "^1.9.4",
|
||||
"react-leaflet": "^4.2.1"
|
||||
"zustand": "^5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/leaflet": "^1.9.8",
|
||||
"@types/node": "^25.2.0",
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@types/leaflet": "^1.9.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||
"@typescript-eslint/parser": "^6.12.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"postcss": "^8.4.32",
|
||||
"tailwindcss": "^3.4.15",
|
||||
"typescript": "^5.3.2",
|
||||
"vite": "^5.0.8",
|
||||
"vitest": "^1.0.4",
|
||||
"eslint": "^8.54.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||
"@typescript-eslint/parser": "^6.12.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0"
|
||||
"vitest": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { ordenesTransporteApi } from '../api/ordenes-transporte.api';
|
||||
import { OTStatusBadge } from './OTStatusBadge';
|
||||
import type { OrdenTransporte, EstadoOrdenTransporte } from '../types';
|
||||
import type { OrdenTransporte } from '../types';
|
||||
|
||||
interface OTDetailProps {
|
||||
orden: OrdenTransporte;
|
||||
@ -20,7 +20,7 @@ export function OTDetail({ orden, onClose, onEdit }: OTDetailProps) {
|
||||
});
|
||||
|
||||
const cancelarMutation = useMutation({
|
||||
mutationFn: () => ordenesTransporteApi.cancelar(orden.id),
|
||||
mutationFn: (motivo: string) => ordenesTransporteApi.cancelar(orden.id, motivo),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['ordenes-transporte'] });
|
||||
},
|
||||
@ -59,8 +59,9 @@ export function OTDetail({ orden, onClose, onEdit }: OTDetailProps) {
|
||||
};
|
||||
|
||||
const handleCancelar = async () => {
|
||||
if (window.confirm('¿Cancelar esta orden de transporte?')) {
|
||||
await cancelarMutation.mutateAsync();
|
||||
const motivo = window.prompt('Motivo de cancelación:');
|
||||
if (motivo) {
|
||||
await cancelarMutation.mutateAsync(motivo);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { ordenesTransporteApi } from '../api/ordenes-transporte.api';
|
||||
import { TipoCarga, ModalidadServicio, TipoEquipo } from '../types';
|
||||
import type { OrdenTransporte, CreateOTDto, UpdateOTDto } from '../types';
|
||||
|
||||
const otSchema = z.object({
|
||||
@ -72,20 +73,20 @@ export function OTForm({ initialData, onSuccess, onCancel }: OTFormProps) {
|
||||
? {
|
||||
clienteId: initialData.clienteId,
|
||||
referenciaCliente: initialData.referenciaCliente || '',
|
||||
modalidad: initialData.modalidad,
|
||||
tipoCarga: initialData.tipoCarga,
|
||||
tipoEquipo: initialData.tipoEquipo,
|
||||
modalidad: (initialData.modalidad || 'FTL') as 'FTL' | 'LTL' | 'DEDICADO' | 'EXPRESS' | 'CONSOLIDADO',
|
||||
tipoCarga: initialData.tipoCarga as 'GENERAL' | 'REFRIGERADA' | 'PELIGROSA' | 'SOBREDIMENSIONADA' | 'FRAGIL' | 'GRANEL' | 'LIQUIDOS' | 'CONTENEDOR',
|
||||
tipoEquipo: initialData.tipoEquipo as 'CAJA_SECA' | 'REFRIGERADO' | 'PLATAFORMA' | 'TANQUE' | 'LOWBOY' | 'PORTACONTENEDOR' | 'TOLVA' | 'GONDOLA' | undefined,
|
||||
origenDireccion: initialData.origenDireccion,
|
||||
origenCiudad: initialData.origenCiudad,
|
||||
origenEstado: initialData.origenEstado,
|
||||
origenCiudad: initialData.origenCiudad || '',
|
||||
origenEstado: initialData.origenEstado || '',
|
||||
origenCP: initialData.origenCP || '',
|
||||
origenPais: initialData.origenPais || 'MX',
|
||||
destinoDireccion: initialData.destinoDireccion,
|
||||
destinoCiudad: initialData.destinoCiudad,
|
||||
destinoEstado: initialData.destinoEstado,
|
||||
destinoCiudad: initialData.destinoCiudad || '',
|
||||
destinoEstado: initialData.destinoEstado || '',
|
||||
destinoCP: initialData.destinoCP || '',
|
||||
destinoPais: initialData.destinoPais || 'MX',
|
||||
fechaRecoleccion: initialData.fechaRecoleccion?.split('T')[0],
|
||||
fechaRecoleccion: initialData.fechaRecoleccion?.split('T')[0] || '',
|
||||
fechaEntregaEstimada: initialData.fechaEntregaEstimada?.split('T')[0],
|
||||
pesoBruto: initialData.pesoBruto,
|
||||
pesoNeto: initialData.pesoNeto,
|
||||
@ -99,8 +100,16 @@ export function OTForm({ initialData, onSuccess, onCancel }: OTFormProps) {
|
||||
notas: initialData.notas || '',
|
||||
}
|
||||
: {
|
||||
modalidad: 'FTL',
|
||||
tipoCarga: 'GENERAL',
|
||||
clienteId: '',
|
||||
origenDireccion: '',
|
||||
origenCiudad: '',
|
||||
origenEstado: '',
|
||||
destinoDireccion: '',
|
||||
destinoCiudad: '',
|
||||
destinoEstado: '',
|
||||
fechaRecoleccion: '',
|
||||
modalidad: 'FTL' as const,
|
||||
tipoCarga: 'GENERAL' as const,
|
||||
origenPais: 'MX',
|
||||
destinoPais: 'MX',
|
||||
moneda: 'MXN',
|
||||
@ -124,10 +133,17 @@ export function OTForm({ initialData, onSuccess, onCancel }: OTFormProps) {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: OTFormData) => {
|
||||
const dto = {
|
||||
...data,
|
||||
tipoCarga: data.tipoCarga as TipoCarga,
|
||||
modalidadServicio: data.modalidad as ModalidadServicio,
|
||||
modalidad: data.modalidad as ModalidadServicio,
|
||||
tipoEquipo: data.tipoEquipo as TipoEquipo | undefined,
|
||||
};
|
||||
if (isEditing) {
|
||||
await updateMutation.mutateAsync(data as UpdateOTDto);
|
||||
await updateMutation.mutateAsync(dto as UpdateOTDto);
|
||||
} else {
|
||||
await createMutation.mutateAsync(data as CreateOTDto);
|
||||
await createMutation.mutateAsync(dto as CreateOTDto);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -8,10 +8,13 @@ interface OTStatusBadgeProps {
|
||||
const estadoConfig: Record<EstadoOrdenTransporte, { label: string; color: string }> = {
|
||||
[EstadoOrdenTransporte.BORRADOR]: { label: 'Borrador', color: 'bg-gray-100 text-gray-800' },
|
||||
[EstadoOrdenTransporte.PENDIENTE]: { label: 'Pendiente', color: 'bg-yellow-100 text-yellow-800' },
|
||||
[EstadoOrdenTransporte.SOLICITADA]: { label: 'Solicitada', color: 'bg-orange-100 text-orange-800' },
|
||||
[EstadoOrdenTransporte.CONFIRMADA]: { label: 'Confirmada', color: 'bg-blue-100 text-blue-800' },
|
||||
[EstadoOrdenTransporte.PROGRAMADA]: { label: 'Programada', color: 'bg-indigo-100 text-indigo-800' },
|
||||
[EstadoOrdenTransporte.ASIGNADA]: { label: 'Asignada', color: 'bg-indigo-100 text-indigo-800' },
|
||||
[EstadoOrdenTransporte.EN_PROCESO]: { label: 'En Proceso', color: 'bg-purple-100 text-purple-800' },
|
||||
[EstadoOrdenTransporte.EN_TRANSITO]: { label: 'En Tránsito', color: 'bg-cyan-100 text-cyan-800' },
|
||||
[EstadoOrdenTransporte.COMPLETADA]: { label: 'Completada', color: 'bg-green-100 text-green-800' },
|
||||
[EstadoOrdenTransporte.ENTREGADA]: { label: 'Entregada', color: 'bg-emerald-100 text-emerald-800' },
|
||||
[EstadoOrdenTransporte.FACTURADA]: { label: 'Facturada', color: 'bg-teal-100 text-teal-800' },
|
||||
[EstadoOrdenTransporte.CANCELADA]: { label: 'Cancelada', color: 'bg-red-100 text-red-800' },
|
||||
};
|
||||
|
||||
@ -25,6 +25,7 @@ export enum TipoCarga {
|
||||
LIQUIDOS = 'LIQUIDOS',
|
||||
CONTENEDOR = 'CONTENEDOR',
|
||||
AUTOMOVILES = 'AUTOMOVILES',
|
||||
FRAGIL = 'FRAGIL',
|
||||
}
|
||||
|
||||
export enum ModalidadServicio {
|
||||
@ -51,8 +52,10 @@ export interface OrdenTransporte {
|
||||
tenantId: string;
|
||||
codigo: string;
|
||||
numeroOt?: string;
|
||||
numero?: string; // Alias for display
|
||||
referenciaCliente?: string;
|
||||
clienteId: string;
|
||||
clienteNombre?: string; // For display
|
||||
shipperId?: string;
|
||||
shipperNombre: string;
|
||||
consigneeId: string;
|
||||
@ -60,8 +63,10 @@ export interface OrdenTransporte {
|
||||
// Origen
|
||||
origenDireccion: string;
|
||||
origenCodigoPostal?: string;
|
||||
origenCP?: string; // Alias
|
||||
origenCiudad?: string;
|
||||
origenEstado?: string;
|
||||
origenPais?: string;
|
||||
origenLatitud?: number;
|
||||
origenLongitud?: number;
|
||||
origenContacto?: string;
|
||||
@ -69,8 +74,10 @@ export interface OrdenTransporte {
|
||||
// Destino
|
||||
destinoDireccion: string;
|
||||
destinoCodigoPostal?: string;
|
||||
destinoCP?: string; // Alias
|
||||
destinoCiudad?: string;
|
||||
destinoEstado?: string;
|
||||
destinoPais?: string;
|
||||
destinoLatitud?: number;
|
||||
destinoLongitud?: number;
|
||||
destinoContacto?: string;
|
||||
@ -79,14 +86,21 @@ export interface OrdenTransporte {
|
||||
fechaRecoleccion?: string;
|
||||
fechaRecoleccionProgramada?: string;
|
||||
fechaEntregaProgramada?: string;
|
||||
fechaEntregaEstimada?: string; // Alias
|
||||
// Carga
|
||||
tipoCarga: TipoCarga;
|
||||
descripcionCarga?: string;
|
||||
descripcionMercancia?: string; // Alias
|
||||
pesoKg?: number;
|
||||
pesoBruto?: number; // Alias
|
||||
pesoNeto?: number;
|
||||
volumenM3?: number;
|
||||
volumen?: number; // Alias
|
||||
piezas?: number;
|
||||
pallets?: number;
|
||||
cantidadBultos?: number;
|
||||
valorDeclarado?: number;
|
||||
valorMercancia?: number; // Alias
|
||||
// Requisitos
|
||||
requiereTemperatura: boolean;
|
||||
temperaturaMin?: number;
|
||||
@ -95,18 +109,24 @@ export interface OrdenTransporte {
|
||||
requiereEscolta: boolean;
|
||||
instruccionesEspeciales?: string;
|
||||
observaciones?: string;
|
||||
notas?: string; // Alias
|
||||
// Servicio y tarifa
|
||||
modalidadServicio: ModalidadServicio;
|
||||
modalidad?: ModalidadServicio; // Alias
|
||||
tipoEquipo?: TipoEquipo;
|
||||
tarifaId?: string;
|
||||
tarifaBase?: number;
|
||||
tarifaTotal?: number;
|
||||
recargos: number;
|
||||
descuentos: number;
|
||||
subtotal?: number;
|
||||
iva?: number;
|
||||
total?: number;
|
||||
moneda?: string;
|
||||
// Estado y asignacion
|
||||
estado: EstadoOrdenTransporte;
|
||||
viajeId?: string;
|
||||
viajeNumero?: string;
|
||||
embarqueId?: string;
|
||||
// Auditoria
|
||||
createdAt: string;
|
||||
@ -136,13 +156,15 @@ export interface CreateOrdenTransporteDto {
|
||||
referenciaCliente?: string;
|
||||
clienteId: string;
|
||||
shipperId?: string;
|
||||
shipperNombre: string;
|
||||
consigneeId: string;
|
||||
consigneeNombre: string;
|
||||
shipperNombre?: string;
|
||||
consigneeId?: string;
|
||||
consigneeNombre?: string;
|
||||
origenDireccion: string;
|
||||
origenCodigoPostal?: string;
|
||||
origenCiudad?: string;
|
||||
origenEstado?: string;
|
||||
origenPais?: string;
|
||||
origenCP?: string;
|
||||
origenLatitud?: number;
|
||||
origenLongitud?: number;
|
||||
origenContacto?: string;
|
||||
@ -151,19 +173,29 @@ export interface CreateOrdenTransporteDto {
|
||||
destinoCodigoPostal?: string;
|
||||
destinoCiudad?: string;
|
||||
destinoEstado?: string;
|
||||
destinoPais?: string;
|
||||
destinoCP?: string;
|
||||
destinoLatitud?: number;
|
||||
destinoLongitud?: number;
|
||||
destinoContacto?: string;
|
||||
destinoTelefono?: string;
|
||||
fechaRecoleccion?: string;
|
||||
fechaRecoleccionProgramada?: string;
|
||||
fechaEntregaProgramada?: string;
|
||||
fechaEntregaEstimada?: string;
|
||||
tipoCarga?: TipoCarga;
|
||||
descripcionCarga?: string;
|
||||
descripcionMercancia?: string;
|
||||
pesoKg?: number;
|
||||
pesoBruto?: number;
|
||||
pesoNeto?: number;
|
||||
volumenM3?: number;
|
||||
volumen?: number;
|
||||
piezas?: number;
|
||||
pallets?: number;
|
||||
cantidadBultos?: number;
|
||||
valorDeclarado?: number;
|
||||
valorMercancia?: number;
|
||||
requiereTemperatura?: boolean;
|
||||
temperaturaMin?: number;
|
||||
temperaturaMax?: number;
|
||||
@ -171,9 +203,22 @@ export interface CreateOrdenTransporteDto {
|
||||
requiereEscolta?: boolean;
|
||||
instruccionesEspeciales?: string;
|
||||
modalidadServicio?: ModalidadServicio;
|
||||
modalidad?: ModalidadServicio;
|
||||
tipoEquipo?: TipoEquipo;
|
||||
tarifaBase?: number;
|
||||
tarifaTotal?: number;
|
||||
moneda?: string;
|
||||
notas?: string;
|
||||
}
|
||||
|
||||
export interface UpdateOrdenTransporteDto extends Partial<CreateOrdenTransporteDto> {
|
||||
estado?: EstadoOrdenTransporte;
|
||||
observaciones?: string;
|
||||
}
|
||||
|
||||
// Type aliases for shorter names (used in components)
|
||||
export type CreateOTDto = CreateOrdenTransporteDto;
|
||||
export type UpdateOTDto = UpdateOrdenTransporteDto;
|
||||
export type OTFilters = OrdenTransporteFilters & {
|
||||
tipoCarga?: TipoCarga;
|
||||
};
|
||||
|
||||
@ -105,7 +105,7 @@ export function ETAProgressBar({
|
||||
|
||||
{/* Milestone markers */}
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
{milestones.map((milestone, index) => (
|
||||
{milestones.map((milestone) => (
|
||||
<div
|
||||
key={milestone.label}
|
||||
className="absolute flex flex-col items-center"
|
||||
|
||||
@ -11,7 +11,6 @@ import { TipoEventoTracking, FuenteEvento, type EventoTracking } from '../types'
|
||||
interface EventTimelineProps {
|
||||
eventos: EventoTracking[];
|
||||
loading?: boolean;
|
||||
viajeId?: string;
|
||||
}
|
||||
|
||||
const tipoEventoConfig: Record<TipoEventoTracking, { label: string; icon: string; color: string }> = {
|
||||
@ -100,7 +99,7 @@ const fuenteLabels: Record<FuenteEvento, string> = {
|
||||
[FuenteEvento.GEOCERCA]: 'Geocerca',
|
||||
};
|
||||
|
||||
export function EventTimeline({ eventos, loading = false, viajeId }: EventTimelineProps) {
|
||||
export function EventTimeline({ eventos, loading = false }: EventTimelineProps) {
|
||||
// Filter out frequent GPS positions to show only meaningful events
|
||||
const eventosImportantes = eventos.filter(
|
||||
(e) =>
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { trackingApi } from '../api/tracking.api';
|
||||
import type { EventoTracking, EventoTrackingFilters, TipoEventoTracking } from '../types';
|
||||
import { TipoEventoTracking } from '../types';
|
||||
import type { EventoTracking, EventoFilters } from '../types';
|
||||
|
||||
interface EventosListProps {
|
||||
viajeId?: string;
|
||||
@ -10,33 +11,39 @@ interface EventosListProps {
|
||||
}
|
||||
|
||||
const tipoEventoLabels: Record<TipoEventoTracking, string> = {
|
||||
POSICION: 'Posición',
|
||||
ENCENDIDO: 'Encendido',
|
||||
APAGADO: 'Apagado',
|
||||
VELOCIDAD_EXCESIVA: 'Velocidad Excesiva',
|
||||
ENTRADA_GEOCERCA: 'Entrada Geocerca',
|
||||
SALIDA_GEOCERCA: 'Salida Geocerca',
|
||||
PARADA_PROLONGADA: 'Parada Prolongada',
|
||||
DESVIO_RUTA: 'Desvío de Ruta',
|
||||
BOTON_PANICO: 'Botón de Pánico',
|
||||
BATERIA_BAJA: 'Batería Baja',
|
||||
DESCONEXION_GPS: 'Desconexión GPS',
|
||||
RECONEXION_GPS: 'Reconexión GPS',
|
||||
[TipoEventoTracking.POSICION]: 'Posición',
|
||||
[TipoEventoTracking.SALIDA]: 'Salida',
|
||||
[TipoEventoTracking.ARRIBO_ORIGEN]: 'Arribo a Origen',
|
||||
[TipoEventoTracking.INICIO_CARGA]: 'Inicio de Carga',
|
||||
[TipoEventoTracking.FIN_CARGA]: 'Fin de Carga',
|
||||
[TipoEventoTracking.ARRIBO_DESTINO]: 'Arribo a Destino',
|
||||
[TipoEventoTracking.INICIO_DESCARGA]: 'Inicio de Descarga',
|
||||
[TipoEventoTracking.FIN_DESCARGA]: 'Fin de Descarga',
|
||||
[TipoEventoTracking.ENTREGA_POD]: 'POD Entregado',
|
||||
[TipoEventoTracking.DESVIO]: 'Desvío',
|
||||
[TipoEventoTracking.PARADA]: 'Parada',
|
||||
[TipoEventoTracking.INCIDENTE]: 'Incidente',
|
||||
[TipoEventoTracking.GPS_POSICION]: 'Posición GPS',
|
||||
[TipoEventoTracking.GEOCERCA_ENTRADA]: 'Entrada Geocerca',
|
||||
[TipoEventoTracking.GEOCERCA_SALIDA]: 'Salida Geocerca',
|
||||
};
|
||||
|
||||
const tipoEventoColors: Record<TipoEventoTracking, string> = {
|
||||
POSICION: 'bg-gray-100 text-gray-800',
|
||||
ENCENDIDO: 'bg-green-100 text-green-800',
|
||||
APAGADO: 'bg-red-100 text-red-800',
|
||||
VELOCIDAD_EXCESIVA: 'bg-orange-100 text-orange-800',
|
||||
ENTRADA_GEOCERCA: 'bg-blue-100 text-blue-800',
|
||||
SALIDA_GEOCERCA: 'bg-purple-100 text-purple-800',
|
||||
PARADA_PROLONGADA: 'bg-yellow-100 text-yellow-800',
|
||||
DESVIO_RUTA: 'bg-pink-100 text-pink-800',
|
||||
BOTON_PANICO: 'bg-red-200 text-red-900',
|
||||
BATERIA_BAJA: 'bg-amber-100 text-amber-800',
|
||||
DESCONEXION_GPS: 'bg-gray-200 text-gray-900',
|
||||
RECONEXION_GPS: 'bg-teal-100 text-teal-800',
|
||||
[TipoEventoTracking.POSICION]: 'bg-gray-100 text-gray-800',
|
||||
[TipoEventoTracking.SALIDA]: 'bg-green-100 text-green-800',
|
||||
[TipoEventoTracking.ARRIBO_ORIGEN]: 'bg-blue-100 text-blue-800',
|
||||
[TipoEventoTracking.INICIO_CARGA]: 'bg-blue-200 text-blue-900',
|
||||
[TipoEventoTracking.FIN_CARGA]: 'bg-blue-100 text-blue-800',
|
||||
[TipoEventoTracking.ARRIBO_DESTINO]: 'bg-purple-100 text-purple-800',
|
||||
[TipoEventoTracking.INICIO_DESCARGA]: 'bg-purple-200 text-purple-900',
|
||||
[TipoEventoTracking.FIN_DESCARGA]: 'bg-purple-100 text-purple-800',
|
||||
[TipoEventoTracking.ENTREGA_POD]: 'bg-green-200 text-green-900',
|
||||
[TipoEventoTracking.DESVIO]: 'bg-yellow-100 text-yellow-800',
|
||||
[TipoEventoTracking.PARADA]: 'bg-orange-100 text-orange-800',
|
||||
[TipoEventoTracking.INCIDENTE]: 'bg-red-100 text-red-800',
|
||||
[TipoEventoTracking.GPS_POSICION]: 'bg-gray-200 text-gray-900',
|
||||
[TipoEventoTracking.GEOCERCA_ENTRADA]: 'bg-cyan-100 text-cyan-800',
|
||||
[TipoEventoTracking.GEOCERCA_SALIDA]: 'bg-cyan-200 text-cyan-900',
|
||||
};
|
||||
|
||||
export function EventosList({ viajeId, unidadId, onSelect }: EventosListProps) {
|
||||
@ -44,10 +51,10 @@ export function EventosList({ viajeId, unidadId, onSelect }: EventosListProps) {
|
||||
const [tipoFilter, setTipoFilter] = useState<TipoEventoTracking | ''>('');
|
||||
const limit = 20;
|
||||
|
||||
const filters: EventoTrackingFilters = {
|
||||
const filters: EventoFilters = {
|
||||
viajeId,
|
||||
unidadId,
|
||||
tipo: tipoFilter || undefined,
|
||||
tipoEvento: tipoFilter || undefined,
|
||||
limit,
|
||||
offset: (page - 1) * limit,
|
||||
};
|
||||
@ -114,10 +121,10 @@ export function EventosList({ viajeId, unidadId, onSelect }: EventosListProps) {
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={`rounded-full px-2 py-0.5 text-xs font-medium ${
|
||||
tipoEventoColors[evento.tipo] || 'bg-gray-100 text-gray-800'
|
||||
tipoEventoColors[evento.tipoEvento] || 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{tipoEventoLabels[evento.tipo] || evento.tipo}
|
||||
{tipoEventoLabels[evento.tipoEvento] || evento.tipoEvento}
|
||||
</span>
|
||||
<span className="text-sm text-gray-500">
|
||||
{formatDateTime(evento.timestamp)}
|
||||
@ -139,9 +146,9 @@ export function EventosList({ viajeId, unidadId, onSelect }: EventosListProps) {
|
||||
)}
|
||||
|
||||
{/* Additional data */}
|
||||
{evento.datosExtra && Object.keys(evento.datosExtra).length > 0 && (
|
||||
{evento.datosAdicionales && Object.keys(evento.datosAdicionales).length > 0 && (
|
||||
<div className="mt-2 text-xs text-gray-500">
|
||||
{Object.entries(evento.datosExtra)
|
||||
{Object.entries(evento.datosAdicionales)
|
||||
.slice(0, 3)
|
||||
.map(([key, value]) => (
|
||||
<span key={key} className="mr-3">
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { trackingApi } from '../api/tracking.api';
|
||||
import type { Geocerca, GeocercaFilters, TipoGeocerca } from '../types';
|
||||
import { TipoGeocerca } from '../types';
|
||||
import type { Geocerca, GeocercaFilters } from '../types';
|
||||
|
||||
interface GeocercasListProps {
|
||||
onSelect?: (geocerca: Geocerca) => void;
|
||||
@ -9,29 +10,29 @@ interface GeocercasListProps {
|
||||
}
|
||||
|
||||
const tipoGeocercaLabels: Record<TipoGeocerca, string> = {
|
||||
CLIENTE: 'Cliente',
|
||||
ALMACEN: 'Almacén',
|
||||
GASOLINERA: 'Gasolinera',
|
||||
CASETA: 'Caseta',
|
||||
PUNTO_CONTROL: 'Punto de Control',
|
||||
ZONA_RIESGO: 'Zona de Riesgo',
|
||||
ZONA_DESCANSO: 'Zona de Descanso',
|
||||
FRONTERA: 'Frontera',
|
||||
ADUANA: 'Aduana',
|
||||
PUERTO: 'Puerto',
|
||||
[TipoGeocerca.CIRCULAR]: 'Circular',
|
||||
[TipoGeocerca.POLIGONAL]: 'Poligonal',
|
||||
[TipoGeocerca.CLIENTE]: 'Cliente',
|
||||
[TipoGeocerca.PROVEEDOR]: 'Proveedor',
|
||||
[TipoGeocerca.PATIO]: 'Patio',
|
||||
[TipoGeocerca.ZONA_RIESGO]: 'Zona de Riesgo',
|
||||
[TipoGeocerca.CASETA]: 'Caseta',
|
||||
[TipoGeocerca.GASOLINERA]: 'Gasolinera',
|
||||
[TipoGeocerca.PUNTO_CONTROL]: 'Punto de Control',
|
||||
[TipoGeocerca.OTRO]: 'Otro',
|
||||
};
|
||||
|
||||
const tipoGeocercaIcons: Record<TipoGeocerca, string> = {
|
||||
CLIENTE: '🏢',
|
||||
ALMACEN: '📦',
|
||||
GASOLINERA: '⛽',
|
||||
CASETA: '🚧',
|
||||
PUNTO_CONTROL: '✓',
|
||||
ZONA_RIESGO: '⚠️',
|
||||
ZONA_DESCANSO: '🅿️',
|
||||
FRONTERA: '🚩',
|
||||
ADUANA: '🛃',
|
||||
PUERTO: '⚓',
|
||||
[TipoGeocerca.CIRCULAR]: '⭕',
|
||||
[TipoGeocerca.POLIGONAL]: '🔷',
|
||||
[TipoGeocerca.CLIENTE]: '🏢',
|
||||
[TipoGeocerca.PROVEEDOR]: '🏭',
|
||||
[TipoGeocerca.PATIO]: '📦',
|
||||
[TipoGeocerca.ZONA_RIESGO]: '⚠️',
|
||||
[TipoGeocerca.CASETA]: '🚧',
|
||||
[TipoGeocerca.GASOLINERA]: '⛽',
|
||||
[TipoGeocerca.PUNTO_CONTROL]: '✓',
|
||||
[TipoGeocerca.OTRO]: '📍',
|
||||
};
|
||||
|
||||
export function GeocercasList({ onSelect, onEdit }: GeocercasListProps) {
|
||||
@ -44,8 +45,7 @@ export function GeocercasList({ onSelect, onEdit }: GeocercasListProps) {
|
||||
|
||||
const filters: GeocercaFilters = {
|
||||
tipo: tipoFilter || undefined,
|
||||
activo: activoFilter !== '' ? activoFilter : undefined,
|
||||
search: searchTerm || undefined,
|
||||
activa: activoFilter !== '' ? activoFilter : undefined,
|
||||
limit,
|
||||
offset: (page - 1) * limit,
|
||||
};
|
||||
@ -160,9 +160,9 @@ export function GeocercasList({ onSelect, onEdit }: GeocercasListProps) {
|
||||
>
|
||||
<td className="whitespace-nowrap px-4 py-3">
|
||||
<div className="font-medium text-gray-900">{geocerca.nombre}</div>
|
||||
{geocerca.descripcion && (
|
||||
{geocerca.direccion && (
|
||||
<div className="text-sm text-gray-500 line-clamp-1">
|
||||
{geocerca.descripcion}
|
||||
{geocerca.direccion}
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
@ -176,12 +176,12 @@ export function GeocercasList({ onSelect, onEdit }: GeocercasListProps) {
|
||||
<td className="whitespace-nowrap px-4 py-3">
|
||||
<span
|
||||
className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${
|
||||
geocerca.activo
|
||||
geocerca.activa
|
||||
? 'bg-green-100 text-green-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{geocerca.activo ? 'Activa' : 'Inactiva'}
|
||||
{geocerca.activa ? 'Activa' : 'Inactiva'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="whitespace-nowrap px-4 py-3 text-right text-sm">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { trackingApi } from '../api/tracking.api';
|
||||
import type { PosicionActual, EventoTracking } from '../types';
|
||||
import type { PosicionActual } from '../types';
|
||||
|
||||
interface TrackingMapProps {
|
||||
unidadIds?: string[];
|
||||
@ -38,7 +38,7 @@ export function TrackingMap({
|
||||
const rutaData = ruta?.data || [];
|
||||
|
||||
// Simple map rendering (placeholder - integrate with actual map library)
|
||||
const renderPosition = (pos: PosicionActual, index: number) => {
|
||||
const renderPosition = (pos: PosicionActual, _index: number) => {
|
||||
const isSelected = selectedUnidad === pos.unidadId;
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -11,14 +11,13 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { trackingApi } from '../api/tracking.api';
|
||||
import { ETAProgressBar } from './ETAProgressBar';
|
||||
import { EventTimeline } from './EventTimeline';
|
||||
import { TipoEventoTracking, type EventoTracking } from '../types';
|
||||
import { TipoEventoTracking } from '../types';
|
||||
|
||||
interface ViajeTrackingViewProps {
|
||||
viajeId: string;
|
||||
folio: string;
|
||||
origen: string;
|
||||
destino: string;
|
||||
fechaSalida: string;
|
||||
etaOriginal: string;
|
||||
onClose?: () => void;
|
||||
}
|
||||
@ -40,7 +39,6 @@ export function ViajeTrackingView({
|
||||
folio,
|
||||
origen,
|
||||
destino,
|
||||
fechaSalida,
|
||||
etaOriginal,
|
||||
onClose,
|
||||
}: ViajeTrackingViewProps) {
|
||||
@ -254,7 +252,6 @@ export function ViajeTrackingView({
|
||||
<EventTimeline
|
||||
eventos={eventos}
|
||||
loading={loadingEventos}
|
||||
viajeId={viajeId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -48,7 +48,7 @@ export function useTrackingWebSocket({
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const getWebSocketUrl = useCallback(() => {
|
||||
// Build WebSocket URL with subscription parameters
|
||||
|
||||
@ -2,11 +2,12 @@ import { useState } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { viajesApi } from '../api/viajes.api';
|
||||
import { ViajeStatusBadge } from './ViajeStatusBadge';
|
||||
import type { Viaje, EstadoViaje } from '../types';
|
||||
import type { Viaje } from '../types';
|
||||
|
||||
interface ViajeDetailProps {
|
||||
viaje: Viaje;
|
||||
onClose: () => void;
|
||||
onEdit?: () => void;
|
||||
}
|
||||
|
||||
export function ViajeDetail({ viaje, onClose }: ViajeDetailProps) {
|
||||
|
||||
@ -22,12 +22,13 @@ type FormData = z.infer<typeof viajeSchema>;
|
||||
|
||||
interface ViajeFormProps {
|
||||
viaje?: Viaje;
|
||||
onSubmit: (data: CreateViajeDto | UpdateViajeDto) => Promise<void>;
|
||||
onSubmit?: (data: CreateViajeDto | UpdateViajeDto) => Promise<void>;
|
||||
onSuccess?: () => void;
|
||||
onCancel: () => void;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export function ViajeForm({ viaje, onSubmit, onCancel, isLoading }: ViajeFormProps) {
|
||||
export function ViajeForm({ viaje, onSubmit, onSuccess, onCancel, isLoading }: ViajeFormProps) {
|
||||
const isEditing = !!viaje;
|
||||
|
||||
const {
|
||||
@ -80,7 +81,10 @@ export function ViajeForm({ viaje, onSubmit, onCancel, isLoading }: ViajeFormPro
|
||||
...(data.distanciaEstimadaKm && { distanciaEstimadaKm: data.distanciaEstimadaKm }),
|
||||
...(data.tiempoEstimadoHoras && { tiempoEstimadoHoras: data.tiempoEstimadoHoras }),
|
||||
};
|
||||
if (onSubmit) {
|
||||
await onSubmit(cleanData);
|
||||
}
|
||||
onSuccess?.();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -5,7 +5,8 @@
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
"strict": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user