Fix new services implementation 5

This commit is contained in:
Urtzi Alfaro
2025-08-15 17:53:59 +02:00
parent 03b4d4185d
commit f7de9115d1
43 changed files with 1714 additions and 891 deletions

View File

@@ -7,13 +7,13 @@ import SmartHistoricalDataImport from '../../components/onboarding/SmartHistoric
import {
useTenant,
useTraining,
useSales,
useTrainingWebSocket,
useOnboarding,
TenantCreate,
TrainingJobRequest
} from '../../api';
import { useTraining } from '../../api/hooks/useTraining';
import { OnboardingRouter } from '../../utils/onboardingRouter';
@@ -134,7 +134,7 @@ const OnboardingPage: React.FC<OnboardingPageProps> = ({ user, onComplete }) =>
fetchTenantIdFromBackend();
}, [tenantId, user, getUserTenants]);
// WebSocket connection for real-time training updates
// Enhanced WebSocket connection for real-time training updates
const {
status,
jobUpdates,
@@ -143,7 +143,11 @@ const OnboardingPage: React.FC<OnboardingPageProps> = ({ user, onComplete }) =>
isConnected,
lastMessage,
tenantId: resolvedTenantId,
wsUrl
wsUrl,
connectionError,
isAuthenticationError,
refreshConnection,
retryWithAuth
} = useTrainingWebSocket(trainingJobId || 'pending', tenantId);
// Handle WebSocket job updates
@@ -203,12 +207,19 @@ const OnboardingPage: React.FC<OnboardingPageProps> = ({ user, onComplete }) =>
currentStep: 'Error en el entrenamiento'
}));
} else if (messageType === 'initial_status') {
} else if (messageType === 'initial_status' || messageType === 'current_status') {
console.log('Received training status update:', messageType, data);
setTrainingProgress(prev => ({
...prev,
progress: typeof data.progress === 'number' ? data.progress : prev.progress,
status: data.status || prev.status,
currentStep: data.current_step || data.currentStep || prev.currentStep
currentStep: data.current_step || data.currentStep || prev.currentStep,
productsCompleted: data.products_completed || data.productsCompleted || prev.productsCompleted,
productsTotal: data.products_total || data.productsTotal || prev.productsTotal,
estimatedTimeRemaining: data.estimated_time_remaining_minutes ||
data.estimated_time_remaining ||
data.estimatedTimeRemaining ||
prev.estimatedTimeRemaining
}));
}
}, []);
@@ -228,10 +239,94 @@ const OnboardingPage: React.FC<OnboardingPageProps> = ({ user, onComplete }) =>
}
}, [jobUpdates, processWebSocketMessage]);
// Connect to WebSocket when training starts
// Enhanced WebSocket connection management with polling fallback
useEffect(() => {
if (tenantId && trainingJobId && currentStep === 3) {
console.log('Connecting to training WebSocket:', { tenantId, trainingJobId, wsUrl });
connect();
// Simple polling fallback for training completion detection (now that we fixed the 404 issue)
const pollingInterval = setInterval(async () => {
if (trainingProgress.status === 'running' || trainingProgress.status === 'pending') {
try {
// Check training job status via REST API as fallback
const response = await fetch(`http://localhost:8000/api/v1/tenants/${tenantId}/training/jobs/${trainingJobId}/status`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
'X-Tenant-ID': tenantId
}
});
if (response.ok) {
const jobStatus = await response.json();
// If the job is completed but we haven't received WebSocket notification
if (jobStatus.status === 'completed' && (trainingProgress.status === 'running' || trainingProgress.status === 'pending')) {
console.log('Training completed detected via REST polling fallback');
setTrainingProgress(prev => ({
...prev,
progress: 100,
status: 'completed',
currentStep: 'Entrenamiento completado',
estimatedTimeRemaining: 0
}));
// Mark training step as completed in onboarding API
completeStep('training_completed', {
training_completed_at: new Date().toISOString(),
user_id: user?.id,
tenant_id: tenantId,
completion_detected_via: 'rest_polling_fallback'
}).catch(error => {
console.warn('Failed to mark training as completed in API:', error);
});
// Show celebration and auto-advance to final step after 3 seconds
toast.success('🎉 Training completed! Your AI model is ready to use.', {
duration: 5000,
icon: '🤖'
});
setTimeout(() => {
manualNavigation.current = true;
setCurrentStep(4);
}, 3000);
// Clear the polling interval
clearInterval(pollingInterval);
}
// If job failed, update status
if (jobStatus.status === 'failed' && (trainingProgress.status === 'running' || trainingProgress.status === 'pending')) {
console.log('Training failure detected via REST polling fallback');
setTrainingProgress(prev => ({
...prev,
status: 'failed',
error: jobStatus.error_message || 'Error en el entrenamiento',
currentStep: 'Error en el entrenamiento'
}));
clearInterval(pollingInterval);
}
}
} catch (error) {
// Ignore polling errors to avoid noise
console.debug('REST polling error (expected if training not started):', error);
}
} else if (trainingProgress.status === 'completed' || trainingProgress.status === 'failed') {
// Clear polling if training is finished
clearInterval(pollingInterval);
}
}, 15000); // Poll every 15 seconds (less aggressive than before)
return () => {
if (isConnected) {
disconnect();
}
clearInterval(pollingInterval);
};
}
return () => {
@@ -239,7 +334,35 @@ const OnboardingPage: React.FC<OnboardingPageProps> = ({ user, onComplete }) =>
disconnect();
}
};
}, [tenantId, trainingJobId, currentStep, connect, disconnect, isConnected]);
}, [tenantId, trainingJobId, currentStep]); // Removed problematic dependencies that cause reconnection loops
// Handle connection errors with user feedback
useEffect(() => {
if (connectionError) {
if (isAuthenticationError) {
toast.error('Sesión expirada. Reintentando conexión...');
// Auto-retry authentication errors after 3 seconds
setTimeout(() => {
retryWithAuth();
}, 3000);
} else {
console.warn('WebSocket connection error:', connectionError);
// Don't show error toast for non-auth errors as they auto-retry
}
}
}, [connectionError, isAuthenticationError, retryWithAuth]);
// Enhanced WebSocket status logging
useEffect(() => {
console.log('WebSocket status changed:', {
status,
isConnected,
jobId: trainingJobId,
tenantId,
connectionError,
isAuthenticationError
});
}, [status, isConnected, trainingJobId, tenantId, connectionError, isAuthenticationError]);
const storeTenantId = (tenantId: string) => {
@@ -632,6 +755,10 @@ const OnboardingPage: React.FC<OnboardingPageProps> = ({ user, onComplete }) =>
estimatedTimeRemaining: trainingProgress.estimatedTimeRemaining,
error: trainingProgress.error
}}
websocketStatus={status}
connectionError={connectionError}
isConnected={isConnected}
onRetryConnection={refreshConnection}
onTimeout={() => {
toast.success('El entrenamiento continuará en segundo plano. ¡Puedes empezar a explorar!');
onComplete(); // Navigate to dashboard