From ddb75f8e55b626b63f44e2d926d46c4c34652011 Mon Sep 17 00:00:00 2001 From: Urtzi Alfaro Date: Mon, 8 Sep 2025 22:28:26 +0200 Subject: [PATCH] Simplify the onboardinf flow components 4 --- .../domain/onboarding/OnboardingWizard.tsx | 21 +++ .../onboarding/steps/MLTrainingStep.tsx | 55 ++----- .../onboarding/steps/UploadSalesDataStep.tsx | 134 +++++++++--------- 3 files changed, 101 insertions(+), 109 deletions(-) diff --git a/frontend/src/components/domain/onboarding/OnboardingWizard.tsx b/frontend/src/components/domain/onboarding/OnboardingWizard.tsx index ee1a9efd..656f6652 100644 --- a/frontend/src/components/domain/onboarding/OnboardingWizard.tsx +++ b/frontend/src/components/domain/onboarding/OnboardingWizard.tsx @@ -192,6 +192,27 @@ export const OnboardingWizard: React.FC = () => { console.log(`✅ Successfully completed step: "${currentStep.id}"`); + // Special handling for smart-inventory-setup: auto-complete suppliers step + if (currentStep.id === 'smart-inventory-setup' && data?.shouldAutoCompleteSuppliers) { + try { + console.log('🔄 Auto-completing suppliers step to enable ML training...'); + await markStepCompleted.mutateAsync({ + userId: user.id, + stepName: 'suppliers', + data: { + auto_completed: true, + completed_at: new Date().toISOString(), + source: 'inventory_creation_auto_completion', + message: 'Suppliers step auto-completed to proceed with ML training' + } + }); + console.log('✅ Suppliers step auto-completed successfully'); + } catch (supplierError) { + console.warn('⚠️ Could not auto-complete suppliers step:', supplierError); + // Don't fail the entire flow if suppliers step completion fails + } + } + if (currentStep.id === 'completion') { navigate('/app'); } else { diff --git a/frontend/src/components/domain/onboarding/steps/MLTrainingStep.tsx b/frontend/src/components/domain/onboarding/steps/MLTrainingStep.tsx index c90339aa..87c7e13c 100644 --- a/frontend/src/components/domain/onboarding/steps/MLTrainingStep.tsx +++ b/frontend/src/components/domain/onboarding/steps/MLTrainingStep.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useEffect } from 'react'; import { Button } from '../../../ui/Button'; import { useCurrentTenant } from '../../../../stores/tenant.store'; import { useCreateTrainingJob, useTrainingWebSocket } from '../../../../api/hooks/training'; @@ -20,9 +20,7 @@ interface TrainingProgress { } export const MLTrainingStep: React.FC = ({ - onPrevious, - onComplete, - isFirstStep + onComplete }) => { const [trainingProgress, setTrainingProgress] = useState(null); const [isTraining, setIsTraining] = useState(false); @@ -87,6 +85,14 @@ export const MLTrainingStep: React.FC = ({ } : undefined ); + // 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'); @@ -140,7 +146,7 @@ export const MLTrainingStep: React.FC = ({

- Ahora entrenaremos tu modelo de inteligencia artificial utilizando los datos de ventas + 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.

@@ -151,14 +157,12 @@ export const MLTrainingStep: React.FC = ({ {!isTraining && !trainingProgress && (
- - - +
-

Listo para Entrenar

+

Iniciando Entrenamiento Automático

- Tu modelo está listo para ser entrenado con los datos proporcionados. + Preparando el entrenamiento de tu modelo con los datos proporcionados...

@@ -246,36 +250,7 @@ export const MLTrainingStep: React.FC = ({
)} - {/* Actions */} -
- - - {!isTraining && !trainingProgress && ( - - )} - - {trainingProgress?.stage === 'completed' && ( - - )} -
+ {/* Auto-completion when training finishes - no manual buttons needed */} ); }; \ No newline at end of file diff --git a/frontend/src/components/domain/onboarding/steps/UploadSalesDataStep.tsx b/frontend/src/components/domain/onboarding/steps/UploadSalesDataStep.tsx index 9b8bd0f7..25fb6000 100644 --- a/frontend/src/components/domain/onboarding/steps/UploadSalesDataStep.tsx +++ b/frontend/src/components/domain/onboarding/steps/UploadSalesDataStep.tsx @@ -7,6 +7,7 @@ import { useCurrentTenant } from '../../../../stores/tenant.store'; import { useCreateIngredient } from '../../../../api/hooks/inventory'; import { useImportFileOnly } from '../../../../api/hooks/dataImport'; import { useClassifyProductsBatch } from '../../../../api/hooks/classification'; +import { useAuth } from '../../../../contexts/AuthContext'; interface UploadSalesDataStepProps { onNext: () => void; @@ -64,27 +65,34 @@ export const UploadSalesDataStep: React.FC = ({ const fileInputRef = useRef(null); const currentTenant = useCurrentTenant(); + const { user } = useAuth(); const { validateFile } = useValidateFileOnly(); const createIngredient = useCreateIngredient(); const { importFile } = useImportFileOnly(); const classifyProducts = useClassifyProductsBatch(); - const handleFileSelect = (event: React.ChangeEvent) => { + const handleFileSelect = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { setSelectedFile(file); setValidationResult(null); setError(''); + + // Automatically trigger validation and classification + await handleAutoValidateAndClassify(file); } }; - const handleDrop = (event: React.DragEvent) => { + const handleDrop = async (event: React.DragEvent) => { event.preventDefault(); const file = event.dataTransfer.files[0]; if (file) { setSelectedFile(file); setValidationResult(null); setError(''); + + // Automatically trigger validation and classification + await handleAutoValidateAndClassify(file); } }; @@ -92,67 +100,69 @@ export const UploadSalesDataStep: React.FC = ({ event.preventDefault(); }; - const handleValidateFile = async () => { - if (!selectedFile || !currentTenant?.id) return; + const handleAutoValidateAndClassify = async (file: File) => { + if (!currentTenant?.id) return; setIsValidating(true); setError(''); - setProgressState({ stage: 'preparing', progress: 0, message: 'Preparando validación del archivo...' }); + setProgressState({ stage: 'preparing', progress: 0, message: 'Preparando validación automática del archivo...' }); try { + // Step 1: Validate the file const result = await validateFile( currentTenant.id, - selectedFile, + file, { onProgress: (stage: string, progress: number, message: string) => { - setProgressState({ stage, progress, message }); + // Map validation progress to 0-50% + setProgressState({ stage, progress: Math.min(progress * 0.5, 50), message }); } } ); if (result.success && result.validationResult) { setValidationResult(result.validationResult); - setProgressState(null); + setProgressState({ stage: 'analyzing', progress: 60, message: 'Validación exitosa. Generando sugerencias automáticamente...' }); + + // Step 2: Automatically trigger classification + await generateInventorySuggestionsAuto(result.validationResult); } else { setError(result.error || 'Error al validar el archivo'); setProgressState(null); + setIsValidating(false); } } catch (error) { setError('Error validando archivo: ' + (error instanceof Error ? error.message : 'Error desconocido')); setProgressState(null); - } - - setIsValidating(false); - }; - - const handleContinue = () => { - if (validationResult) { - // Generate inventory suggestions based on validation - generateInventorySuggestions(); + setIsValidating(false); } }; - const generateInventorySuggestions = async () => { - if (!currentTenant?.id || !validationResult) { + + const generateInventorySuggestionsAuto = async (validationData: ImportValidationResponse) => { + if (!currentTenant?.id) { setError('No hay datos de validación disponibles para generar sugerencias'); + setIsValidating(false); + setProgressState(null); return; } - setProgressState({ stage: 'analyzing', progress: 25, message: 'Analizando productos de ventas...' }); - try { + setProgressState({ stage: 'analyzing', progress: 65, message: 'Analizando productos de ventas...' }); + // Extract product data from validation result - use the exact backend structure - const products = validationResult.product_list?.map((productName: string) => ({ + const products = validationData.product_list?.map((productName: string) => ({ product_name: productName })) || []; if (products.length === 0) { setError('No se encontraron productos en los datos de ventas'); setProgressState(null); + setIsValidating(false); return; } - setProgressState({ stage: 'classifying', progress: 50, message: 'Clasificando productos con IA...' }); + setProgressState({ stage: 'classifying', progress: 75, message: 'Clasificando productos con IA...' }); // Call the classification API const suggestions = await classifyProducts.mutateAsync({ @@ -160,7 +170,7 @@ export const UploadSalesDataStep: React.FC = ({ batchData: { products } }); - setProgressState({ stage: 'preparing', progress: 75, message: 'Preparando sugerencias de inventario...' }); + setProgressState({ stage: 'preparing', progress: 90, message: 'Preparando sugerencias de inventario...' }); // Convert API response to InventoryItem format - use exact backend structure plus UI fields const items: InventoryItem[] = suggestions.map(suggestion => { @@ -201,13 +211,16 @@ export const UploadSalesDataStep: React.FC = ({ setInventoryItems(items); setShowInventoryStep(true); setProgressState(null); + setIsValidating(false); } catch (err) { console.error('Error generating inventory suggestions:', err); setError('Error al generar sugerencias de inventario. Por favor, inténtalo de nuevo.'); setProgressState(null); + setIsValidating(false); } }; + const handleToggleSelection = (id: string) => { setInventoryItems(items => items.map(item => @@ -328,7 +341,10 @@ export const UploadSalesDataStep: React.FC = ({ totalItems: selectedItems.length, validationResult, file: selectedFile, - salesImportResult + salesImportResult, + inventoryConfigured: true, // Flag for ML training dependency + shouldAutoCompleteSuppliers: true, // Flag to trigger suppliers auto-completion after step completion + userId: user?.id // Pass user ID for suppliers completion }); } catch (err) { console.error('Error creating inventory items:', err); @@ -354,7 +370,7 @@ export const UploadSalesDataStep: React.FC = ({

- Basado en tus datos de ventas, hemos generado estas sugerencias de inventario. + ¡Perfecto! Hemos analizado automáticamente tus datos de ventas y generado estas sugerencias de inventario inteligentes. Revisa y selecciona los artículos que te gustaría agregar a tu inventario.

@@ -487,14 +503,7 @@ export const UploadSalesDataStep: React.FC = ({ )} {/* Actions */} -
- +
{/* Removed back button */} +
{/* Removed back button */} -
- {selectedFile && !validationResult && ( - - )} - - {validationResult && ( - - )} -
+ {selectedFile && !showInventoryStep && ( +
+ {isValidating ? ( + <> +
+ Procesando automáticamente... + + ) : validationResult ? ( + <> + + + + Archivo procesado exitosamente + + ) : null} +
+ )}
);