Se soluciono authenticacion

This commit is contained in:
2025-08-05 15:09:35 -06:00
parent c9df4e3ab2
commit 42cafd54b9
6 changed files with 255 additions and 137 deletions

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { fetchProcesamientoPedimentos } from '../api/procesos.ts';
import { postWithAuth } from '../fetchWithAuth';
import { postWithAuth, putWithAuth } from '../fetchWithAuth';
const API_URL = import.meta.env.VITE_EFC_API_URL;
const MICROSERVICE_URL = import.meta.env.VITE_EFC_MICROSERVICE_URL;
@@ -29,10 +29,73 @@ export default function Procesos() {
// Estado para loading de ejecución de servicio
const [executingId, setExecutingId] = useState(null);
// Estado para loading de cambio de estado
const [changingStateId, setChangingStateId] = useState(null);
// Dropdown state: id del proceso abierto o null
const [openDropdownId, setOpenDropdownId] = useState(null);
// Función para cambiar estado de Error a En Espera
const handlePasarAEspera = async (proc) => {
setChangingStateId(proc.id);
try {
const body = {
estado: 1, // Cambiar a En Espera
tipo_procesamiento: 2,
pedimento: typeof proc.pedimento === 'object' && proc.pedimento !== null ? proc.pedimento.id : proc.pedimento,
servicio: proc.servicio,
organizacion: proc.organizacion_id || proc.organizacion || proc.organizacionId
};
const res = await putWithAuth(`${API_URL}/customs/procesamientopedimentos/${proc.id}/`, body);
if (!res.ok) {
// Intentar obtener más detalles del error
let errorText = 'Error desconocido';
try {
errorText = await res.text();
} catch (textErr) {
// Error al leer respuesta
}
throw new Error(`Error al cambiar el estado del proceso: ${errorText}`);
}
alert('Estado cambiado a "En Espera" correctamente');
setOpenDropdownId(null);
// Refrescar la lista de procesos
window.location.reload();
} catch (err) {
// Crear un mensaje de error más detallado y persistente
const errorDetails = {
message: err.message,
status: err.status || 'N/A',
stack: err.stack,
timestamp: new Date().toISOString()
};
// Alert más detallado que permanece visible
const detailedMessage = `
🚨 ERROR DETALLADO:
⏰ Tiempo: ${new Date().toLocaleString()}
📝 Mensaje: ${err.message}
🔢 Status: ${err.status || 'N/A'}
🔍 Tipo: ${err.name || 'Error'}
📋 Copia este mensaje y compártelo para debugging.
`.trim();
if (err.message === 'SESSION_EXPIRED') {
alert('🚪 SESIÓN EXPIRADA\n\nTu sesión ha expirado. Por favor, inicia sesión nuevamente.\n\n' + detailedMessage);
} else {
alert(detailedMessage);
}
} finally {
setChangingStateId(null);
}
};
// Función para ejecutar el servicio según el tipo de proceso
const handleEjecutarServicio = async (proc) => {
@@ -77,7 +140,6 @@ export default function Procesos() {
alert('Servicio ejecutado correctamente');
setOpenDropdownId(null);
} catch (err) {
console.error('Error executing service:', err);
if (err.message === 'SESSION_EXPIRED') {
alert('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.');
} else {
@@ -91,18 +153,25 @@ export default function Procesos() {
// Cierra el dropdown si se hace click fuera
useEffect(() => {
if (openDropdownId === null) return;
function handleClickOutside(e) {
const el = document.getElementById(`dropdown-acciones-${openDropdownId}`);
if (el && !el.contains(e.target)) {
// Buscar el dropdown específico que está abierto
const dropdownContainer = document.getElementById(`dropdown-acciones-${openDropdownId}`);
// Si el click fue fuera del dropdown, cerrarlo
if (dropdownContainer && !dropdownContainer.contains(e.target)) {
setOpenDropdownId(null);
}
}
// Usar setTimeout para evitar que el click que abre el dropdown lo cierre inmediatamente
setTimeout(() => {
document.addEventListener('click', handleClickOutside);
// Agregar el listener con un pequeño delay para evitar que se cierre inmediatamente
const timeoutId = setTimeout(() => {
document.addEventListener('click', handleClickOutside, true);
}, 100);
return () => {
document.removeEventListener('click', handleClickOutside);
clearTimeout(timeoutId);
document.removeEventListener('click', handleClickOutside, true);
};
}, [openDropdownId]);
@@ -120,15 +189,11 @@ export default function Procesos() {
filters['ordering'] = (sortOrder === 'desc' ? '-' : '') + sortField;
}
console.log('Fetching procesos with filters:', filters);
const data = await fetchProcesamientoPedimentos(page, itemsPerPage, filters);
console.log('Data received:', data);
setProcesos(data.results || []);
setCount(data.count || 0);
} catch (err) {
console.error('Error fetching procesos:', err);
if (err.message === 'SESSION_EXPIRED') {
setError('Tu sesión ha expirado. Por favor, inicia sesión nuevamente.');
} else {
@@ -306,9 +371,10 @@ export default function Procesos() {
) : (
<>
{/* Vista de tabla para pantallas grandes */}
<div className="hidden lg:block overflow-x-auto bg-white rounded-2xl border border-gray-200 shadow-sm">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gradient-to-r from-gray-50 to-slate-50">
<div className="hidden lg:block overflow-x-auto bg-white rounded-2xl border border-gray-200 shadow-sm relative">
<table className="min-w-full divide-y divide-gray-200 relative"
style={{ position: 'relative', zIndex: 1 }}>
<thead className="bg-gradient-to-r from-gray-50 to-slate-50 sticky top-0 z-10">
<tr>
<th className="px-4 py-4 text-center text-xs font-bold text-gray-600 uppercase tracking-wider cursor-pointer select-none hover:bg-gray-100 transition-colors duration-200 rounded-tl-2xl"
onClick={() => {
@@ -365,7 +431,7 @@ export default function Procesos() {
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-100">
<tbody className="bg-white divide-y divide-gray-100 relative" style={{ position: 'relative' }}>
{procesos.length === 0 ? (
<tr>
<td colSpan={6} className="text-center py-12">
@@ -419,7 +485,7 @@ export default function Procesos() {
: String(proc.servicio)}
</td>
<td className="px-4 py-4 text-center align-middle whitespace-nowrap">
<div className="relative inline-block text-left z-30" id={`dropdown-acciones-${proc.id}`}>
<div className="relative inline-block text-left" id={`dropdown-acciones-${proc.id}`}>
<button
className="inline-flex justify-center items-center rounded-xl border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200 transform hover:scale-105 active:bg-gray-100"
type="button"
@@ -431,53 +497,60 @@ export default function Procesos() {
</svg>
</button>
{openDropdownId === proc.id && (
<div className="absolute right-0 mt-2 w-48 rounded-xl shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-[9999] border border-gray-200">
<div className="py-2">
<button
className="flex items-center w-full text-left px-4 py-3 text-sm text-blue-700 hover:bg-blue-50 disabled:opacity-60 disabled:cursor-not-allowed transition-colors duration-200"
onClick={() => {
handleEjecutarServicio(proc);
setOpenDropdownId(null); // Cerrar dropdown después de ejecutar
}}
disabled={
executingId === proc.id ||
proc.estado === 2 || // Procesando
proc.estado === 3 || // Finalizado
proc.estado === 4 // Error
}
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h8m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
{executingId === proc.id ? 'Ejecutando...' : 'Ejecutar Servicio'}
</button>
<button
className={`flex items-center w-full text-left px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200 ${(proc.estado === 2 || proc.estado === 4) ? '' : ' opacity-50 cursor-not-allowed'}`}
disabled={!(proc.estado === 2 || proc.estado === 4)}
onClick={() => {
setOpenDropdownId(null); // Cerrar dropdown
// Aquí iría la lógica para pasar a espera
}}
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Pasar a espera
</button>
<button
className="flex items-center w-full text-left px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200"
onClick={() => {
setOpenDropdownId(null); // Cerrar dropdown
// Aquí iría la lógica para editar
}}
>
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
Editar
</button>
<>
{/* Overlay invisible para cerrar dropdown */}
<div
className="fixed inset-0 z-[100]"
onClick={() => setOpenDropdownId(null)}
/>
{/* Dropdown menu */}
<div className="absolute right-0 mt-2 w-56 rounded-xl shadow-2xl bg-white ring-1 ring-black ring-opacity-5 z-[200] border border-gray-200 overflow-hidden"
style={{
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
transform: 'translateZ(0)' // Force hardware acceleration
}}>
<div className="py-1" role="menu">
<button
className="flex items-center w-full text-left px-4 py-3 text-sm text-blue-700 hover:bg-blue-50 disabled:opacity-60 disabled:cursor-not-allowed transition-colors duration-200 border-b border-gray-100"
onClick={(e) => {
e.stopPropagation();
handleEjecutarServicio(proc);
setOpenDropdownId(null);
}}
disabled={
executingId === proc.id ||
proc.estado === 2 || // Procesando
proc.estado === 3 || // Finalizado
proc.estado === 4 // Error
}
role="menuitem"
>
<svg className="w-4 h-4 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h8m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<span className="font-medium">
{executingId === proc.id ? 'Ejecutando...' : 'Ejecutar Servicio'}
</span>
</button>
<button
className={`flex items-center w-full text-left px-4 py-3 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200 ${proc.estado === 4 ? '' : ' opacity-50 cursor-not-allowed'}`}
disabled={proc.estado !== 4 || changingStateId === proc.id}
onClick={(e) => {
e.stopPropagation();
handlePasarAEspera(proc);
}}
role="menuitem"
>
<svg className="w-4 h-4 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="font-medium">
{changingStateId === proc.id ? 'Cambiando...' : 'Pasar a espera'}
</span>
</button>
</div>
</div>
</div>
</>
)}
</div>
</td>
@@ -591,29 +664,18 @@ export default function Procesos() {
</span>
</button>
<button
className={`flex items-center w-full text-left px-4 py-4 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200 border-b border-gray-100 ${(proc.estado === 2 || proc.estado === 4) ? '' : ' opacity-50 cursor-not-allowed'}`}
disabled={!(proc.estado === 2 || proc.estado === 4)}
className={`flex items-center w-full text-left px-4 py-4 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200 ${proc.estado === 4 ? '' : ' opacity-50 cursor-not-allowed'}`}
disabled={proc.estado !== 4 || changingStateId === proc.id}
onClick={() => {
setOpenDropdownId(null); // Cerrar dropdown
// Aquí iría la lógica para pasar a espera
handlePasarAEspera(proc);
}}
>
<svg className="w-4 h-4 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="font-medium">Pasar a espera</span>
</button>
<button
className="flex items-center w-full text-left px-4 py-4 text-sm text-gray-700 hover:bg-gray-50 transition-colors duration-200"
onClick={() => {
setOpenDropdownId(null); // Cerrar dropdown
// Aquí iría la lógica para editar
}}
>
<svg className="w-4 h-4 mr-3 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
<span className="font-medium">Editar</span>
<span className="font-medium">
{changingStateId === proc.id ? 'Cambiando...' : 'Pasar a espera'}
</span>
</button>
</div>
</div>