se modifico pedimentoDetail

This commit is contained in:
2025-10-03 23:17:12 -06:00
parent fce8c9f9dd
commit 4b6abb0cad
2 changed files with 574 additions and 91 deletions

1
.env
View File

@@ -2,3 +2,4 @@ VITE_DEBUG_MODE=true
VITE_EFC_API_URL=http://192.168.1.79:8000/api/v1
VITE_EFC_MICROSERVICE_URL=http://192.168.1.79:8001/api/v1
VITE_EFC_MICROSERVICE_URL_2=http://192.168.1.79:8001/api/v2

View File

@@ -25,6 +25,7 @@ import { useNotification } from '../context/NotificationContext';
const API_URL = import.meta.env.VITE_EFC_API_URL;
const MICROSERVICE_URL = import.meta.env.VITE_EFC_MICROSERVICE_URL;
const MICROSERVICE_URL_2 = import.meta.env.VITE_EFC_MICROSERVICE_URL_2;
// Funciones auxiliares para estados y servicios (copiadas de procesos.js)
const getEstadoLabel = (estado) => {
@@ -196,6 +197,108 @@ export default function PedimentoDetail() {
const [downloadingPartidas, setDownloadingPartidas] = useState(false);
const [downloadingAllPartidas, setDownloadingAllPartidas] = useState(false);
// Estados para credenciales VUCEM
const [credenciales, setCredenciales] = useState([]);
const [processingPartida, setProcessingPartida] = useState(null);
const [processingCove, setProcessingCove] = useState(null);
const [processingAcuseCove, setProcessingAcuseCove] = useState(null);
const [processingEdoc, setProcessingEdoc] = useState(null);
const [processingAcuseEdoc, setProcessingAcuseEdoc] = useState(null);
// Función para obtener credenciales VUCEM
const fetchCredenciales = async (contribuyente) => {
try {
const response = await fetchWithAuth(`${API_URL}/vucem/vucem/?importador=${contribuyente}`);
if (response.ok) {
const data = await response.json();
// La API devuelve un array directamente, no un objeto con results
const credenciales = Array.isArray(data) ? data : (data.results || []);
return credenciales;
}
return [];
} catch (error) {
console.error('Error fetching credenciales:', error);
return [];
}
};
// Función para procesar partida
const handlePartidaProcess = async (partida) => {
setProcessingPartida(partida.id);
try {
// Obtener credenciales para el contribuyente del pedimento
const credencialesList = await fetchCredenciales(pedimento.contribuyente);
if (credencialesList.length === 0) {
showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error');
return;
}
// Usar la primera credencial activa disponible
const credencial = credencialesList.find(c => c.is_active) || credencialesList[0];
const requestBody = {
partida: {
id: partida.id,
numero: partida.numero_partida
},
pedimento: {
id: pedimento.id,
pedimento: pedimento.pedimento,
pedimento_app: pedimento.pedimento_app,
aduana: pedimento.aduana,
patente: pedimento.patente,
organizacion: pedimento.organizacion,
regimen: pedimento.regimen || "test",
clave_pedimento: pedimento.clave_pedimento || "test",
numero_operacion: pedimento.numero_operacion || ""
},
credencial: {
id: credencial.id,
user: credencial.usuario,
password: credencial.password,
efirma: credencial.efirma,
// Convertir URLs completas a rutas relativas
key: credencial.key ? credencial.key.split('/').slice(-2).join('/') : '',
cer: credencial.cer ? credencial.cer.split('/').slice(-2).join('/') : '',
is_active: credencial.is_active,
organizacion: credencial.organizacion
}
};
// Verificar si MICROSERVICE_URL_2 está definido
if (!MICROSERVICE_URL_2) {
throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada');
}
const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/partida/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody)
});
if (response.ok) {
const result = await response.json();
showMessage('Partida procesada correctamente', 'success');
} else {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || 'Error al procesar la partida');
}
} catch (error) {
console.error('Error procesando partida:', error);
showMessage(`Error al procesar la partida: ${error.message}`, 'error');
} finally {
setProcessingPartida(null);
}
};
// Función para obtener partidas
const fetchPedimentoPartidas = async (page = 1, pageSize = 10, filters = {}) => {
console.log('fetchPedimentoPartidas called with:', { page, pageSize, filters });
@@ -1201,6 +1304,300 @@ export default function PedimentoDetail() {
// Aquí implementarías la lógica de petición específica para COVEs
};
// Función para procesar COVE
const handleCoveProcess = async (cove) => {
setProcessingCove(cove.id);
try {
// Obtener credenciales para el contribuyente del pedimento
const credencialesList = await fetchCredenciales(pedimento.contribuyente);
if (credencialesList.length === 0) {
showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error');
return;
}
// Usar la primera credencial activa disponible
const credencial = credencialesList.find(c => c.is_active) || credencialesList[0];
const requestBody = {
cove: {
id: cove.id,
cove: cove.numero_cove
},
pedimento: {
id: pedimento.id,
pedimento: pedimento.pedimento,
pedimento_app: pedimento.pedimento_app,
aduana: pedimento.aduana,
patente: pedimento.patente,
organizacion: pedimento.organizacion,
regimen: pedimento.regimen || "test",
clave_pedimento: pedimento.clave_pedimento || "test",
numero_operacion: pedimento.numero_operacion || "test"
},
credencial: {
id: credencial.id,
user: credencial.usuario,
password: credencial.password,
efirma: credencial.efirma,
key: credencial.key,
cer: credencial.cer,
is_active: credencial.is_active,
organizacion: credencial.organizacion
}
};
// Verificar si MICROSERVICE_URL_2 está definido
if (!MICROSERVICE_URL_2) {
throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada');
}
const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/cove/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody)
});
if (response.ok) {
const result = await response.json();
showMessage('COVE procesado correctamente', 'success');
} else {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || 'Error al procesar el COVE');
}
} catch (error) {
console.error('Error procesando COVE:', error);
showMessage(`Error al procesar el COVE: ${error.message}`, 'error');
} finally {
setProcessingCove(null);
}
};
// Función para procesar Acuse de COVE
const handleAcuseCoveProcess = async (cove) => {
setProcessingAcuseCove(cove.id);
try {
// Obtener credenciales para el contribuyente del pedimento
const credencialesList = await fetchCredenciales(pedimento.contribuyente);
if (credencialesList.length === 0) {
showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error');
return;
}
// Usar la primera credencial activa disponible
const credencial = credencialesList.find(c => c.is_active) || credencialesList[0];
const requestBody = {
cove: {
id: cove.id,
cove: cove.numero_cove
},
pedimento: {
id: pedimento.id,
pedimento: pedimento.pedimento,
pedimento_app: pedimento.pedimento_app,
aduana: pedimento.aduana,
patente: pedimento.patente,
organizacion: pedimento.organizacion,
regimen: pedimento.regimen || "test",
clave_pedimento: pedimento.clave_pedimento || "test",
numero_operacion: pedimento.numero_operacion || "test"
},
credencial: {
id: credencial.id,
user: credencial.usuario,
password: credencial.password,
efirma: credencial.efirma,
key: credencial.key,
cer: credencial.cer,
is_active: credencial.is_active,
organizacion: credencial.organizacion
}
};
// Verificar si MICROSERVICE_URL_2 está definido
if (!MICROSERVICE_URL_2) {
throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada');
}
const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/acuse/cove/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody)
});
if (response.ok) {
const result = await response.json();
showMessage('Acuse de COVE procesado correctamente', 'success');
} else {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || 'Error al procesar el acuse de COVE');
}
} catch (error) {
console.error('Error procesando acuse de COVE:', error);
showMessage(`Error al procesar el acuse de COVE: ${error.message}`, 'error');
} finally {
setProcessingAcuseCove(null);
}
};
// Función para procesar EDoc
const handleEdocProcess = async (edoc) => {
setProcessingEdoc(edoc.id);
try {
// Obtener credenciales para el contribuyente del pedimento
const credencialesList = await fetchCredenciales(pedimento.contribuyente);
if (credencialesList.length === 0) {
showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error');
return;
}
// Usar la primera credencial activa disponible
const credencial = credencialesList.find(c => c.is_active) || credencialesList[0];
const requestBody = {
edoc: {
id: edoc.id,
numero_edocument: edoc.numero_edocument
},
idEDocument: edoc.numero_edocument,
pedimento: {
id: pedimento.id,
pedimento: pedimento.pedimento,
pedimento_app: pedimento.pedimento_app,
aduana: pedimento.aduana,
patente: pedimento.patente,
organizacion: pedimento.organizacion,
regimen: pedimento.regimen || "test",
clave_pedimento: pedimento.clave_pedimento || "test",
numero_operacion: pedimento.numero_operacion || "test"
},
credencial: {
id: credencial.id,
user: credencial.usuario,
password: credencial.password,
efirma: credencial.efirma,
key: credencial.key,
cer: credencial.cer,
is_active: credencial.is_active,
organizacion: credencial.organizacion
}
};
// Verificar si MICROSERVICE_URL_2 está definido
if (!MICROSERVICE_URL_2) {
throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada');
}
const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/download/edoc/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody)
});
if (response.ok) {
const result = await response.json();
showMessage('EDoc procesado correctamente', 'success');
} else {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || 'Error al procesar el EDoc');
}
} catch (error) {
console.error('Error procesando EDoc:', error);
showMessage(`Error al procesar el EDoc: ${error.message}`, 'error');
} finally {
setProcessingEdoc(null);
}
};
// Función para procesar Acuse de EDoc
const handleAcuseEdocProcess = async (edoc) => {
setProcessingAcuseEdoc(edoc.id);
try {
// Obtener credenciales para el contribuyente del pedimento
const credencialesList = await fetchCredenciales(pedimento.contribuyente);
if (credencialesList.length === 0) {
showMessage('No se encontraron credenciales VUCEM para este contribuyente', 'error');
return;
}
// Usar la primera credencial activa disponible
const credencial = credencialesList.find(c => c.is_active) || credencialesList[0];
const requestBody = {
edoc: {
id: edoc.id,
numero_edocument: edoc.numero_edocument
},
idEDocument: edoc.numero_edocument,
pedimento: {
id: pedimento.id,
pedimento: pedimento.pedimento,
pedimento_app: pedimento.pedimento_app,
aduana: pedimento.aduana,
patente: pedimento.patente,
organizacion: pedimento.organizacion,
regimen: pedimento.regimen || "test",
clave_pedimento: pedimento.clave_pedimento || "test",
numero_operacion: pedimento.numero_operacion || "test"
},
credencial: {
id: credencial.id,
user: credencial.usuario,
password: credencial.password,
efirma: credencial.efirma,
key: credencial.key,
cer: credencial.cer,
is_active: credencial.is_active,
organizacion: credencial.organizacion
}
};
// Verificar si MICROSERVICE_URL_2 está definido
if (!MICROSERVICE_URL_2) {
throw new Error('La variable de entorno VITE_EFC_MICROSERVICE_URL_2 no está configurada');
}
const response = await fetchWithAuth(`${MICROSERVICE_URL_2}/services/acuse/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody)
});
if (response.ok) {
const result = await response.json();
showMessage('Acuse de EDoc procesado correctamente', 'success');
} else {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || 'Error al procesar el acuse de EDoc');
}
} catch (error) {
console.error('Error procesando acuse de EDoc:', error);
showMessage(`Error al procesar el acuse de EDoc: ${error.message}`, 'error');
} finally {
setProcessingAcuseEdoc(null);
}
};
const handleEdocRequest = async (edoc) => {
console.log('Request edoc:', edoc);
showMessage(`Procesando petición para EDocs #${edoc.numero_edocument}...`, 'info');
@@ -1231,9 +1628,7 @@ export default function PedimentoDetail() {
};
const handlePartidaRequest = async (partida) => {
console.log('Request partida:', partida);
showMessage(`Procesando petición para partida #${partida.numero_partida}...`, 'info');
// Aquí implementarías la lógica de petición específica para partidas
await handlePartidaProcess(partida);
};
// Funciones de selección para partidas
@@ -2160,17 +2555,28 @@ export default function PedimentoDetail() {
{/* Botón Petición (solo activo si está pendiente) */}
<button
onClick={() => handlePartidaRequest(partida)}
disabled={partida.descargado}
disabled={partida.descargado || processingPartida === partida.id}
className={`p-1 rounded transition-colors ${
partida.descargado
partida.descargado || processingPartida === partida.id
? 'text-gray-400 cursor-not-allowed'
: 'text-purple-600 hover:text-purple-900'
}`}
title={partida.descargado ? 'No disponible - Ya descargado' : 'Procesar petición'}
title={
processingPartida === partida.id ? 'Procesando partida...' :
partida.descargado ? 'No disponible - Ya descargado' :
'Procesar petición'
}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 4H6a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-2m-4-1v8m0 0l3-3m-3 3L9 8m-5 5h2.586a1 1 0 01.707.293l2.414 2.414a1 1 0 00.707.293H20" />
</svg>
{processingPartida === partida.id ? (
<svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
) : (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 4H6a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-2m-4-1v8m0 0l3-3m-3 3L9 8m-5 5h2.586a1 1 0 01.707.293l2.414 2.414a1 1 0 00.707.293H20" />
</svg>
)}
</button>
</div>
</td>
@@ -2409,20 +2815,52 @@ export default function PedimentoDetail() {
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end space-x-2">
{/* Botón Procesar petición (solo activo si está pendiente) */}
{/* Botón COVE */}
<button
onClick={() => handleCoveRequest(cove)}
disabled={cove.cove_descargado && cove.acuse_cove_descargado}
onClick={() => handleCoveProcess(cove)}
disabled={cove.cove_descargado || processingCove === cove.id}
className={`p-1 rounded transition-colors ${
cove.cove_descargado && cove.acuse_cove_descargado
cove.cove_descargado
? 'text-gray-400 cursor-not-allowed'
: 'text-purple-600 hover:text-purple-900'
: processingCove === cove.id
? 'text-blue-400 cursor-not-allowed'
: 'text-blue-600 hover:text-blue-900'
}`}
title={(cove.cove_descargado && cove.acuse_cove_descargado) ? 'No disponible - Ya descargado' : 'Procesar petición'}
title={cove.cove_descargado ? 'COVE ya descargado' : processingCove === cove.id ? 'Procesando COVE...' : 'Procesar COVE'}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 4H6a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-2m-4-1v8m0 0l3-3m-3 3L9 8m-5 5h2.586a1 1 0 01.707.293l2.414 2.414a1 1 0 00.707.293H20" />
</svg>
{processingCove === cove.id ? (
<svg className="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
) : (
<svg className="w-4 h-4" 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 4h8m-10-4a9 9 0 1118 0 9 9 0 01-18 0z" />
</svg>
)}
</button>
{/* Botón Acuse de COVE */}
<button
onClick={() => handleAcuseCoveProcess(cove)}
disabled={cove.acuse_cove_descargado || processingAcuseCove === cove.id}
className={`p-1 rounded transition-colors ${
cove.acuse_cove_descargado
? 'text-gray-400 cursor-not-allowed'
: processingAcuseCove === cove.id
? 'text-green-400 cursor-not-allowed'
: 'text-green-600 hover:text-green-900'
}`}
title={cove.acuse_cove_descargado ? 'Acuse de COVE ya descargado' : processingAcuseCove === cove.id ? 'Procesando Acuse de COVE...' : 'Procesar Acuse de COVE'}
>
{processingAcuseCove === cove.id ? (
<svg className="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
) : (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
)}
</button>
</div>
</td>
@@ -2712,20 +3150,52 @@ export default function PedimentoDetail() {
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div className="flex items-center justify-end space-x-2">
{/* Botón Procesar petición (solo activo si está pendiente) */}
{/* Botón EDoc */}
<button
onClick={() => handleEdocRequest(edoc)}
disabled={edoc.edocument_descargado && edoc.acuse_descargado}
onClick={() => handleEdocProcess(edoc)}
disabled={edoc.edocument_descargado || processingEdoc === edoc.id}
className={`p-1 rounded transition-colors ${
edoc.edocument_descargado && edoc.acuse_descargado
edoc.edocument_descargado
? 'text-gray-400 cursor-not-allowed'
: 'text-purple-600 hover:text-purple-900'
: processingEdoc === edoc.id
? 'text-blue-400 cursor-not-allowed'
: 'text-blue-600 hover:text-blue-900'
}`}
title={(edoc.edocument_descargado && edoc.acuse_descargado) ? 'No disponible - Ya descargado' : 'Procesar petición'}
title={edoc.edocument_descargado ? 'EDoc ya descargado' : processingEdoc === edoc.id ? 'Procesando EDoc...' : 'Procesar EDoc'}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 4H6a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-2m-4-1v8m0 0l3-3m-3 3L9 8m-5 5h2.586a1 1 0 01.707.293l2.414 2.414a1 1 0 00.707.293H20" />
</svg>
{processingEdoc === edoc.id ? (
<svg className="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
) : (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 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>
)}
</button>
{/* Botón Acuse de EDoc */}
<button
onClick={() => handleAcuseEdocProcess(edoc)}
disabled={edoc.acuse_descargado || processingAcuseEdoc === edoc.id}
className={`p-1 rounded transition-colors ${
edoc.acuse_descargado
? 'text-gray-400 cursor-not-allowed'
: processingAcuseEdoc === edoc.id
? 'text-green-400 cursor-not-allowed'
: 'text-green-600 hover:text-green-900'
}`}
title={edoc.acuse_descargado ? 'Acuse de EDoc ya descargado' : processingAcuseEdoc === edoc.id ? 'Procesando Acuse de EDoc...' : 'Procesar Acuse de EDoc'}
>
{processingAcuseEdoc === edoc.id ? (
<svg className="w-4 h-4 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
) : (
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
)}
</button>
</div>
</td>
@@ -2991,70 +3461,82 @@ export default function PedimentoDetail() {
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div className="flex items-center space-x-2">
{/* Botón Ejecutar Servicio - solo para estados Pendiente o Error */}
{(proceso.estado === 1 || proceso.estado === 4) && (
<button
onClick={() => handleEjecutarServicio(proceso)}
disabled={executingId === proceso.id}
className={`inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white transition-colors duration-200 ${
executingId === proceso.id
? 'bg-gray-400 cursor-not-allowed'
: 'bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500'
}`}
>
{executingId === proceso.id ? (
<>
<svg className="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Ejecutando...
</>
) : (
<>
<svg className="-ml-1 mr-2 h-4 w-4" 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 0h1M9 16h1m4 0h1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Ejecutar
</>
)}
</button>
)}
{/* Botón Play (Ejecutar Servicio) */}
<button
onClick={() => handleEjecutarServicio(proceso)}
disabled={
executingId === proceso.id ||
proceso.estado === 2 || // En Proceso
proceso.estado === 3 || // Completado
proceso.estado === 5 // Cancelado
}
className={`inline-flex items-center p-2 border border-transparent rounded-md transition-colors duration-200 ${
executingId === proceso.id ||
proceso.estado === 2 ||
proceso.estado === 3 ||
proceso.estado === 5
? 'bg-gray-200 text-gray-400 cursor-not-allowed'
: 'bg-green-100 text-green-700 hover:bg-green-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500'
}`}
title={
executingId === proceso.id ? 'Ejecutando...' :
proceso.estado === 2 ? 'No disponible - En proceso' :
proceso.estado === 3 ? 'No disponible - Completado' :
proceso.estado === 5 ? 'No disponible - Cancelado' :
'Ejecutar servicio'
}
>
{executingId === proceso.id ? (
<svg className="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
) : (
<svg className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z"/>
</svg>
)}
</button>
{/* Botón Pasar a Espera - solo para estados En Proceso, Completado o Error */}
{(proceso.estado === 2 || proceso.estado === 3 || proceso.estado === 4) && (
<button
onClick={() => handlePasarAEspera(proceso)}
disabled={changingStateId === proceso.id}
className={`inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded-md text-white transition-colors duration-200 ${
changingStateId === proceso.id
? 'bg-gray-400 cursor-not-allowed'
: 'bg-yellow-600 hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500'
}`}
>
{changingStateId === proceso.id ? (
<>
<svg className="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Cambiando...
</>
) : (
<>
<svg className="-ml-1 mr-2 h-4 w-4" 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>
A Espera
</>
)}
</button>
)}
{/* Mostrar texto si no hay acciones disponibles */}
{proceso.estado !== 1 && proceso.estado !== 2 && proceso.estado !== 3 && proceso.estado !== 4 && (
<span className="text-xs text-gray-400 italic">Sin acciones</span>
)}
{/* Botón Refresh (Pasar a Espera) */}
<button
onClick={() => handlePasarAEspera(proceso)}
disabled={
changingStateId === proceso.id ||
proceso.estado === 1 || // Pendiente
proceso.estado === 2 || // En Proceso
proceso.estado === 3 || // Completado
proceso.estado === 5 // Cancelado
}
className={`inline-flex items-center p-2 border border-transparent rounded-md transition-colors duration-200 ${
changingStateId === proceso.id ||
proceso.estado === 1 ||
proceso.estado === 2 ||
proceso.estado === 3 ||
proceso.estado === 5
? 'bg-gray-200 text-gray-400 cursor-not-allowed'
: 'bg-yellow-100 text-yellow-700 hover:bg-yellow-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500'
}`}
title={
changingStateId === proceso.id ? 'Cambiando estado...' :
proceso.estado === 1 ? 'No disponible - Use Play para ejecutar' :
proceso.estado === 2 ? 'No disponible - En proceso' :
proceso.estado === 3 ? 'No disponible - Completado' :
proceso.estado === 5 ? 'No disponible - Cancelado' :
'Pasar a espera'
}
>
{changingStateId === proceso.id ? (
<svg className="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
) : (
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
)}
</button>
</div>
</td>
</tr>