Start integrating the onboarding flow with backend 12

This commit is contained in:
Urtzi Alfaro
2025-09-07 17:25:30 +02:00
parent 9005286ada
commit b73f3b4993
32 changed files with 3172 additions and 3087 deletions

View File

@@ -1,282 +1,87 @@
/**
* Main onboarding hook - orchestrates all focused onboarding hooks
* This is the primary hook that components should use
* Main onboarding hook - Clean, unified interface for components
* This is the primary hook that all components should use
*/
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuthUser } from '../../../stores/auth.store';
import { useCurrentTenant } from '../../../stores';
import { useOnboardingFlow } from './useOnboardingFlow';
import { useOnboardingData } from './useOnboardingData';
import { useTenantCreation } from './useTenantCreation';
import { useSalesProcessing } from './useSalesProcessing';
import { useInventorySetup } from './useInventorySetup';
import { useTrainingOrchestration } from './useTrainingOrchestration';
import type {
OnboardingData,
ProgressCallback,
ProductSuggestionResponse
} from './types';
import type { BakeryRegistration } from '../../../api';
import { useOnboardingStore } from './core/store';
import { useOnboardingActions } from './core/actions';
import { useTenantCreation } from './services/useTenantCreation';
import { useSalesProcessing } from './services/useSalesProcessing';
import { useInventorySetup } from './services/useInventorySetup';
import { useTrainingOrchestration } from './services/useTrainingOrchestration';
import { useProgressTracking } from './services/useProgressTracking';
import { useResumeLogic } from './services/useResumeLogic';
export const useOnboarding = () => {
const navigate = useNavigate();
const user = useAuthUser();
const currentTenant = useCurrentTenant();
// Focused hooks
const flow = useOnboardingFlow();
const data = useOnboardingData();
// Core store and actions
const store = useOnboardingStore();
const actions = useOnboardingActions();
// Service hooks for detailed state access
const tenantCreation = useTenantCreation();
const salesProcessing = useSalesProcessing();
const inventorySetup = useInventorySetup();
const trainingOrchestration = useTrainingOrchestration();
// Data management
const updateStepData = useCallback((stepId: string, stepData: Partial<OnboardingData>) => {
data.updateStepData(stepId, stepData);
}, [data]);
const validateCurrentStep = useCallback((): string | null => {
const currentStep = flow.getCurrentStep();
const validationResult = data.validateStep(currentStep.id);
// Also check for specific step validations
if (validationResult) return validationResult;
return null;
}, [flow, data]);
// Step-specific actions
const createTenant = useCallback(async (bakeryData: BakeryRegistration): Promise<boolean> => {
const success = await tenantCreation.createTenant(bakeryData);
if (success) {
// Store the bakery data with tenant creation success flag and tenant ID
updateStepData('setup', {
bakery: {
...bakeryData,
tenantCreated: true,
tenant_id: currentTenant?.id || 'created'
} as any // Type assertion to allow the additional properties
});
console.log('useOnboarding - Tenant created successfully, updated step data with tenant ID:', currentTenant?.id);
}
return success;
}, [tenantCreation, updateStepData, currentTenant]);
// Navigation actions
const nextStep = useCallback(async (): Promise<boolean> => {
try {
const currentStep = flow.getCurrentStep();
console.log('useOnboarding - nextStep called from step:', currentStep.id);
const validation = validateCurrentStep();
if (validation) {
console.log('useOnboarding - Validation failed:', validation);
return false;
}
// Handle step-specific actions before moving to next step
if (currentStep.id === 'setup') {
console.log('useOnboarding - Creating tenant before leaving setup step');
const allStepData = data.getAllStepData();
const bakeryData = allStepData?.setup?.bakery;
if (bakeryData && !tenantCreation.isSuccess) {
console.log('useOnboarding - Tenant data found, creating tenant:', bakeryData);
const tenantSuccess = await createTenant(bakeryData);
console.log('useOnboarding - Tenant creation result:', tenantSuccess);
if (!tenantSuccess) {
console.log('useOnboarding - Tenant creation failed, stopping navigation');
return false;
}
} else {
console.log('useOnboarding - No tenant data found or tenant already created');
}
}
if (flow.nextStep()) {
flow.markStepCompleted(flow.currentStep - 1);
return true;
}
return false;
} catch (error) {
console.error('useOnboarding - Error in nextStep:', error);
return false;
}
}, [flow, validateCurrentStep, data, createTenant, tenantCreation]);
const previousStep = useCallback((): boolean => {
return flow.previousStep();
}, [flow]);
const goToStep = useCallback((stepIndex: number): boolean => {
return flow.goToStep(stepIndex);
}, [flow]);
const processSalesFile = useCallback(async (
file: File,
onProgress?: ProgressCallback
): Promise<boolean> => {
const result = await salesProcessing.processFile(file, onProgress);
if (result.success) {
updateStepData('sales-validation', {
files: { salesData: file },
processingStage: 'completed',
processingResults: result.validationResults,
suggestions: result.suggestions || []
});
}
return result.success;
}, [salesProcessing, updateStepData]);
const generateInventorySuggestions = useCallback(async (
productList: string[]
): Promise<ProductSuggestionResponse[] | null> => {
return salesProcessing.generateSuggestions(productList);
}, [salesProcessing]);
const createInventoryFromSuggestions = useCallback(async (
suggestions: ProductSuggestionResponse[]
): Promise<boolean> => {
const result = await inventorySetup.createInventoryFromSuggestions(suggestions);
if (result.success) {
updateStepData('inventory', {
inventoryItems: result.createdItems,
inventoryMapping: result.inventoryMapping,
inventoryConfigured: true,
});
}
return result.success;
}, [inventorySetup, updateStepData]);
const importSalesData = useCallback(async (
salesData: any,
inventoryMapping: { [productName: string]: string }
): Promise<boolean> => {
const result = await inventorySetup.importSalesData(salesData, inventoryMapping);
if (result.success) {
updateStepData('inventory', {
salesImportResult: {
success: result.success,
imported: true,
records_created: result.recordsCreated,
message: result.message,
},
});
}
return result.success;
}, [inventorySetup, updateStepData]);
const startTraining = useCallback(async (options?: {
products?: string[];
startDate?: string;
endDate?: string;
}): Promise<boolean> => {
// First validate training data requirements
const allStepData = data.getAllStepData();
const validation = await trainingOrchestration.validateTrainingData(allStepData);
if (!validation.isValid) {
return false;
}
const success = await trainingOrchestration.startTraining(options);
if (success) {
updateStepData('ml-training', {
trainingStatus: 'training',
trainingJob: trainingOrchestration.job,
trainingLogs: trainingOrchestration.logs,
});
}
return success;
}, [trainingOrchestration, data, updateStepData]);
const completeOnboarding = useCallback(async (): Promise<boolean> => {
// Mark final completion
updateStepData('completion', {
completionStats: {
totalProducts: data.data.processingResults?.unique_products || 0,
inventoryItems: data.data.inventoryItems?.length || 0,
suppliersConfigured: data.data.suppliers?.length || 0,
mlModelAccuracy: data.data.trainingMetrics?.accuracy || 0,
estimatedTimeSaved: '2-3 horas por día',
completionScore: 95,
},
});
flow.markStepCompleted(flow.steps.length - 1);
// Navigate to dashboard after completion
setTimeout(() => {
navigate('/app/dashboard');
}, 2000);
return true;
}, [data, flow, navigate, updateStepData]);
// Utilities
const clearError = useCallback(() => {
data.clearError();
tenantCreation.clearError();
salesProcessing.clearError();
inventorySetup.clearError();
trainingOrchestration.clearError();
}, [data, tenantCreation, salesProcessing, inventorySetup, trainingOrchestration]);
const reset = useCallback(() => {
flow.resetFlow();
data.resetData();
tenantCreation.reset();
salesProcessing.reset();
inventorySetup.reset();
trainingOrchestration.reset();
}, [flow, data, tenantCreation, salesProcessing, inventorySetup, trainingOrchestration]);
// Determine overall loading and error state
const isLoading = tenantCreation.isLoading ||
salesProcessing.isLoading ||
inventorySetup.isLoading ||
trainingOrchestration.isLoading;
const error = data.error?.message ||
tenantCreation.error ||
salesProcessing.error ||
inventorySetup.error ||
trainingOrchestration.error;
const progressTracking = useProgressTracking();
const resumeLogic = useResumeLogic();
return {
// State from flow management
currentStep: flow.currentStep,
steps: flow.steps,
progress: flow.getProgress(),
// Core state from store
currentStep: store.getCurrentStep(),
steps: store.steps,
data: store.data,
progress: store.getProgress(),
isLoading: store.isLoading,
error: store.error,
// State from data management
data: data.data,
allStepData: data.getAllStepData(),
// User context
user,
currentTenant,
// State from individual hooks
// Step data helpers
stepData: {
setup: store.getStepData('setup'),
'smart-inventory-setup': store.getStepData('smart-inventory-setup'),
suppliers: store.getStepData('suppliers'),
'ml-training': store.getStepData('ml-training'),
completion: store.getStepData('completion'),
},
// Service states (for components that need detailed service info)
tenantCreation: {
isLoading: tenantCreation.isLoading,
isSuccess: tenantCreation.isSuccess,
error: tenantCreation.error,
tenantData: tenantCreation.tenantData,
},
salesProcessing: {
isLoading: salesProcessing.isLoading,
error: salesProcessing.error,
stage: salesProcessing.stage,
progress: salesProcessing.progress,
currentMessage: salesProcessing.currentMessage,
validationResults: salesProcessing.validationResults,
suggestions: salesProcessing.suggestions,
},
inventorySetup: {
isLoading: inventorySetup.isLoading,
error: inventorySetup.error,
createdItems: inventorySetup.createdItems,
inventoryMapping: inventorySetup.inventoryMapping,
salesImportResult: inventorySetup.salesImportResult,
isInventoryConfigured: inventorySetup.isInventoryConfigured,
},
trainingOrchestration: {
isLoading: trainingOrchestration.isLoading,
error: trainingOrchestration.error,
status: trainingOrchestration.status,
progress: trainingOrchestration.progress,
currentStep: trainingOrchestration.currentStep,
@@ -285,27 +90,55 @@ export const useOnboarding = () => {
logs: trainingOrchestration.logs,
metrics: trainingOrchestration.metrics,
},
progressTracking: {
isLoading: progressTracking.isLoading,
error: progressTracking.error,
progress: progressTracking.progress,
isCompleted: progressTracking.isCompleted,
completionPercentage: progressTracking.completionPercentage,
isInitialized: progressTracking.isInitialized,
currentBackendStep: progressTracking.currentBackendStep,
},
resumeLogic: {
isCheckingResume: resumeLogic.isCheckingResume,
resumePoint: resumeLogic.resumePoint,
shouldResume: resumeLogic.shouldResume,
progress: resumeLogic.progress,
isCompleted: resumeLogic.isCompleted,
completionPercentage: resumeLogic.completionPercentage,
},
// Overall state
isLoading,
error,
user,
currentTenant,
// Actions
nextStep,
previousStep,
goToStep,
updateStepData,
validateCurrentStep,
createTenant,
processSalesFile,
generateInventorySuggestions,
createInventoryFromSuggestions,
importSalesData,
startTraining,
completeOnboarding,
clearError,
reset,
// Actions from the core actions hook
nextStep: actions.nextStep,
previousStep: actions.previousStep,
goToStep: actions.goToStep,
validateCurrentStep: actions.validateCurrentStep,
// Step data management
updateStepData: store.setStepData,
clearStepData: store.clearStepData,
// Step-specific actions
createTenant: actions.createTenant,
processSalesFile: actions.processSalesFile,
createInventoryFromSuggestions: actions.createInventoryFromSuggestions,
importSalesData: actions.importSalesData,
startTraining: actions.startTraining,
completeOnboarding: actions.completeOnboarding,
// Service-specific actions (for components that need direct service access)
generateSuggestions: salesProcessing.generateSuggestions,
addTrainingLog: trainingOrchestration.addLog,
validateTrainingData: trainingOrchestration.validateTrainingData,
// Resume actions
checkForSavedProgress: resumeLogic.checkForResume,
resumeFromSavedProgress: resumeLogic.resumeFlow,
// Utility actions
clearError: actions.clearError,
reset: actions.reset,
};
};