Fix new services implementation 1

This commit is contained in:
Urtzi Alfaro
2025-08-13 21:41:00 +02:00
parent 16b8a9d50c
commit 262b3dc9c4
13 changed files with 1702 additions and 1210 deletions

View File

@@ -21,6 +21,8 @@ import {
import toast from 'react-hot-toast';
import {
FileValidationResult,
ProductSuggestionsResult,
OnboardingAnalysisResult,
InventorySuggestion,
BusinessModelAnalysis,
@@ -35,12 +37,13 @@ interface SmartHistoricalDataImportProps {
onBack?: () => void;
}
type ImportPhase = 'upload' | 'analysis' | 'review' | 'creation' | 'import' | 'complete';
type ImportPhase = 'upload' | 'validation' | 'suggestions' | 'review' | 'creation' | 'import' | 'complete';
interface PhaseState {
phase: ImportPhase;
file?: File;
analysisResult?: OnboardingAnalysisResult;
validationResult?: FileValidationResult;
suggestionsResult?: ProductSuggestionsResult;
reviewedSuggestions?: InventorySuggestion[];
creationResult?: InventoryCreationResult;
importResult?: SalesImportResult;
@@ -57,22 +60,56 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
const [showAllSuggestions, setShowAllSuggestions] = useState(false);
const handleFileUpload = useCallback(async (file: File) => {
setState(prev => ({ ...prev, file, phase: 'analysis' }));
setState(prev => ({ ...prev, file, phase: 'validation' }));
setIsProcessing(true);
try {
toast.loading('🧠 Analizando tu archivo con IA...', { id: 'analysis' });
// Step 1: Validate file and extract products
toast.loading('📋 Validando archivo...', { id: 'validation' });
const analysisResult = await onboardingService.analyzeSalesDataForOnboarding(tenantId, file);
const validationResult = await onboardingService.validateFileAndExtractProducts(tenantId, file);
toast.success(`¡Análisis completado! ${analysisResult.total_products_found} productos encontrados`, {
id: 'analysis'
if (!validationResult.is_valid) {
throw new Error(`Archivo inválido: ${validationResult.validation_errors.map(e => e.message || e).join(', ')}`);
}
toast.success(`¡Archivo válido! ${validationResult.unique_products} productos únicos encontrados`, {
id: 'validation'
});
setState(prev => ({ ...prev, validationResult, phase: 'suggestions' }));
// Step 2: Generate AI suggestions
setTimeout(() => handleGenerateSuggestions(file, validationResult.product_list), 1000);
} catch (error: any) {
toast.error('Error al validar el archivo', { id: 'validation' });
setState(prev => ({
...prev,
error: error.message || 'Error de validación',
phase: 'upload'
}));
} finally {
setIsProcessing(false);
}
}, [tenantId]);
const handleGenerateSuggestions = useCallback(async (file: File, productList: string[]) => {
setIsProcessing(true);
try {
toast.loading('🧠 Generando sugerencias con IA...', { id: 'suggestions' });
const suggestionsResult = await onboardingService.generateInventorySuggestions(tenantId, file, productList);
toast.success(`¡${suggestionsResult.total_products} productos clasificados! ${suggestionsResult.high_confidence_count} con alta confianza`, {
id: 'suggestions'
});
setState(prev => ({
...prev,
analysisResult,
reviewedSuggestions: analysisResult.inventory_suggestions.map(s => ({
suggestionsResult,
reviewedSuggestions: suggestionsResult.suggestions.map(s => ({
...s,
user_approved: s.confidence_score >= 0.7
})),
@@ -80,11 +117,11 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
}));
} catch (error: any) {
toast.error('Error al analizar el archivo', { id: 'analysis' });
toast.error('Error al generar sugerencias', { id: 'suggestions' });
setState(prev => ({
...prev,
error: error.message || 'Error desconocido',
phase: 'upload'
error: error.message || 'Error en sugerencias de IA',
phase: 'validation'
}));
} finally {
setIsProcessing(false);
@@ -475,17 +512,17 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
</div>
);
case 'analysis':
case 'validation':
return (
<div className="text-center py-12">
<div className="w-20 h-20 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full flex items-center justify-center mx-auto mb-6 animate-pulse">
<Brain className="w-10 h-10 text-white" />
<div className="w-20 h-20 bg-gradient-to-r from-blue-500 to-green-500 rounded-full flex items-center justify-center mx-auto mb-6 animate-pulse">
<CheckCircle2 className="w-10 h-10 text-white" />
</div>
<h2 className="text-xl font-semibold text-gray-900 mb-3">
🧠 Analizando tu archivo con IA...
📋 Validando archivo...
</h2>
<p className="text-gray-600 mb-6">
Esto puede tomar unos momentos mientras clasificamos tus productos
Verificando formato y extrayendo productos únicos
</p>
<div className="bg-white rounded-lg shadow-sm p-4 max-w-md mx-auto">
<div className="flex items-center justify-between text-sm text-gray-600">
@@ -493,16 +530,48 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
<span className="font-medium">{state.file?.name}</span>
</div>
<div className="mt-2 bg-gray-200 rounded-full h-2">
<div className="bg-gradient-to-r from-blue-500 to-purple-500 h-2 rounded-full w-1/2 animate-pulse"></div>
<div className="bg-gradient-to-r from-blue-500 to-green-500 h-2 rounded-full w-1/3 animate-pulse"></div>
</div>
<div className="mt-2 text-xs text-gray-500">Paso 1 de 4: Validación</div>
</div>
</div>
);
case 'suggestions':
return (
<div className="text-center py-12">
<div className="w-20 h-20 bg-gradient-to-r from-purple-500 to-pink-500 rounded-full flex items-center justify-center mx-auto mb-6 animate-pulse">
<Brain className="w-10 h-10 text-white" />
</div>
<h2 className="text-xl font-semibold text-gray-900 mb-3">
🧠 Generando sugerencias con IA...
</h2>
<p className="text-gray-600 mb-6">
Clasificando productos y analizando tu modelo de negocio
</p>
<div className="bg-white rounded-lg shadow-sm p-4 max-w-md mx-auto">
{state.validationResult && (
<div className="mb-4">
<div className="flex items-center justify-center space-x-2 text-green-600 mb-2">
<CheckCircle2 className="w-4 h-4" />
<span className="text-sm font-medium">
{state.validationResult.unique_products} productos únicos encontrados
</span>
</div>
</div>
)}
<div className="bg-gray-200 rounded-full h-2">
<div className="bg-gradient-to-r from-purple-500 to-pink-500 h-2 rounded-full w-2/3 animate-pulse"></div>
</div>
<div className="mt-2 text-xs text-gray-500">Paso 2 de 4: Clasificación IA</div>
</div>
</div>
);
case 'review':
if (!state.analysisResult) return null;
if (!state.suggestionsResult) return null;
const { analysisResult, reviewedSuggestions } = state;
const { suggestionsResult, reviewedSuggestions } = state;
const approvedCount = reviewedSuggestions?.filter(s => s.user_approved).length || 0;
const highConfidenceCount = reviewedSuggestions?.filter(s => s.confidence_score >= 0.7).length || 0;
const visibleSuggestions = showAllSuggestions
@@ -519,12 +588,15 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
¡Análisis Completado! 🎉
</h2>
<p className="text-gray-600">
Hemos encontrado <strong>{analysisResult.total_products_found} productos</strong> y
Hemos encontrado <strong>{suggestionsResult.total_products} productos</strong> y
sugerimos <strong>{approvedCount} para tu inventario</strong>
</p>
<div className="mt-2 text-sm text-gray-500">
Procesado en {suggestionsResult.processing_time_seconds.toFixed(1)}s
</div>
</div>
{renderBusinessModelInsight(analysisResult.business_model_analysis)}
{renderBusinessModelInsight(suggestionsResult.business_model_analysis)}
<div className="bg-white border rounded-xl p-6">
<div className="flex items-center justify-between mb-4">
@@ -579,15 +651,15 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
{visibleSuggestions?.map(renderSuggestionCard)}
</div>
{analysisResult.warnings.length > 0 && (
{state.validationResult?.validation_warnings && state.validationResult.validation_warnings.length > 0 && (
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 mb-4">
<div className="flex">
<AlertTriangle className="h-5 w-5 text-amber-400" />
<div className="ml-3">
<h4 className="text-sm font-medium text-amber-800">Advertencias</h4>
<h4 className="text-sm font-medium text-amber-800">Advertencias de Validación</h4>
<ul className="mt-2 text-sm text-amber-700 space-y-1">
{analysisResult.warnings.map((warning, idx) => (
<li key={idx}> {warning}</li>
{state.validationResult.validation_warnings.map((warning, idx) => (
<li key={idx}> {warning.message || warning}</li>
))}
</ul>
</div>
@@ -630,6 +702,9 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
case 'creation':
case 'import':
const isCreating = state.phase === 'creation';
const stepNumber = isCreating ? 3 : 4;
const stepProgress = isCreating ? 75 : 90;
return (
<div className="text-center py-12">
<div className="w-20 h-20 bg-gradient-to-r from-green-400 to-blue-500 rounded-full flex items-center justify-center mx-auto mb-6 animate-pulse">
@@ -662,11 +737,17 @@ const SmartHistoricalDataImport: React.FC<SmartHistoricalDataImportProps> = ({
)}
<div className="bg-gray-200 rounded-full h-3">
<div className="bg-gradient-to-r from-green-400 to-blue-500 h-3 rounded-full w-3/4 animate-pulse"></div>
<div
className="bg-gradient-to-r from-green-400 to-blue-500 h-3 rounded-full animate-pulse transition-all duration-500"
style={{ width: `${stepProgress}%` }}
/>
</div>
<div className="flex justify-between items-center mt-2">
<p className="text-sm text-gray-500">
{isCreating ? 'Creando inventario...' : 'Procesando importación final...'}
</p>
<span className="text-xs text-gray-400">Paso {stepNumber} de 4</span>
</div>
<p className="text-sm text-gray-500 mt-2">
{isCreating ? 'Creando inventario...' : 'Procesando importación final...'}
</p>
</div>
</div>
);