// frontend/src/utils/onboardingRouter.ts /** * Onboarding Router Utility * Determines user's next step based on their current progress */ import { onboardingService } from '../api/services/onboarding.service'; import type { UserProgress } from '../api/services/onboarding.service'; export type OnboardingStep = | 'user_registered' | 'bakery_registered' | 'sales_data_uploaded' | 'training_completed' | 'dashboard_accessible'; export type NextAction = | 'register' | 'login' | 'onboarding_bakery' | 'onboarding_data' | 'onboarding_training' | 'dashboard' | 'landing'; export interface RoutingDecision { nextAction: NextAction; currentStep: OnboardingStep | null; message?: string; canSkip?: boolean; completionPercentage: number; } export class OnboardingRouter { /** * Determine next action for authenticated user */ static async getNextActionForUser(userProgress?: UserProgress): Promise { try { const progress = userProgress || await onboardingService.getUserProgress(); // Check if user has fully completed onboarding or has high completion percentage const isFullyCompleted = progress.fully_completed || progress.completion_percentage >= 80; const currentStep = progress.current_step as OnboardingStep; let nextAction: NextAction; if (isFullyCompleted || currentStep === 'training_completed' || currentStep === 'dashboard_accessible') { // User can access dashboard nextAction = 'dashboard'; } else { // Use step-based routing nextAction = this.mapStepToAction(currentStep); } return { nextAction, currentStep, completionPercentage: progress.completion_percentage, message: this.getStepMessage(currentStep), canSkip: this.canSkipStep(currentStep) }; } catch (error) { console.error('Error getting user progress:', error); // Fallback logic when API fails return { nextAction: 'onboarding_bakery', currentStep: 'bakery_registered', completionPercentage: 0, message: 'Let\'s set up your bakery information' }; } } /** * Determine next action for unauthenticated user */ static getNextActionForGuest(): RoutingDecision { return { nextAction: 'landing', currentStep: null, completionPercentage: 0, message: 'Welcome to PanIA - AI for your bakery' }; } /** * Check if user can access dashboard with current progress */ static async canAccessDashboard(): Promise { try { const progress = await onboardingService.getUserProgress(); return progress.fully_completed || progress.completion_percentage >= 80; } catch (error) { console.error('Error checking dashboard access:', error); return false; } } /** * Get dynamic onboarding step within the flow */ static async getOnboardingStepFromProgress(): Promise<{ step: number; totalSteps: number; canProceed: boolean; }> { try { const progress = await onboardingService.getUserProgress(); const currentStep = progress.current_step as OnboardingStep; const stepMap: Record = { 'user_registered': 1, 'bakery_registered': 2, 'sales_data_uploaded': 3, 'training_completed': 4, 'dashboard_accessible': 5 }; const currentStepNumber = stepMap[currentStep] || 1; return { step: Math.max(currentStepNumber - 1, 1), // Convert to 1-based onboarding steps totalSteps: 4, // We have 4 onboarding steps (excluding user registration) canProceed: progress.completion_percentage > 0 }; } catch (error) { console.error('Error getting onboarding step:', error); return { step: 1, totalSteps: 4, canProceed: true }; } } /** * Update progress when user completes an action */ static async completeStep( step: OnboardingStep, data?: Record ): Promise { return await onboardingService.completeStep(step, data); } /** * Check if user has completed a specific step */ static async hasCompletedStep(step: OnboardingStep): Promise { try { const progress = await onboardingService.getUserProgress(); const stepStatus = progress.steps.find((s: any) => s.step_name === step); return stepStatus?.completed || false; } catch (error) { console.error(`Error checking step completion for ${step}:`, error); return false; } } /** * Get user-friendly message for current step */ private static getStepMessage(step: OnboardingStep): string { const messages: Record = { 'user_registered': 'Welcome! Your account has been created successfully.', 'bakery_registered': 'Let\'s set up your bakery information to get started.', 'sales_data_uploaded': 'Upload your historical sales data for better predictions.', 'training_completed': 'Great! Your AI model is ready. Welcome to your dashboard!', 'dashboard_accessible': 'Welcome back! You\'re ready to use your AI-powered dashboard.' }; return messages[step] || 'Continue setting up your account'; } /** * Map step to corresponding frontend action */ private static mapStepToAction(step: OnboardingStep): NextAction { const actionMap: Record = { 'user_registered': 'onboarding_bakery', 'bakery_registered': 'onboarding_bakery', 'sales_data_uploaded': 'onboarding_data', 'training_completed': 'dashboard', // ✅ Users can access dashboard when training is completed 'dashboard_accessible': 'dashboard' }; return actionMap[step] || 'onboarding_bakery'; } /** * Check if user can skip a specific step */ private static canSkipStep(step: OnboardingStep): boolean { // Define which steps can be skipped const skippableSteps: OnboardingStep[] = [ 'training_completed' // Users can access dashboard even if training is still in progress ]; return skippableSteps.includes(step); } } // Helper functions for components export const useOnboardingRouter = () => { return { getNextActionForUser: OnboardingRouter.getNextActionForUser, getNextActionForGuest: OnboardingRouter.getNextActionForGuest, canAccessDashboard: OnboardingRouter.canAccessDashboard, getOnboardingStepFromProgress: OnboardingRouter.getOnboardingStepFromProgress, completeStep: OnboardingRouter.completeStep, hasCompletedStep: OnboardingRouter.hasCompletedStep, }; };