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 { useModal } from '../../../../hooks/ui/useModal'; import { useToast } from '../../../../hooks/ui/useToast'; import { useOnboarding } from '../../../../hooks/business/onboarding'; import { useAuthUser, useAuthLoading } from '../../../../stores/auth.store'; import { useCurrentTenant, useTenantLoading } from '../../../../stores'; type ProcessingStage = 'upload' | 'validating' | 'analyzing' | 'completed' | 'error'; interface ProcessingResult { // Validation data is_valid: boolean; total_records: number; unique_products: number; product_list: string[]; validation_errors: string[]; validation_warnings: string[]; summary: { date_range: string; total_sales: number; average_daily_sales: number; }; // Analysis data productsIdentified: number; categoriesDetected: number; businessModel: string; confidenceScore: number; recommendations: string[]; } // This function has been replaced by the onboarding hooks export const DataProcessingStep: React.FC = ({ data, onDataChange, onNext, onPrevious, isFirstStep, isLastStep }) => { const user = useAuthUser(); const authLoading = useAuthLoading(); const currentTenant = useCurrentTenant(); const tenantLoading = useTenantLoading(); // Use the new onboarding hooks const { processSalesFile, generateInventorySuggestions, salesProcessing: { stage: onboardingStage, progress: onboardingProgress, currentMessage: onboardingMessage, validationResults, suggestions }, tenantCreation, isLoading, error, clearError } = useOnboarding(); const errorModal = useModal(); const toast = useToast(); // Check if we're still loading user or tenant data const isLoadingUserData = authLoading || tenantLoading; // Get tenant ID from multiple sources with fallback const getTenantId = (): string | null => { // Also check the onboarding data for tenant creation success const onboardingTenantId = data.bakery?.tenant_id; const tenantId = currentTenant?.id || user?.tenant_id || onboardingTenantId || null; console.log('DataProcessingStep - getTenantId:', { currentTenant: currentTenant?.id, userTenantId: user?.tenant_id, onboardingTenantId: onboardingTenantId, finalTenantId: tenantId, isLoadingUserData, authLoading, tenantLoading, user: user ? { id: user.id, email: user.email } : null, tenantCreationSuccess: data.tenantCreation?.isSuccess }); return tenantId; }; // Check if tenant data is available (not loading and has ID, OR tenant was created successfully) const isTenantAvailable = (): boolean => { const hasAuth = !authLoading && user; const hasTenantId = getTenantId() !== null; const tenantCreatedSuccessfully = tenantCreation.isSuccess; const tenantCreatedInOnboarding = data.bakery?.tenantCreated === true; const isAvailable = hasAuth && (hasTenantId || tenantCreatedSuccessfully || tenantCreatedInOnboarding); console.log('DataProcessingStep - isTenantAvailable:', { hasAuth, hasTenantId, tenantCreatedSuccessfully, tenantCreatedInOnboarding, isAvailable, authLoading, tenantLoading, tenantCreationFromHook: tenantCreation, bakeryData: data.bakery }); return isAvailable; }; // Use onboarding hook state when available, fallback to local state const [localStage, setLocalStage] = useState(data.processingStage || 'upload'); const [uploadedFile, setUploadedFile] = useState(data.files?.salesData || null); const [localResults, setLocalResults] = useState(data.processingResults || null); // Derive current state from onboarding hooks or local state // Priority: if local is 'completed' or 'error', use local; otherwise use onboarding state const stage = (localStage === 'completed' || localStage === 'error') ? localStage : (onboardingStage || localStage); const progress = onboardingProgress || 0; const currentMessage = onboardingMessage || ''; const results = (validationResults && suggestions) ? { ...validationResults, aiSuggestions: suggestions, // Add calculated fields from backend response productsIdentified: validationResults.unique_products || validationResults.product_list?.length || 0, categoriesDetected: suggestions ? new Set(suggestions.map((s: any) => s.category)).size : 0, businessModel: 'production', confidenceScore: 85, recommendations: validationResults.summary?.suggestions || [], // Backend response details totalRecords: validationResults.total_records || 0, validRecords: validationResults.valid_records || 0, invalidRecords: validationResults.invalid_records || 0, fileFormat: validationResults.summary?.file_format || 'csv', fileSizeMb: validationResults.summary?.file_size_mb || 0, estimatedProcessingTime: validationResults.summary?.estimated_processing_time_seconds || 0, detectedColumns: validationResults.summary?.detected_columns || [], validationMessage: validationResults.message || 'Validación completada' } : localResults; // Debug logging for state changes console.log('DataProcessingStep - State debug:', { localStage, onboardingStage, finalStage: stage, hasValidationResults: !!validationResults, hasSuggestions: !!suggestions, hasResults: !!results, localResults: !!localResults }); const [dragActive, setDragActive] = useState(false); const fileInputRef = useRef(null); const lastStateRef = useRef({ stage, progress, currentMessage, results, suggestions, uploadedFile }); useEffect(() => { // Only update if state actually changed const currentState = { stage, progress, currentMessage, results, suggestions, uploadedFile }; if (JSON.stringify(currentState) !== JSON.stringify(lastStateRef.current)) { lastStateRef.current = currentState; // Update parent data when state changes onDataChange({ processingStage: stage, processingProgress: progress, currentMessage: currentMessage, processingResults: results, suggestions: suggestions, files: { ...data.files, salesData: uploadedFile } }); } }, [stage, progress, currentMessage, results, suggestions, uploadedFile, onDataChange, data.files]); const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); setDragActive(true); }; const handleDragLeave = (e: React.DragEvent) => { e.preventDefault(); setDragActive(false); }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); setDragActive(false); const files = Array.from(e.dataTransfer.files); if (files.length > 0) { handleFileUpload(files[0]); } }; const handleFileSelect = (e: React.ChangeEvent) => { if (e.target.files && e.target.files.length > 0) { handleFileUpload(e.target.files[0]); } }; const handleFileUpload = async (file: File) => { // Validate file type const validExtensions = ['.csv', '.xlsx', '.xls', '.json']; const fileExtension = file.name.toLowerCase().substring(file.name.lastIndexOf('.')); if (!validExtensions.includes(fileExtension)) { toast.addToast('Formato de archivo no válido. Usa CSV, JSON o Excel (.xlsx, .xls)', { title: 'Formato inválido', type: 'error' }); return; } // Check file size (max 10MB) if (file.size > 10 * 1024 * 1024) { toast.addToast('El archivo es demasiado grande. Máximo 10MB permitido.', { title: 'Archivo muy grande', type: 'error' }); return; } setUploadedFile(file); setLocalStage('validating'); try { // Wait for user data to load if still loading if (!isTenantAvailable()) { console.log('Tenant not available, waiting...'); setUploadedFile(null); setLocalStage('upload'); toast.addToast('Por favor espere mientras cargamos su información...', { title: 'Esperando datos de usuario', type: 'info' }); return; } console.log('DataProcessingStep - Starting file processing', { fileName: file.name, fileSize: file.size, fileType: file.type, lastModified: file.lastModified }); // Use the onboarding hook for file processing const success = await processSalesFile(file, (progress, stage, message) => { console.log(`Processing: ${progress}% - ${stage} - ${message}`); }); if (success) { setLocalStage('completed'); // If we have results from the onboarding hook, store them locally too if (validationResults && suggestions) { const processedResults = { ...validationResults, aiSuggestions: suggestions, productsIdentified: validationResults.product_list?.length || 0, categoriesDetected: suggestions ? new Set(suggestions.map((s: any) => s.category)).size : 0, businessModel: 'production', confidenceScore: 85, recommendations: [] }; setLocalResults(processedResults); } toast.addToast('El archivo se procesó correctamente', { title: 'Procesamiento completado', type: 'success' }); } else { throw new Error('Error procesando el archivo'); } } catch (error) { console.error('DataProcessingStep - Processing error:', error); console.error('DataProcessingStep - Error details:', { error, errorMessage: error instanceof Error ? error.message : 'Unknown error', errorStack: error instanceof Error ? error.stack : undefined, uploadedFile: file?.name, fileSize: file?.size, fileType: file?.type, localUploadedFile: uploadedFile?.name }); setLocalStage('error'); const errorMessage = error instanceof Error ? error.message : 'Error en el procesamiento de datos'; toast.addToast(errorMessage, { title: 'Error en el procesamiento', type: 'error' }); } }; const downloadTemplate = () => { // Provide a static CSV template const csvContent = `fecha,producto,cantidad,precio_unitario,precio_total,cliente,canal_venta 2024-01-15,Pan Integral,5,2.50,12.50,Cliente A,Tienda 2024-01-15,Croissant,3,1.80,5.40,Cliente B,Online 2024-01-15,Baguette,2,3.00,6.00,Cliente C,Tienda 2024-01-16,Pan de Centeno,4,2.80,11.20,Cliente A,Tienda 2024-01-16,Empanadas,6,4.50,27.00,Cliente D,Delivery`; const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', 'plantilla_ventas.csv'); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); toast.addToast('La plantilla se descargó correctamente.', { title: 'Plantilla descargada', type: 'success' }); }; const resetProcess = () => { setLocalStage('upload'); setUploadedFile(null); setLocalResults(null); if (fileInputRef.current) { fileInputRef.current.value = ''; } if (error) { clearError(); } }; return (
{/* Loading state when tenant data is not available */} {!isTenantAvailable() && (

Cargando datos de usuario...

Por favor espere mientras cargamos su información de tenant

)} {/* Improved Upload Stage */} {(stage === 'idle' || localStage === 'upload') && isTenantAvailable() && ( <>
fileInputRef.current?.click()} >
{uploadedFile ? ( <>

¡Perfecto! Archivo listo

📄 {uploadedFile.name}

{(uploadedFile.size / 1024 / 1024).toFixed(2)} MB • Listo para procesar

) : ( <>

Sube tu historial de ventas

Arrastra y suelta tu archivo aquí, o haz clic para seleccionar

{/* Visual indicators */}
📊
CSV
📈
Excel
Hasta 10MB
)}
💡 Formatos aceptados: CSV, Excel (XLSX, XLS) • Tamaño máximo: 10MB
{/* Improved Template Download Section */}

¿Necesitas ayuda con el formato?

Descarga nuestra plantilla Excel con ejemplos y formato correcto para tus datos de ventas

)} {/* Processing Stages */} {(stage === 'validating' || stage === 'analyzing') && (
{stage === 'validating' ? ( ) : ( )}

{stage === 'validating' ? 'Validando datos...' : 'Analizando con IA...'}

{currentMessage}

{/* Progress Bar */}
Progreso {progress}%
{/* Processing Steps */}
= 40 ? 'bg-[var(--color-success)]/10 text-[var(--color-success)]' : 'bg-[var(--bg-secondary)]' }`}> Validación
= 70 ? 'bg-[var(--color-success)]/10 text-[var(--color-success)]' : 'bg-[var(--bg-secondary)]' }`}> Análisis IA
= 100 ? 'bg-[var(--color-success)]/10 text-[var(--color-success)]' : 'bg-[var(--bg-secondary)]' }`}> Completo
)} {/* Simplified Results Stage */} {stage === 'completed' && results && (
{/* Success Header */}

¡Procesamiento Completado!

{results.validationMessage || 'Tus datos han sido procesados exitosamente'}

{results.fileSizeMb && (
Archivo {results.fileFormat?.toUpperCase()} • {results.fileSizeMb.toFixed(2)} MB {results.estimatedProcessingTime && ` • ${results.estimatedProcessingTime}s procesamiento`}
)}
{/* Enhanced Stats Cards */}

{results.totalRecords || results.total_records}

Total Registros

{results.validRecords || results.valid_records}

Válidos

{results.invalidRecords || results.invalid_records || 0}

Inválidos

{results.productsIdentified}

Productos

{results.confidenceScore}%

Confianza

{results.businessModel === 'artisan' ? 'Artesanal' : results.businessModel === 'retail' ? 'Retail' : 'Híbrido'}

Modelo

{/* Additional Details from Backend */} {(results.detectedColumns?.length > 0 || results.recommendations?.length > 0) && (
{/* Detected Columns */} {results.detectedColumns?.length > 0 && (

Columnas Detectadas

{results.detectedColumns.map((column, index) => ( {column} ))}
)} {/* Backend Recommendations */} {results.recommendations?.length > 0 && (

Recomendaciones

    {results.recommendations.slice(0, 3).map((rec, index) => (
  • {rec}
  • ))}
)}
)}
)} {/* Error State */} {stage === 'error' && (

Error en el procesamiento

{currentMessage}

)}
); };