import React, { useState, useCallback, useEffect } from 'react'; import { Button } from '../../../ui/Button'; import { useCurrentTenant } from '../../../../stores/tenant.store'; import { useCreateTrainingJob, useTrainingWebSocket, useTrainingJobStatus } from '../../../../api/hooks/training'; interface MLTrainingStepProps { onNext: () => void; onPrevious: () => void; onComplete: (data?: any) => void; isFirstStep: boolean; isLastStep: boolean; } interface TrainingProgress { stage: string; progress: number; message: string; currentStep?: string; estimatedTimeRemaining?: number; } export const MLTrainingStep: React.FC = ({ onComplete }) => { const [trainingProgress, setTrainingProgress] = useState(null); const [isTraining, setIsTraining] = useState(false); const [error, setError] = useState(''); const [jobId, setJobId] = useState(null); const currentTenant = useCurrentTenant(); const createTrainingJob = useCreateTrainingJob(); // Memoized WebSocket callbacks to prevent reconnections const handleProgress = useCallback((data: any) => { setTrainingProgress({ stage: 'training', progress: data.data?.progress || 0, message: data.data?.message || 'Entrenando modelo...', currentStep: data.data?.current_step, estimatedTimeRemaining: data.data?.estimated_time_remaining }); }, []); const handleCompleted = useCallback((_data: any) => { setTrainingProgress({ stage: 'completed', progress: 100, message: 'Entrenamiento completado exitosamente' }); setIsTraining(false); setTimeout(() => { onComplete({ jobId: jobId, success: true, message: 'Modelo entrenado correctamente' }); }, 2000); }, [onComplete, jobId]); const handleError = useCallback((data: any) => { setError(data.data?.error || data.error || 'Error durante el entrenamiento'); setIsTraining(false); setTrainingProgress(null); }, []); const handleStarted = useCallback((_data: any) => { setTrainingProgress({ stage: 'starting', progress: 5, message: 'Iniciando entrenamiento del modelo...' }); }, []); // WebSocket for real-time training progress - only connect when we have a jobId const { isConnected, connectionError } = useTrainingWebSocket( currentTenant?.id || '', jobId || '', undefined, // token will be handled by the service jobId ? { onProgress: handleProgress, onCompleted: handleCompleted, onError: handleError, onStarted: handleStarted } : undefined ); // Smart fallback polling - automatically disabled when WebSocket is connected const { data: jobStatus } = useTrainingJobStatus( currentTenant?.id || '', jobId || '', { enabled: !!jobId && !!currentTenant?.id, isWebSocketConnected: isConnected, // This will disable HTTP polling when WebSocket is connected } ); // Handle training status updates from HTTP polling (fallback only) useEffect(() => { if (!jobStatus || !jobId || trainingProgress?.stage === 'completed') { return; } console.log('📊 HTTP fallback status update:', jobStatus); // Check if training completed via HTTP polling fallback if (jobStatus.status === 'completed' && trainingProgress?.stage !== 'completed') { console.log('✅ Training completion detected via HTTP fallback'); setTrainingProgress({ stage: 'completed', progress: 100, message: 'Entrenamiento completado exitosamente (detectado por verificación HTTP)' }); setIsTraining(false); setTimeout(() => { onComplete({ jobId: jobId, success: true, message: 'Modelo entrenado correctamente', detectedViaPolling: true }); }, 2000); } else if (jobStatus.status === 'failed') { console.log('❌ Training failure detected via HTTP fallback'); setError('Error detectado durante el entrenamiento (verificación de estado)'); setIsTraining(false); setTrainingProgress(null); } else if (jobStatus.status === 'running' && jobStatus.progress !== undefined) { // Update progress if we have newer information from HTTP polling fallback const currentProgress = trainingProgress?.progress || 0; if (jobStatus.progress > currentProgress) { console.log(`📈 Progress update via HTTP fallback: ${jobStatus.progress}%`); setTrainingProgress(prev => ({ ...prev, stage: 'training', progress: jobStatus.progress, message: jobStatus.message || 'Entrenando modelo...', currentStep: jobStatus.current_step }) as TrainingProgress); } } }, [jobStatus, jobId, trainingProgress?.stage, onComplete]); // Auto-trigger training when component mounts useEffect(() => { if (currentTenant?.id && !isTraining && !trainingProgress && !error) { console.log('🚀 Auto-starting ML training for tenant:', currentTenant.id); handleStartTraining(); } }, [currentTenant?.id]); // Only run when tenant is available const handleStartTraining = async () => { if (!currentTenant?.id) { setError('No se encontró información del tenant'); return; } setIsTraining(true); setError(''); setTrainingProgress({ stage: 'preparing', progress: 0, message: 'Preparando datos para entrenamiento...' }); try { const response = await createTrainingJob.mutateAsync({ tenantId: currentTenant.id, request: { // Use the exact backend schema - all fields are optional // This will train on all available data } }); setJobId(response.job_id); setTrainingProgress({ stage: 'queued', progress: 10, message: 'Trabajo de entrenamiento en cola...' }); } catch (err) { setError('Error al iniciar el entrenamiento del modelo'); setIsTraining(false); setTrainingProgress(null); } }; const formatTime = (seconds?: number) => { if (!seconds) return ''; if (seconds < 60) { return `${Math.round(seconds)}s`; } else if (seconds < 3600) { return `${Math.round(seconds / 60)}m`; } else { return `${Math.round(seconds / 3600)}h ${Math.round((seconds % 3600) / 60)}m`; } }; return (

Perfecto! Ahora entrenaremos automáticamente tu modelo de inteligencia artificial utilizando los datos de ventas e inventario que has proporcionado. Este proceso puede tomar varios minutos.

{/* Training Status Card */}
{!isTraining && !trainingProgress && (

Iniciando Entrenamiento Automático

Preparando el entrenamiento de tu modelo con los datos proporcionados...

)} {trainingProgress && (
{trainingProgress.stage === 'completed' ? (
) : (
)} {trainingProgress.progress > 0 && trainingProgress.stage !== 'completed' && (
{trainingProgress.progress}%
)}

{trainingProgress.stage === 'completed' ? '¡Entrenamiento Completo!' : 'Entrenando Modelo IA' }

{trainingProgress.message}

{trainingProgress.stage !== 'completed' && (
{trainingProgress.currentStep || 'Procesando...'}
{jobId && ( {isConnected ? '🟢 Conectado' : '🔴 Desconectado'} )} {trainingProgress.estimatedTimeRemaining && ( Tiempo estimado: {formatTime(trainingProgress.estimatedTimeRemaining)} )}
)}
)}
{/* Training Info */}

¿Qué sucede durante el entrenamiento?

  • • Análisis de patrones de ventas históricos
  • • Creación de modelos predictivos de demanda
  • • Optimización de algoritmos de inventario
  • • Validación y ajuste de precisión
{(error || connectionError) && (

{error || connectionError}

)} {/* Auto-completion when training finishes - no manual buttons needed */}
); };