diff --git a/src/pages/Procesos.jsx b/src/pages/Procesos.jsx index 7dcd02a..7268130 100644 --- a/src/pages/Procesos.jsx +++ b/src/pages/Procesos.jsx @@ -40,7 +40,7 @@ export default function Procesos() { const [isSelectAll, setIsSelectAll] = useState(false); // Función para mostrar toast - const showToast = (type, title, message, details = '') => { + const showToast = (type, title, message, details = '', persistent = false, progress = null) => { const id = Date.now(); const newToast = { id, @@ -48,16 +48,29 @@ export default function Procesos() { title, message, details, - isVisible: true + isVisible: true, + persistent, + progress }; setToasts(prev => [...prev, newToast]); - // Auto remover el toast después de 5 segundos (8 segundos para info) - const timeout = type === 'info' ? 8000 : 5000; - setTimeout(() => { - removeToast(id); - }, timeout); + // Auto remover el toast después de 5 segundos (8 segundos para info) solo si no es persistente + if (!persistent) { + const timeout = type === 'info' ? 8000 : 5000; + setTimeout(() => { + removeToast(id); + }, timeout); + } + + return id; // Retornar el ID para poder actualizar el toast + }; + + // Función para actualizar toast existente (especialmente útil para progreso) + const updateToast = (id, updates) => { + setToasts(prev => prev.map(toast => + toast.id === id ? { ...toast, ...updates } : toast + )); }; // Función para remover toast @@ -102,12 +115,43 @@ export default function Procesos() { return; } - showToast('info', 'Iniciando procesamiento masivo', `Procesando ${procesosEjecutables.length} procesos de la página actual...`); + // Calcular tiempo estimado (aproximadamente 2-3 segundos por proceso) + const tiempoEstimadoSegundos = procesosEjecutables.length * 2.5; + const minutos = Math.floor(tiempoEstimadoSegundos / 60); + const segundos = Math.round(tiempoEstimadoSegundos % 60); + const tiempoTexto = minutos > 0 ? `${minutos}m ${segundos}s` : `${segundos}s`; + // Crear toast persistente con progreso + const progressToastId = showToast('info', 'Procesamiento Masivo en Progreso', + `Procesando ${procesosEjecutables.length} procesos...\n⏱️ Tiempo estimado: ${tiempoTexto}`, + '', + true, // persistente + { current: 0, total: procesosEjecutables.length, percentage: 0 } + ); + + const inicioTiempo = Date.now(); let exitosos = 0; let errores = 0; - for (const proc of procesosEjecutables) { + for (let i = 0; i < procesosEjecutables.length; i++) { + const proc = procesosEjecutables[i]; + + // Actualizar progreso en tiempo real + const tiempoTranscurrido = Math.round((Date.now() - inicioTiempo) / 1000); + const progresoPercentage = Math.round(((i + 1) / procesosEjecutables.length) * 100); + const tiempoRestanteEstimado = i > 0 ? Math.round((tiempoTranscurrido / i) * (procesosEjecutables.length - i)) : tiempoEstimadoSegundos; + + updateToast(progressToastId, { + message: `Procesando ${procesosEjecutables.length} procesos...\n⏱️ Transcurrido: ${tiempoTranscurrido}s | Restante: ~${tiempoRestanteEstimado}s\n📊 Proceso actual: ${proc.pedimento?.numero || proc.pedimento} (${i + 1}/${procesosEjecutables.length})`, + progress: { + current: i + 1, + total: procesosEjecutables.length, + percentage: progresoPercentage, + exitosos, + errores + } + }); + try { // Cambiar estado visual a "Procesando" updateProcesoEstado(proc.id, 2); @@ -151,8 +195,25 @@ export default function Procesos() { } } - showToast('success', 'Procesamiento masivo completado', - `Resultados: ${exitosos} exitosos, ${errores} errores de ${procesosEjecutables.length} procesos.`); + const tiempoTotalTranscurrido = Math.round((Date.now() - inicioTiempo) / 1000); + const minutosTotales = Math.floor(tiempoTotalTranscurrido / 60); + const segundosTotales = tiempoTotalTranscurrido % 60; + const tiempoTotalTexto = minutosTotales > 0 ? `${minutosTotales}m ${segundosTotales}s` : `${segundosTotales}s`; + + // Actualizar toast final + updateToast(progressToastId, { + type: 'success', + title: '✅ Procesamiento Completado', + message: `Resultados finales:\n✓ ${exitosos} exitosos | ✗ ${errores} errores\n⏱️ Tiempo total: ${tiempoTotalTexto}`, + progress: { + current: procesosEjecutables.length, + total: procesosEjecutables.length, + percentage: 100, + exitosos, + errores, + completed: true + } + }); }; // Función para procesar seleccionados @@ -171,12 +232,43 @@ export default function Procesos() { return; } - showToast('info', 'Iniciando procesamiento de seleccionados', `Procesando ${procesosAEjecutar.length} procesos seleccionados...`); + // Calcular tiempo estimado (aproximadamente 2-3 segundos por proceso) + const tiempoEstimadoSegundos = procesosAEjecutar.length * 2.5; + const minutos = Math.floor(tiempoEstimadoSegundos / 60); + const segundos = Math.round(tiempoEstimadoSegundos % 60); + const tiempoTexto = minutos > 0 ? `${minutos}m ${segundos}s` : `${segundos}s`; + // Crear toast persistente con progreso + const progressToastId = showToast('info', 'Procesamiento de Seleccionados en Progreso', + `Procesando ${procesosAEjecutar.length} procesos seleccionados...\n⏱️ Tiempo estimado: ${tiempoTexto}`, + '', + true, // persistente + { current: 0, total: procesosAEjecutar.length, percentage: 0 } + ); + + const inicioTiempo = Date.now(); let exitosos = 0; let errores = 0; - for (const proc of procesosAEjecutar) { + for (let i = 0; i < procesosAEjecutar.length; i++) { + const proc = procesosAEjecutar[i]; + + // Actualizar progreso en tiempo real + const tiempoTranscurrido = Math.round((Date.now() - inicioTiempo) / 1000); + const progresoPercentage = Math.round(((i + 1) / procesosAEjecutar.length) * 100); + const tiempoRestanteEstimado = i > 0 ? Math.round((tiempoTranscurrido / i) * (procesosAEjecutar.length - i)) : tiempoEstimadoSegundos; + + updateToast(progressToastId, { + message: `Procesando ${procesosAEjecutar.length} procesos seleccionados...\n⏱️ Transcurrido: ${tiempoTranscurrido}s | Restante: ~${tiempoRestanteEstimado}s\n📊 Proceso actual: ${proc.pedimento?.numero || proc.pedimento} (${i + 1}/${procesosAEjecutar.length})`, + progress: { + current: i + 1, + total: procesosAEjecutar.length, + percentage: progresoPercentage, + exitosos, + errores + } + }); + try { // Cambiar estado visual a "Procesando" updateProcesoEstado(proc.id, 2); @@ -224,8 +316,25 @@ export default function Procesos() { setSelectedProcesos([]); setIsSelectAll(false); - showToast('success', 'Procesamiento de seleccionados completado', - `Resultados: ${exitosos} exitosos, ${errores} errores de ${procesosAEjecutar.length} procesos.`); + const tiempoTotalTranscurrido = Math.round((Date.now() - inicioTiempo) / 1000); + const minutosTotales = Math.floor(tiempoTotalTranscurrido / 60); + const segundosTotales = tiempoTotalTranscurrido % 60; + const tiempoTotalTexto = minutosTotales > 0 ? `${minutosTotales}m ${segundosTotales}s` : `${segundosTotales}s`; + + // Actualizar toast final + updateToast(progressToastId, { + type: 'success', + title: '✅ Procesamiento de Seleccionados Completado', + message: `Resultados finales:\n✓ ${exitosos} exitosos | ✗ ${errores} errores\n⏱️ Tiempo total: ${tiempoTotalTexto}`, + progress: { + current: procesosAEjecutar.length, + total: procesosAEjecutar.length, + percentage: 100, + exitosos, + errores, + completed: true + } + }); }; // Función para pasar página entera a En Espera @@ -237,12 +346,43 @@ export default function Procesos() { return; } - showToast('info', 'Iniciando cambio masivo de estado', `Pasando ${procesosEnError.length} procesos a "En Espera"...`); + // Calcular tiempo estimado (aproximadamente 1 segundo por proceso) + const tiempoEstimadoSegundos = procesosEnError.length * 1; + const minutos = Math.floor(tiempoEstimadoSegundos / 60); + const segundos = Math.round(tiempoEstimadoSegundos % 60); + const tiempoTexto = minutos > 0 ? `${minutos}m ${segundos}s` : `${segundos}s`; + // Crear toast persistente con progreso + const progressToastId = showToast('info', 'Cambio Masivo de Estado en Progreso', + `Pasando ${procesosEnError.length} procesos a "En Espera"...\n⏱️ Tiempo estimado: ${tiempoTexto}`, + '', + true, // persistente + { current: 0, total: procesosEnError.length, percentage: 0 } + ); + + const inicioTiempo = Date.now(); let exitosos = 0; let errores = 0; - for (const proc of procesosEnError) { + for (let i = 0; i < procesosEnError.length; i++) { + const proc = procesosEnError[i]; + + // Actualizar progreso en tiempo real + const tiempoTranscurrido = Math.round((Date.now() - inicioTiempo) / 1000); + const progresoPercentage = Math.round(((i + 1) / procesosEnError.length) * 100); + const tiempoRestanteEstimado = i > 0 ? Math.round((tiempoTranscurrido / i) * (procesosEnError.length - i)) : tiempoEstimadoSegundos; + + updateToast(progressToastId, { + message: `Pasando ${procesosEnError.length} procesos a "En Espera"...\n⏱️ Transcurrido: ${tiempoTranscurrido}s | Restante: ~${tiempoRestanteEstimado}s\n📊 Proceso actual: ${proc.pedimento?.numero || proc.pedimento} (${i + 1}/${procesosEnError.length})`, + progress: { + current: i + 1, + total: procesosEnError.length, + percentage: progresoPercentage, + exitosos, + errores + } + }); + try { // Cambiar estado visual a "Procesando" temporalmente updateProcesoEstado(proc.id, 2); @@ -275,8 +415,25 @@ export default function Procesos() { } } - showToast('success', 'Cambio masivo de estado completado', - `Resultados: ${exitosos} exitosos, ${errores} errores de ${procesosEnError.length} procesos.`); + const tiempoTotalTranscurrido = Math.round((Date.now() - inicioTiempo) / 1000); + const minutosTotales = Math.floor(tiempoTotalTranscurrido / 60); + const segundosTotales = tiempoTotalTranscurrido % 60; + const tiempoTotalTexto = minutosTotales > 0 ? `${minutosTotales}m ${segundosTotales}s` : `${segundosTotales}s`; + + // Actualizar toast final + updateToast(progressToastId, { + type: 'success', + title: '✅ Cambio Masivo de Estado Completado', + message: `Resultados finales:\n✓ ${exitosos} exitosos | ✗ ${errores} errores\n⏱️ Tiempo total: ${tiempoTotalTexto}`, + progress: { + current: procesosEnError.length, + total: procesosEnError.length, + percentage: 100, + exitosos, + errores, + completed: true + } + }); }; // Función para pasar seleccionados a En Espera @@ -295,12 +452,43 @@ export default function Procesos() { return; } - showToast('info', 'Iniciando cambio de estado de seleccionados', `Pasando ${procesosACambiar.length} procesos seleccionados a "En Espera"...`); + // Calcular tiempo estimado (aproximadamente 1 segundo por proceso) + const tiempoEstimadoSegundos = procesosACambiar.length * 1; + const minutos = Math.floor(tiempoEstimadoSegundos / 60); + const segundos = Math.round(tiempoEstimadoSegundos % 60); + const tiempoTexto = minutos > 0 ? `${minutos}m ${segundos}s` : `${segundos}s`; + // Crear toast persistente con progreso + const progressToastId = showToast('info', 'Cambio de Estado de Seleccionados en Progreso', + `Pasando ${procesosACambiar.length} procesos seleccionados a "En Espera"...\n⏱️ Tiempo estimado: ${tiempoTexto}`, + '', + true, // persistente + { current: 0, total: procesosACambiar.length, percentage: 0 } + ); + + const inicioTiempo = Date.now(); let exitosos = 0; let errores = 0; - for (const proc of procesosACambiar) { + for (let i = 0; i < procesosACambiar.length; i++) { + const proc = procesosACambiar[i]; + + // Actualizar progreso en tiempo real + const tiempoTranscurrido = Math.round((Date.now() - inicioTiempo) / 1000); + const progresoPercentage = Math.round(((i + 1) / procesosACambiar.length) * 100); + const tiempoRestanteEstimado = i > 0 ? Math.round((tiempoTranscurrido / i) * (procesosACambiar.length - i)) : tiempoEstimadoSegundos; + + updateToast(progressToastId, { + message: `Pasando ${procesosACambiar.length} procesos seleccionados a "En Espera"...\n⏱️ Transcurrido: ${tiempoTranscurrido}s | Restante: ~${tiempoRestanteEstimado}s\n📊 Proceso actual: ${proc.pedimento?.numero || proc.pedimento} (${i + 1}/${procesosACambiar.length})`, + progress: { + current: i + 1, + total: procesosACambiar.length, + percentage: progresoPercentage, + exitosos, + errores + } + }); + try { // Cambiar estado visual a "Procesando" temporalmente updateProcesoEstado(proc.id, 2); @@ -337,8 +525,25 @@ export default function Procesos() { setSelectedProcesos([]); setIsSelectAll(false); - showToast('success', 'Cambio de estado de seleccionados completado', - `Resultados: ${exitosos} exitosos, ${errores} errores de ${procesosACambiar.length} procesos.`); + const tiempoTotalTranscurrido = Math.round((Date.now() - inicioTiempo) / 1000); + const minutosTotales = Math.floor(tiempoTotalTranscurrido / 60); + const segundosTotales = tiempoTotalTranscurrido % 60; + const tiempoTotalTexto = minutosTotales > 0 ? `${minutosTotales}m ${segundosTotales}s` : `${segundosTotales}s`; + + // Actualizar toast final + updateToast(progressToastId, { + type: 'success', + title: '✅ Cambio de Estado de Seleccionados Completado', + message: `Resultados finales:\n✓ ${exitosos} exitosos | ✗ ${errores} errores\n⏱️ Tiempo total: ${tiempoTotalTexto}`, + progress: { + current: procesosACambiar.length, + total: procesosACambiar.length, + percentage: 100, + exitosos, + errores, + completed: true + } + }); }; // Función para cambiar estado de Error a En Espera @@ -1302,7 +1507,7 @@ export default function Procesos() {

{toast.title}

-

{toast.message}

+

{toast.message}

{toast.details && (
@@ -1323,49 +1528,108 @@ export default function Procesos() {
)} + {/* Barra de progreso tipo tqdm */} + {toast.progress && ( +
+
+ + {toast.progress.current}/{toast.progress.total} + ({toast.progress.percentage}%) + + {toast.progress.exitosos !== undefined && toast.progress.errores !== undefined && ( + + ✓{toast.progress.exitosos} + {toast.progress.errores > 0 && ( + ✗{toast.progress.errores} + )} + + )} +
+
+
+ {/* Barra de progreso animada */} + {!toast.progress.completed && ( +
+ )} +
+
+ {/* Indicador de velocidad/rate (similar a tqdm) */} + {toast.progress.current > 0 && !toast.progress.completed && ( +
+ 📈 {((toast.progress.current / (Date.now() - (toast.id || Date.now()))) * 1000 * 60).toFixed(1)} items/min +
+ )} +
+ )} - + {/* Botón de cerrar - solo visible si es persistente o completado */} + {(toast.persistent || (toast.progress && toast.progress.completed)) && ( + + )} + {/* Indicador de toast automático */} + {!toast.persistent && (!toast.progress || !toast.progress.completed) && ( +
+ + + +
+ )} - {/* Barra de progreso */} -
-
-
+ {/* Barra de progreso de tiempo (solo para toasts automáticos sin progreso personalizado) */} + {!toast.persistent && !toast.progress && ( +
+
+
+ )}
))}