import React, { useState, useEffect } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Button } from '../../ui/Button'; import { Card, CardHeader, CardBody } from '../../ui/Card'; import { useAuth } from '../../../contexts/AuthContext'; import { useUserProgress, useMarkStepCompleted } from '../../../api/hooks/onboarding'; import { useTenantActions } from '../../../stores/tenant.store'; import { useTenantInitializer } from '../../../stores/useTenantInitializer'; import { WizardProvider, useWizardContext, BakeryType, DataSource } from './context'; import { BakeryTypeSelectionStep, DataSourceChoiceStep, RegisterTenantStep, UploadSalesDataStep, ProductionProcessesStep, MLTrainingStep, CompletionStep } from './steps'; // Import setup wizard steps import { SuppliersSetupStep, InventorySetupStep, RecipesSetupStep, QualitySetupStep, TeamSetupStep, ReviewSetupStep, } from '../setup-wizard/steps'; import { Building2 } from 'lucide-react'; interface StepConfig { id: string; title: string; description: string; component: React.ComponentType; isConditional?: boolean; condition?: (context: any) => boolean; } interface StepProps { onNext?: () => void; onPrevious?: () => void; onComplete?: (data?: any) => void; onUpdate?: (data?: any) => void; isFirstStep?: boolean; isLastStep?: boolean; initialData?: any; } const OnboardingWizardContent: React.FC = () => { const { t } = useTranslation(); const [searchParams] = useSearchParams(); const navigate = useNavigate(); const { user } = useAuth(); const wizardContext = useWizardContext(); // All possible steps with conditional visibility const ALL_STEPS: StepConfig[] = [ // Phase 1: Discovery { id: 'bakery-type-selection', title: t('onboarding:steps.bakery_type.title', 'Tipo de Panadería'), description: t('onboarding:steps.bakery_type.description', 'Selecciona tu tipo de negocio'), component: BakeryTypeSelectionStep, }, { id: 'data-source-choice', title: t('onboarding:steps.data_source.title', 'Método de Configuración'), description: t('onboarding:steps.data_source.description', 'Elige cómo configurar'), component: DataSourceChoiceStep, isConditional: true, condition: (ctx) => ctx.state.bakeryType !== null, }, // Phase 2: Core Setup { id: 'setup', title: t('onboarding:steps.setup.title', 'Registrar Panadería'), description: t('onboarding:steps.setup.description', 'Información básica'), component: RegisterTenantStep, isConditional: true, condition: (ctx) => ctx.state.dataSource !== null, }, // Phase 2a: AI-Assisted Path { id: 'smart-inventory-setup', title: t('onboarding:steps.smart_inventory.title', 'Subir Datos de Ventas'), description: t('onboarding:steps.smart_inventory.description', 'Configuración con IA'), component: UploadSalesDataStep, isConditional: true, condition: (ctx) => ctx.state.dataSource === 'ai-assisted', }, // Phase 2b: Core Data Entry { id: 'suppliers-setup', title: t('onboarding:steps.suppliers.title', 'Proveedores'), description: t('onboarding:steps.suppliers.description', 'Configura tus proveedores'), component: SuppliersSetupStep, isConditional: true, condition: (ctx) => ctx.state.dataSource !== null, }, { id: 'inventory-setup', title: t('onboarding:steps.inventory.title', 'Inventario'), description: t('onboarding:steps.inventory.description', 'Productos e ingredientes'), component: InventorySetupStep, isConditional: true, condition: (ctx) => ctx.state.dataSource !== null, }, { id: 'recipes-setup', title: t('onboarding:steps.recipes.title', 'Recetas'), description: t('onboarding:steps.recipes.description', 'Recetas de producción'), component: RecipesSetupStep, isConditional: true, condition: (ctx) => ctx.state.bakeryType === 'production' || ctx.state.bakeryType === 'mixed', }, { id: 'production-processes', title: t('onboarding:steps.processes.title', 'Procesos'), description: t('onboarding:steps.processes.description', 'Procesos de terminado'), component: ProductionProcessesStep, isConditional: true, condition: (ctx) => ctx.state.bakeryType === 'retail' || ctx.state.bakeryType === 'mixed', }, // Phase 3: Advanced Features (Optional) { id: 'quality-setup', title: t('onboarding:steps.quality.title', 'Calidad'), description: t('onboarding:steps.quality.description', 'Estándares de calidad'), component: QualitySetupStep, isConditional: true, condition: (ctx) => ctx.state.dataSource !== null, }, { id: 'team-setup', title: t('onboarding:steps.team.title', 'Equipo'), description: t('onboarding:steps.team.description', 'Miembros del equipo'), component: TeamSetupStep, isConditional: true, condition: (ctx) => ctx.state.dataSource !== null, }, // Phase 4: ML & Finalization { id: 'ml-training', title: t('onboarding:steps.ml_training.title', 'Entrenamiento IA'), description: t('onboarding:steps.ml_training.description', 'Modelo personalizado'), component: MLTrainingStep, isConditional: true, condition: (ctx) => ctx.state.inventoryCompleted || ctx.state.aiAnalysisComplete, }, { id: 'setup-review', title: t('onboarding:steps.review.title', 'Revisión'), description: t('onboarding:steps.review.description', 'Confirma tu configuración'), component: ReviewSetupStep, isConditional: true, condition: (ctx) => ctx.state.dataSource !== null, }, { id: 'completion', title: t('onboarding:steps.completion.title', 'Completado'), description: t('onboarding:steps.completion.description', '¡Todo listo!'), component: CompletionStep, }, ]; // Filter visible steps based on wizard context const getVisibleSteps = (): StepConfig[] => { return ALL_STEPS.filter(step => { if (!step.isConditional) return true; if (!step.condition) return true; return step.condition(wizardContext); }); }; const VISIBLE_STEPS = getVisibleSteps(); const isNewTenant = searchParams.get('new') === 'true'; const [currentStepIndex, setCurrentStepIndex] = useState(0); const [isInitialized, setIsInitialized] = useState(isNewTenant); useTenantInitializer(); const { data: userProgress, isLoading: isLoadingProgress, error: progressError } = useUserProgress( user?.id || '', { enabled: !!user?.id } ); const markStepCompleted = useMarkStepCompleted(); const { setCurrentTenant } = useTenantActions(); const [autoCompletionAttempted, setAutoCompletionAttempted] = React.useState(false); // Auto-complete user_registered step useEffect(() => { if (userProgress && user?.id && !autoCompletionAttempted && !markStepCompleted.isPending) { const userRegisteredStep = userProgress.steps.find(s => s.step_name === 'user_registered'); if (!userRegisteredStep?.completed) { console.log('🔄 Auto-completing user_registered step for new user...'); setAutoCompletionAttempted(true); const existingData = userRegisteredStep?.data || {}; markStepCompleted.mutate({ userId: user.id, stepName: 'user_registered', data: { ...existingData, auto_completed: true, completed_at: new Date().toISOString(), source: 'onboarding_wizard_auto_completion' } }, { onSuccess: () => console.log('✅ user_registered step auto-completed successfully'), onError: (error) => { console.error('❌ Failed to auto-complete user_registered step:', error); setAutoCompletionAttempted(false); } }); } } }, [userProgress, user?.id, autoCompletionAttempted, markStepCompleted.isPending]); // Initialize step index based on backend progress useEffect(() => { if (isNewTenant) return; if (userProgress && !isInitialized) { console.log('🔄 Initializing onboarding progress:', userProgress); const userRegisteredStep = userProgress.steps.find(s => s.step_name === 'user_registered'); if (!userRegisteredStep?.completed) { console.log('⏳ Waiting for user_registered step to be auto-completed...'); return; } let stepIndex = 0; if (isNewTenant) { console.log('🆕 New tenant creation - starting from first step'); stepIndex = 0; } else { const currentStepFromBackend = userProgress.current_step; stepIndex = VISIBLE_STEPS.findIndex(step => step.id === currentStepFromBackend); console.log(`🎯 Backend current step: "${currentStepFromBackend}", found at index: ${stepIndex}`); if (stepIndex === -1) { for (let i = 0; i < VISIBLE_STEPS.length; i++) { const step = VISIBLE_STEPS[i]; const stepProgress = userProgress.steps.find(s => s.step_name === step.id); if (!stepProgress?.completed) { stepIndex = i; console.log(`📍 Found first incomplete step: "${step.id}" at index ${i}`); break; } } if (stepIndex === -1) { stepIndex = VISIBLE_STEPS.length - 1; console.log('✅ All steps completed, going to last step'); } } const firstIncompleteStepIndex = VISIBLE_STEPS.findIndex(step => { const stepProgress = userProgress.steps.find(s => s.step_name === step.id); return !stepProgress?.completed; }); if (firstIncompleteStepIndex !== -1 && stepIndex > firstIncompleteStepIndex) { console.log(`🚫 User trying to skip ahead. Redirecting to first incomplete step at index ${firstIncompleteStepIndex}`); stepIndex = firstIncompleteStepIndex; } } console.log(`🎯 Final step index: ${stepIndex} ("${VISIBLE_STEPS[stepIndex]?.id}")`); if (stepIndex !== currentStepIndex) { setCurrentStepIndex(stepIndex); } setIsInitialized(true); } }, [userProgress, isInitialized, currentStepIndex, isNewTenant, VISIBLE_STEPS]); // Recalculate visible steps when wizard context changes useEffect(() => { const newVisibleSteps = getVisibleSteps(); // If current step is no longer visible, move to next visible step const currentStep = VISIBLE_STEPS[currentStepIndex]; if (currentStep && !newVisibleSteps.find(s => s.id === currentStep.id)) { setCurrentStepIndex(0); // Reset to first visible step } }, [wizardContext.state]); const currentStep = VISIBLE_STEPS[currentStepIndex]; const handleStepComplete = async (data?: any) => { if (!user?.id) { console.error('User ID not available'); return; } if (markStepCompleted.isPending) { console.warn(`⚠️ Step completion already in progress for "${currentStep.id}", skipping duplicate call`); return; } console.log(`🎯 Completing step: "${currentStep.id}" with data:`, data); try { // Update wizard context based on step if (currentStep.id === 'bakery-type-selection' && data?.bakeryType) { wizardContext.updateBakeryType(data.bakeryType as BakeryType); } if (currentStep.id === 'data-source-choice' && data?.dataSource) { wizardContext.updateDataSource(data.dataSource as DataSource); } if (currentStep.id === 'smart-inventory-setup' && data?.aiSuggestions) { wizardContext.updateAISuggestions(data.aiSuggestions); wizardContext.setAIAnalysisComplete(true); } if (currentStep.id === 'inventory-setup') { wizardContext.markStepComplete('inventoryCompleted'); } if (currentStep.id === 'setup' && data?.tenant) { setCurrentTenant(data.tenant); } // Mark step as completed in backend console.log(`📤 Sending API request to complete step: "${currentStep.id}"`); await markStepCompleted.mutateAsync({ userId: user.id, stepName: currentStep.id, data }); console.log(`✅ Successfully completed step: "${currentStep.id}"`); // Special handling for smart-inventory-setup if (currentStep.id === 'smart-inventory-setup' && data?.shouldAutoCompleteSuppliers) { try { console.log('🔄 Auto-completing suppliers step...'); await markStepCompleted.mutateAsync({ userId: user.id, stepName: 'suppliers', data: { auto_completed: true, completed_at: new Date().toISOString(), source: 'inventory_creation_auto_completion', } }); console.log('✅ Suppliers step auto-completed successfully'); } catch (supplierError) { console.warn('⚠️ Could not auto-complete suppliers step:', supplierError); } } if (currentStep.id === 'completion') { wizardContext.resetWizard(); navigate(isNewTenant ? '/app/dashboard' : '/app'); } else { if (currentStepIndex < VISIBLE_STEPS.length - 1) { setCurrentStepIndex(currentStepIndex + 1); } } } catch (error: any) { console.error(`❌ Error completing step "${currentStep.id}":`, error); const errorMessage = error?.response?.data?.detail || error?.message || 'Unknown error'; alert(`${t('onboarding:errors.step_failed', 'Error al completar paso')} "${currentStep.title}": ${errorMessage}`); } }; const handleStepUpdate = (data?: any) => { // Handle intermediate updates without marking step complete if (currentStep.id === 'bakery-type-selection' && data?.bakeryType) { wizardContext.updateBakeryType(data.bakeryType as BakeryType); } if (currentStep.id === 'data-source-choice' && data?.dataSource) { wizardContext.updateDataSource(data.dataSource as DataSource); } }; // Show loading state if (!isNewTenant && (isLoadingProgress || !isInitialized)) { return (

