Start integrating the onboarding flow with backend 4
This commit is contained in:
@@ -2,7 +2,12 @@ import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Upload, Brain, CheckCircle, AlertCircle, Download, FileText, Activity, TrendingUp } from 'lucide-react';
|
||||
import { Button, Card, Badge } from '../../../ui';
|
||||
import { OnboardingStepProps } from '../OnboardingWizard';
|
||||
import { onboardingApiService } from '../../../../services/api/onboarding.service';
|
||||
import { useInventory } from '../../../../hooks/api/useInventory';
|
||||
import { useSales } from '../../../../hooks/api/useSales';
|
||||
import { useModal } from '../../../../hooks/ui/useModal';
|
||||
import { useToast } from '../../../../hooks/ui/useToast';
|
||||
import { salesService } from '../../../../services/api/sales.service';
|
||||
import { inventoryService } from '../../../../services/api/inventory.service';
|
||||
import { useAuthUser, useAuthLoading } from '../../../../stores/auth.store';
|
||||
import { useCurrentTenant, useTenantLoading } from '../../../../stores/tenant.store';
|
||||
import { useAlertActions } from '../../../../stores/alerts.store';
|
||||
@@ -30,70 +35,90 @@ interface ProcessingResult {
|
||||
recommendations: string[];
|
||||
}
|
||||
|
||||
// Real data processing service using backend APIs
|
||||
const dataProcessingService = {
|
||||
processFile: async (
|
||||
file: File,
|
||||
tenantId: string,
|
||||
onProgress: (progress: number, stage: string, message: string) => void
|
||||
) => {
|
||||
try {
|
||||
// Stage 1: Validate file with sales service
|
||||
onProgress(20, 'validating', 'Validando estructura del archivo...');
|
||||
const validationResult = await onboardingApiService.validateOnboardingFile(tenantId, file);
|
||||
|
||||
onProgress(40, 'validating', 'Verificando integridad de datos...');
|
||||
|
||||
if (!validationResult.is_valid) {
|
||||
throw new Error('Archivo de datos inválido');
|
||||
}
|
||||
|
||||
if (!validationResult.product_list || validationResult.product_list.length === 0) {
|
||||
throw new Error('No se encontraron productos en el archivo');
|
||||
}
|
||||
|
||||
// Stage 2: Generate AI suggestions with inventory service
|
||||
onProgress(60, 'analyzing', 'Identificando productos únicos...');
|
||||
onProgress(80, 'analyzing', 'Analizando patrones de venta...');
|
||||
|
||||
console.log('DataProcessingStep - Calling generateInventorySuggestions with:', {
|
||||
tenantId,
|
||||
fileName: file.name,
|
||||
productList: validationResult.product_list
|
||||
});
|
||||
|
||||
const suggestionsResult = await onboardingApiService.generateInventorySuggestions(
|
||||
tenantId,
|
||||
file,
|
||||
validationResult.product_list
|
||||
);
|
||||
|
||||
console.log('DataProcessingStep - AI suggestions result:', suggestionsResult);
|
||||
|
||||
onProgress(90, 'analyzing', 'Generando recomendaciones con IA...');
|
||||
onProgress(100, 'completed', 'Procesamiento completado');
|
||||
|
||||
// Combine results
|
||||
const combinedResult = {
|
||||
...validationResult,
|
||||
productsIdentified: suggestionsResult.total_products || validationResult.unique_products,
|
||||
categoriesDetected: suggestionsResult.suggestions ?
|
||||
new Set(suggestionsResult.suggestions.map(s => s.category)).size : 4,
|
||||
businessModel: suggestionsResult.business_model_analysis?.model || 'production',
|
||||
confidenceScore: suggestionsResult.high_confidence_count && suggestionsResult.total_products ?
|
||||
Math.round((suggestionsResult.high_confidence_count / suggestionsResult.total_products) * 100) : 85,
|
||||
recommendations: suggestionsResult.business_model_analysis?.recommendations || [],
|
||||
aiSuggestions: suggestionsResult.suggestions || []
|
||||
};
|
||||
|
||||
console.log('DataProcessingStep - Combined result:', combinedResult);
|
||||
console.log('DataProcessingStep - Combined result aiSuggestions:', combinedResult.aiSuggestions);
|
||||
console.log('DataProcessingStep - Combined result aiSuggestions length:', combinedResult.aiSuggestions?.length);
|
||||
return combinedResult;
|
||||
} catch (error) {
|
||||
console.error('Data processing error:', error);
|
||||
throw error;
|
||||
// Data processing utility function
|
||||
const processDataFile = async (
|
||||
file: File,
|
||||
onProgress: (progress: number, stage: string, message: string) => void,
|
||||
validateSalesData: any,
|
||||
generateInventorySuggestions: any
|
||||
) => {
|
||||
try {
|
||||
// Stage 1: Validate file with sales service
|
||||
onProgress(20, 'validating', 'Validando estructura del archivo...');
|
||||
const validationResult = await validateSalesData(file);
|
||||
|
||||
onProgress(40, 'validating', 'Verificando integridad de datos...');
|
||||
|
||||
if (!validationResult.is_valid) {
|
||||
throw new Error('Archivo de datos inválido');
|
||||
}
|
||||
|
||||
if (!validationResult.product_list || validationResult.product_list.length === 0) {
|
||||
throw new Error('No se encontraron productos en el archivo');
|
||||
}
|
||||
|
||||
// Stage 2: Store validation result for later import (after inventory setup)
|
||||
onProgress(50, 'validating', 'Procesando datos identificados...');
|
||||
|
||||
// Stage 3: Generate AI suggestions with inventory service
|
||||
onProgress(60, 'analyzing', 'Identificando productos únicos...');
|
||||
onProgress(80, 'analyzing', 'Analizando patrones de venta...');
|
||||
|
||||
console.log('DataProcessingStep - Validation result:', validationResult);
|
||||
console.log('DataProcessingStep - Product list:', validationResult.product_list);
|
||||
console.log('DataProcessingStep - Product list length:', validationResult.product_list?.length);
|
||||
|
||||
// Extract product list from validation result
|
||||
const productList = validationResult.product_list || [];
|
||||
|
||||
console.log('DataProcessingStep - Generating AI suggestions with:', {
|
||||
fileName: file.name,
|
||||
productList: productList,
|
||||
productListLength: productList.length
|
||||
});
|
||||
|
||||
let suggestionsResult;
|
||||
if (productList.length > 0) {
|
||||
suggestionsResult = await generateInventorySuggestions(productList);
|
||||
} else {
|
||||
console.warn('DataProcessingStep - No products found, creating default suggestions');
|
||||
suggestionsResult = {
|
||||
suggestions: [],
|
||||
total_products: validationResult.unique_products || 0,
|
||||
business_model_analysis: {
|
||||
model: 'production' as const,
|
||||
recommendations: []
|
||||
},
|
||||
high_confidence_count: 0
|
||||
};
|
||||
}
|
||||
|
||||
console.log('DataProcessingStep - AI suggestions result:', suggestionsResult);
|
||||
|
||||
onProgress(90, 'analyzing', 'Generando recomendaciones con IA...');
|
||||
onProgress(100, 'completed', 'Procesamiento completado');
|
||||
|
||||
// Combine results
|
||||
const combinedResult = {
|
||||
...validationResult,
|
||||
salesDataFile: file, // Store file for later import after inventory setup
|
||||
productsIdentified: suggestionsResult.total_products || validationResult.unique_products,
|
||||
categoriesDetected: suggestionsResult.suggestions ?
|
||||
new Set(suggestionsResult.suggestions.map(s => s.category)).size : 4,
|
||||
businessModel: suggestionsResult.business_model_analysis?.model || 'production',
|
||||
confidenceScore: suggestionsResult.high_confidence_count && suggestionsResult.total_products ?
|
||||
Math.round((suggestionsResult.high_confidence_count / suggestionsResult.total_products) * 100) : 85,
|
||||
recommendations: suggestionsResult.business_model_analysis?.recommendations || [],
|
||||
aiSuggestions: suggestionsResult.suggestions || []
|
||||
};
|
||||
|
||||
console.log('DataProcessingStep - Combined result:', combinedResult);
|
||||
console.log('DataProcessingStep - Combined result aiSuggestions:', combinedResult.aiSuggestions);
|
||||
console.log('DataProcessingStep - Combined result aiSuggestions length:', combinedResult.aiSuggestions?.length);
|
||||
return combinedResult;
|
||||
} catch (error) {
|
||||
console.error('Data processing error:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -111,6 +136,12 @@ export const DataProcessingStep: React.FC<OnboardingStepProps> = ({
|
||||
const tenantLoading = useTenantLoading();
|
||||
const { createAlert } = useAlertActions();
|
||||
|
||||
// Use hooks for UI and direct service calls for now (until we extend hooks)
|
||||
const { isLoading: inventoryLoading } = useInventory();
|
||||
const { isLoading: salesLoading } = useSales();
|
||||
const errorModal = useModal();
|
||||
const { showToast } = useToast();
|
||||
|
||||
// Check if we're still loading user or tenant data
|
||||
const isLoadingUserData = authLoading || tenantLoading;
|
||||
|
||||
@@ -189,13 +220,21 @@ export const DataProcessingStep: React.FC<OnboardingStepProps> = ({
|
||||
const fileExtension = file.name.toLowerCase().substring(file.name.lastIndexOf('.'));
|
||||
|
||||
if (!validExtensions.includes(fileExtension)) {
|
||||
alert('Formato de archivo no válido. Usa CSV o Excel (.xlsx, .xls)');
|
||||
showToast({
|
||||
title: 'Formato inválido',
|
||||
message: 'Formato de archivo no válido. Usa CSV o Excel (.xlsx, .xls)',
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check file size (max 10MB)
|
||||
if (file.size > 10 * 1024 * 1024) {
|
||||
alert('El archivo es demasiado grande. Máximo 10MB permitido.');
|
||||
showToast({
|
||||
title: 'Archivo muy grande',
|
||||
message: 'El archivo es demasiado grande. Máximo 10MB permitido.',
|
||||
type: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -236,14 +275,15 @@ export const DataProcessingStep: React.FC<OnboardingStepProps> = ({
|
||||
|
||||
console.log('DataProcessingStep - Starting file processing with tenant:', tenantId);
|
||||
|
||||
const result = await dataProcessingService.processFile(
|
||||
const result = await processDataFile(
|
||||
file,
|
||||
tenantId,
|
||||
(newProgress, newStage, message) => {
|
||||
setProgress(newProgress);
|
||||
setStage(newStage as ProcessingStage);
|
||||
setCurrentMessage(message);
|
||||
}
|
||||
},
|
||||
salesService.validateSalesData.bind(salesService),
|
||||
inventoryService.generateInventorySuggestions.bind(inventoryService)
|
||||
);
|
||||
|
||||
setResults(result);
|
||||
@@ -321,8 +361,14 @@ export const DataProcessingStep: React.FC<OnboardingStepProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const templateData = await onboardingApiService.getSalesImportTemplate(tenantId, 'csv');
|
||||
onboardingApiService.downloadTemplate(templateData, 'plantilla_ventas.csv', 'csv');
|
||||
// Template download functionality can be implemented later if needed
|
||||
console.warn('Template download not yet implemented in reorganized structure');
|
||||
createAlert({
|
||||
type: 'info',
|
||||
category: 'system',
|
||||
title: 'Descarga de plantilla no disponible',
|
||||
message: 'Esta funcionalidad se implementará próximamente.'
|
||||
});
|
||||
|
||||
createAlert({
|
||||
type: 'success',
|
||||
|
||||
Reference in New Issue
Block a user