import React, { useState, useEffect, useRef } from 'react'; import { Brain, Activity, Zap, CheckCircle, AlertCircle, TrendingUp, Upload, Database } from 'lucide-react'; import { Button, Card, Badge } from '../../../ui'; import { OnboardingStepProps } from '../OnboardingWizard'; import { useOnboarding } from '../../../../hooks/business/onboarding'; import { useAuthUser } from '../../../../stores/auth.store'; import { useCurrentTenant } from '../../../../stores'; // Type definitions for training messages (will be moved to API types later) interface TrainingProgressMessage { type: 'training_progress'; progress: number; stage: string; message: string; } interface TrainingCompletedMessage { type: 'training_completed'; metrics: TrainingMetrics; } interface TrainingErrorMessage { type: 'training_error'; error: string; } interface TrainingMetrics { accuracy: number; mape: number; mae: number; rmse: number; } interface TrainingLog { timestamp: string; message: string; level: 'info' | 'warning' | 'error' | 'success'; } interface TrainingJob { id: string; status: 'pending' | 'running' | 'completed' | 'failed'; progress: number; started_at?: string; completed_at?: string; error_message?: string; metrics?: TrainingMetrics; } // Using the proper training service from services/api/training.service.ts export const MLTrainingStep: React.FC = ({ data, onDataChange, onNext, onPrevious, isFirstStep, isLastStep }) => { const user = useAuthUser(); const currentTenant = useCurrentTenant(); // Use the onboarding hooks const { startTraining, trainingOrchestration: { status, progress, currentStep, estimatedTimeRemaining, job, logs, metrics }, data: allStepData, isLoading, error, clearError } = useOnboarding(); // Local state for UI-only elements const [hasStarted, setHasStarted] = useState(false); const wsRef = useRef(null); // Validate that required data is available for training const validateDataRequirements = (): { isValid: boolean; missingItems: string[] } => { const missingItems: string[] = []; console.log('MLTrainingStep - Validating data requirements'); console.log('MLTrainingStep - Current allStepData:', allStepData); // Check if sales data was processed const hasProcessingResults = allStepData?.processingResults && allStepData.processingResults.is_valid && allStepData.processingResults.total_records > 0; // Check if sales data was imported (required for training) const hasImportResults = allStepData?.salesImportResult && (allStepData.salesImportResult.records_created > 0 || allStepData.salesImportResult.success === true || allStepData.salesImportResult.imported === true); if (!hasProcessingResults) { missingItems.push('Datos de ventas validados'); } // Sales data must be imported for ML training to work if (!hasImportResults) { missingItems.push('Datos de ventas importados'); } // Check if products were approved in review step const hasApprovedProducts = allStepData?.approvedProducts && allStepData.approvedProducts.length > 0 && allStepData.reviewCompleted; if (!hasApprovedProducts) { missingItems.push('Productos aprobados en revisión'); } // Check if inventory was configured const hasInventoryConfig = allStepData?.inventoryConfigured && allStepData?.inventoryItems && allStepData.inventoryItems.length > 0; if (!hasInventoryConfig) { missingItems.push('Inventario configurado'); } // Check if we have enough data for training if (dataProcessingData?.processingResults?.total_records && dataProcessingData.processingResults.total_records < 10) { missingItems.push('Suficientes registros de ventas (mínimo 10)'); } console.log('MLTrainingStep - Validation result:', { isValid: missingItems.length === 0, missingItems, hasProcessingResults, hasImportResults, hasApprovedProducts, hasInventoryConfig }); return { isValid: missingItems.length === 0, missingItems }; }; const handleStartTraining = async () => { // Validate data requirements const validation = validateDataRequirements(); if (!validation.isValid) { console.error('Datos insuficientes para entrenamiento:', validation.missingItems); return; } setHasStarted(true); // Use the onboarding hook for training const success = await startTraining({ // You can pass options here if needed startDate: allStepData?.processingResults?.summary?.date_range?.split(' - ')[0], endDate: allStepData?.processingResults?.summary?.date_range?.split(' - ')[1], }); if (!success) { console.error('Error starting training'); setHasStarted(false); } }; // Cleanup WebSocket on unmount useEffect(() => { return () => { if (wsRef.current) { wsRef.current.disconnect(); wsRef.current = null; } }; }, []); useEffect(() => { // Auto-start training if all requirements are met and not already started const validation = validateDataRequirements(); console.log('MLTrainingStep - useEffect validation:', validation); if (validation.isValid && status === 'idle' && data.autoStartTraining) { console.log('MLTrainingStep - Auto-starting training...'); // Auto-start after a brief delay to allow user to see the step const timer = setTimeout(() => { handleStartTraining(); }, 1000); return () => clearTimeout(timer); } }, [allStepData, data.autoStartTraining, status]); const getStatusIcon = () => { switch (status) { case 'idle': return ; case 'validating': return ; case 'training': return ; case 'completed': return ; case 'failed': return ; default: return ; } }; const getStatusColor = () => { switch (status) { case 'completed': return 'text-[var(--color-success)]'; case 'failed': return 'text-[var(--color-error)]'; case 'training': case 'validating': return 'text-[var(--color-info)]'; default: return 'text-[var(--text-primary)]'; } }; const getStatusMessage = () => { switch (status) { case 'idle': return 'Listo para entrenar tu asistente IA'; case 'validating': return 'Validando datos para entrenamiento...'; case 'training': return 'Entrenando modelo de predicción...'; case 'completed': return '¡Tu asistente IA está listo!'; case 'failed': return 'Error en el entrenamiento'; default: return 'Estado desconocido'; } }; // Check data requirements for display const validation = validateDataRequirements(); if (!validation.isValid) { return (

Datos insuficientes para entrenamiento

Para entrenar tu modelo de IA, necesitamos que completes los siguientes elementos:

Elementos requeridos:

    {validation.missingItems.map((item, index) => (
  • {item}
  • ))}

Una vez completados estos elementos, el entrenamiento se iniciará automáticamente.

); } return (
{/* Header */}
{getStatusIcon()}

Entrenamiento de IA

{getStatusMessage()}

Creando tu asistente inteligente personalizado con tus datos de ventas e inventario

{/* Progress Bar */}
Progreso del entrenamiento {progress.toFixed(1)}%
{currentStep && (
Paso actual: {currentStep}
)} {estimatedTimeRemaining > 0 && (
Tiempo estimado restante: {Math.round(estimatedTimeRemaining / 60)} minutos
)}
{/* Training Logs */}

Registro de entrenamiento

{trainingLogs.length === 0 ? (

Esperando inicio de entrenamiento...

) : ( trainingLogs.map((log, index) => (

{log.message}

{new Date(log.timestamp).toLocaleTimeString()}

)) )}
{/* Training Metrics */} {metrics && status === 'completed' && (

Métricas del modelo

{(metrics.accuracy * 100).toFixed(1)}%

Precisión

{metrics.mape.toFixed(1)}%

MAPE

{metrics.mae.toFixed(2)}

MAE

{metrics.rmse.toFixed(2)}

RMSE

)} {/* Manual Start Button (if not auto-started) */} {status === 'idle' && ( )} {/* Navigation */}
); };