{t('common:loading', 'Cargando tu progreso...')}

); } // Show error state if (!isNewTenant && progressError) { return (

{t('onboarding:errors.network_error', 'Error al cargar progreso')}

{t('onboarding:errors.try_again', 'No pudimos cargar tu progreso. Puedes continuar desde el inicio.')}

); } const StepComponent = currentStep.component; const progressPercentage = isNewTenant ? ((currentStepIndex + 1) / VISIBLE_STEPS.length) * 100 : userProgress?.completion_percentage || ((currentStepIndex + 1) / VISIBLE_STEPS.length) * 100; return (
{/* Progress Header */}

{isNewTenant ? t('onboarding:wizard.title_new', 'Nueva Panadería') : t('onboarding:wizard.title', 'Configuración Inicial')}

{t('onboarding:wizard.subtitle', 'Configura tu sistema paso a paso')}

{t('onboarding:wizard.progress.step_of', 'Paso {{current}} de {{total}}', { current: currentStepIndex + 1, total: VISIBLE_STEPS.length })}
{Math.round(progressPercentage)}% {t('onboarding:wizard.progress.completed', 'completado')}
{/* Progress Bar */}
{/* Step Content */}
{currentStepIndex + 1}

{currentStep.title}

{currentStep.description}

{}} onPrevious={() => {}} onComplete={handleStepComplete} onUpdate={handleStepUpdate} isFirstStep={currentStepIndex === 0} isLastStep={currentStepIndex === VISIBLE_STEPS.length - 1} />
); }; export const UnifiedOnboardingWizard: React.FC = () => { return ( ); }; export default UnifiedOnboardingWizard;