cambios en datastage
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
|
// Consultar el estado de un task por task_id
|
||||||
|
export async function fetchTaskStatus(task_id) {
|
||||||
|
const url = `${import.meta.env.VITE_EFC_API_URL}/datastage/datastages/task-status/?task_id=${encodeURIComponent(task_id)}`;
|
||||||
|
const res = await getWithAuth(url);
|
||||||
|
if (!res.ok) throw new Error('Error al consultar el estado del task');
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
import.meta.env;
|
import.meta.env;
|
||||||
import { getWithAuth, postWithAuth, patchWithAuth, deleteWithAuth } from '../fetchWithAuth';
|
import { getWithAuth, postWithAuth, patchWithAuth, deleteWithAuth } from '../fetchWithAuth';
|
||||||
|
|||||||
@@ -1,17 +1,124 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { fetchTaskStatus } from '../api/datastage.js';
|
||||||
|
|
||||||
export default function SuccessModal({ open, onClose, message = 'Descarga exitosa' }) {
|
export default function SuccessModal({ open, onClose, message = 'Descarga exitosa' }) {
|
||||||
if (!open) return null;
|
if (!open) return null;
|
||||||
|
|
||||||
|
// Detectar si es mensaje de procesamiento iniciado
|
||||||
|
let isProcessing = false;
|
||||||
|
let taskId = '';
|
||||||
|
if (typeof message === 'string' && message.includes('Procesamiento iniciado.') && message.includes('Task ID:')) {
|
||||||
|
isProcessing = true;
|
||||||
|
// Extraer taskId
|
||||||
|
const match = message.match(/Task ID: ([\w-]+)/);
|
||||||
|
if (match) taskId = match[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estado para mostrar status del task
|
||||||
|
const [taskStatus, setTaskStatus] = useState(null);
|
||||||
|
const [loadingStatus, setLoadingStatus] = useState(false);
|
||||||
|
const [errorStatus, setErrorStatus] = useState('');
|
||||||
|
// Estado para subtasks
|
||||||
|
const [subtaskStatus, setSubtaskStatus] = useState({}); // { subtaskId: { loading, error, data } }
|
||||||
|
|
||||||
|
const handleCheckStatus = async () => {
|
||||||
|
setLoadingStatus(true);
|
||||||
|
setErrorStatus('');
|
||||||
|
try {
|
||||||
|
const res = await fetchTaskStatus(taskId);
|
||||||
|
setTaskStatus(res);
|
||||||
|
} catch (e) {
|
||||||
|
setErrorStatus('Error al consultar el estado');
|
||||||
|
}
|
||||||
|
setLoadingStatus(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubtaskClick = async (subId) => {
|
||||||
|
setSubtaskStatus(prev => ({ ...prev, [subId]: { loading: true, error: '', data: null } }));
|
||||||
|
try {
|
||||||
|
const res = await fetchTaskStatus(subId);
|
||||||
|
setSubtaskStatus(prev => ({ ...prev, [subId]: { loading: false, error: '', data: res } }));
|
||||||
|
} catch (e) {
|
||||||
|
setSubtaskStatus(prev => ({ ...prev, [subId]: { loading: false, error: 'Error al consultar el estado', data: null } }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-40">
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-40">
|
||||||
<div className="bg-white rounded-xl shadow-2xl border border-green-200 p-8 max-w-sm w-full flex flex-col items-center animate-fade-in">
|
<div className={`bg-white rounded-xl shadow-2xl border ${isProcessing ? 'border-blue-300' : 'border-green-200'} p-8 max-w-md w-full flex flex-col items-center animate-fade-in`}>
|
||||||
|
{isProcessing ? (
|
||||||
|
<>
|
||||||
|
{/* Header decorativo */}
|
||||||
|
<div className="w-full flex items-center gap-3 mb-4 bg-gradient-to-r from-blue-600 to-blue-800 rounded-t-xl p-4 relative">
|
||||||
|
<div className="flex-shrink-0 bg-white/20 backdrop-blur-sm rounded-full p-2 shadow-lg">
|
||||||
|
<svg className="h-7 w-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<ellipse cx="12" cy="7" rx="8" ry="3" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M4 7v10c0 1.657 3.582 3 8 3s8-1.343 8-3V7" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
<path d="M4 17c0 1.657 3.582 3 8 3s8-1.343 8-3" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h2 className="text-lg sm:text-xl font-extrabold text-white tracking-tight">Procesamiento iniciado</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center mb-2 mt-2 w-full">
|
||||||
|
<div
|
||||||
|
className={`bg-blue-50 border border-blue-200 rounded-lg px-4 py-2 w-full text-center mb-2 cursor-pointer transition hover:bg-blue-100 ${loadingStatus ? 'opacity-60 pointer-events-none' : ''}`}
|
||||||
|
title="Consultar estado"
|
||||||
|
onClick={handleCheckStatus}
|
||||||
|
>
|
||||||
|
<span className="block text-xs text-blue-800 font-semibold tracking-wide">Task ID:</span>
|
||||||
|
<span className="block text-sm font-mono text-blue-900 break-all underline hover:text-blue-700">{taskId}</span>
|
||||||
|
{loadingStatus && <div className="text-xs text-blue-700 mt-1">Consultando estado...</div>}
|
||||||
|
{errorStatus && <div className="text-red-600 text-xs mt-1">{errorStatus}</div>}
|
||||||
|
{taskStatus && (
|
||||||
|
<>
|
||||||
|
<div className="mt-2 text-xs text-blue-800 font-semibold">Estado: <span className="font-mono text-blue-900">{taskStatus.status}</span></div>
|
||||||
|
{taskStatus.result && (
|
||||||
|
<div className="text-xs text-blue-800 mt-2 text-left">
|
||||||
|
<div className="mb-1"><span className="font-semibold">Group ID:</span> <span className="font-mono text-blue-900 break-all">{taskStatus.result.group_id}</span></div>
|
||||||
|
<div className="mb-1"><span className="font-semibold">Subtask IDs:</span>
|
||||||
|
<ul className="list-disc list-inside ml-4 max-h-32 overflow-y-auto">
|
||||||
|
{Array.isArray(taskStatus.result.subtask_ids) && taskStatus.result.subtask_ids.map(id => (
|
||||||
|
<li key={id} className="font-mono text-blue-900 break-all text-xs cursor-pointer underline hover:text-blue-700"
|
||||||
|
title="Consultar estado de subtask"
|
||||||
|
onClick={() => handleSubtaskClick(id)}>
|
||||||
|
{id}
|
||||||
|
{subtaskStatus[id]?.loading && <span className="ml-2 text-blue-600">Consultando...</span>}
|
||||||
|
{subtaskStatus[id]?.error && <div className="text-red-600 text-xs mt-1">{subtaskStatus[id].error}</div>}
|
||||||
|
{subtaskStatus[id]?.data && (
|
||||||
|
<div className="mt-1 text-xs text-blue-800 bg-blue-50 border border-blue-200 rounded p-2 text-left">
|
||||||
|
<div><span className="font-semibold">Estado:</span> <span className="font-mono text-blue-900">{subtaskStatus[id].data.status}</span></div>
|
||||||
|
{subtaskStatus[id].data.result && (
|
||||||
|
<div className="mt-1"><span className="font-semibold">Resultado:</span> <span className="font-mono text-blue-900 break-all">{typeof subtaskStatus[id].data.result === 'object' ? JSON.stringify(subtaskStatus[id].data.result) : subtaskStatus[id].data.result}</span></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{taskStatus.result.detail && (
|
||||||
|
<div className="mb-1"><span className="font-semibold">Detalle:</span> <span className="text-blue-900">{taskStatus.result.detail}</span></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<svg className="h-12 w-12 text-green-500 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="h-12 w-12 text-green-500 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7" />
|
||||||
</svg>
|
</svg>
|
||||||
<h2 className="text-2xl font-bold text-green-700 mb-2">{message}</h2>
|
<h2 className="text-2xl font-bold text-green-700 mb-2">{message}</h2>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="mt-4 px-6 py-2 bg-green-600 text-white rounded-lg font-semibold shadow hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-400"
|
className={`mt-4 px-6 py-2 ${isProcessing ? 'bg-blue-600 hover:bg-blue-700 focus:ring-blue-400' : 'bg-green-600 hover:bg-green-700 focus:ring-green-400'} text-white rounded-lg font-semibold shadow focus:outline-none focus:ring-2`}
|
||||||
>
|
>
|
||||||
Cerrar
|
Cerrar
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ async function procesarDatastage(item, setDatastages, setSuccess, setError, setR
|
|||||||
} else {
|
} else {
|
||||||
setSuccess('Procesado correctamente');
|
setSuccess('Procesado correctamente');
|
||||||
}
|
}
|
||||||
|
// El modal de éxito se debe mostrar en el componente principal después de setSuccess
|
||||||
|
// No se llama aquí
|
||||||
if (data && data.registros_cargados) {
|
if (data && data.registros_cargados) {
|
||||||
setRegistrosCargados(data.registros_cargados);
|
setRegistrosCargados(data.registros_cargados);
|
||||||
setShowRegistrosModal(true);
|
setShowRegistrosModal(true);
|
||||||
@@ -136,6 +138,10 @@ export default function Datastage() {
|
|||||||
useLayoutEffect(() => { setShowAnimation(true); }, []);
|
useLayoutEffect(() => { setShowAnimation(true); }, []);
|
||||||
useEffect(() => { if (showAnimation && !hasAnimated) setTimeout(() => setHasAnimated(true), 800); }, [showAnimation, hasAnimated]);
|
useEffect(() => { if (showAnimation && !hasAnimated) setTimeout(() => setHasAnimated(true), 800); }, [showAnimation, hasAnimated]);
|
||||||
|
|
||||||
|
// Mostrar modal de éxito cuando cambia el mensaje de success
|
||||||
|
useEffect(() => {
|
||||||
|
if (success) setShowSuccessModal(true);
|
||||||
|
}, [success]);
|
||||||
// Cargar lista
|
// Cargar lista
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user