2025-09-05 22:46:28 +02:00
|
|
|
/**
|
|
|
|
|
* Main onboarding hook - orchestrates all focused onboarding hooks
|
|
|
|
|
* This is the primary hook that 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';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const useOnboarding = () => {
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
const user = useAuthUser();
|
|
|
|
|
const currentTenant = useCurrentTenant();
|
|
|
|
|
|
|
|
|
|
// Focused hooks
|
|
|
|
|
const flow = useOnboardingFlow();
|
|
|
|
|
const data = useOnboardingData();
|
|
|
|
|
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) {
|
2025-09-06 19:40:47 +02:00
|
|
|
// 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);
|
2025-09-05 22:46:28 +02:00
|
|
|
}
|
|
|
|
|
return success;
|
2025-09-06 19:40:47 +02:00
|
|
|
}, [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]);
|
2025-09-05 22:46:28 +02:00
|
|
|
|
|
|
|
|
const processSalesFile = useCallback(async (
|
|
|
|
|
file: File,
|
|
|
|
|
onProgress?: ProgressCallback
|
|
|
|
|
): Promise<boolean> => {
|
|
|
|
|
const result = await salesProcessing.processFile(file, onProgress);
|
|
|
|
|
if (result.success) {
|
|
|
|
|
updateStepData('data-processing', {
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
// State from flow management
|
|
|
|
|
currentStep: flow.currentStep,
|
|
|
|
|
steps: flow.steps,
|
|
|
|
|
progress: flow.getProgress(),
|
|
|
|
|
|
|
|
|
|
// State from data management
|
|
|
|
|
data: data.data,
|
|
|
|
|
allStepData: data.getAllStepData(),
|
|
|
|
|
|
|
|
|
|
// State from individual hooks
|
|
|
|
|
tenantCreation: {
|
|
|
|
|
isLoading: tenantCreation.isLoading,
|
|
|
|
|
isSuccess: tenantCreation.isSuccess,
|
|
|
|
|
},
|
|
|
|
|
salesProcessing: {
|
|
|
|
|
stage: salesProcessing.stage,
|
|
|
|
|
progress: salesProcessing.progress,
|
|
|
|
|
currentMessage: salesProcessing.currentMessage,
|
|
|
|
|
validationResults: salesProcessing.validationResults,
|
|
|
|
|
suggestions: salesProcessing.suggestions,
|
|
|
|
|
},
|
|
|
|
|
inventorySetup: {
|
|
|
|
|
createdItems: inventorySetup.createdItems,
|
|
|
|
|
inventoryMapping: inventorySetup.inventoryMapping,
|
|
|
|
|
salesImportResult: inventorySetup.salesImportResult,
|
|
|
|
|
isInventoryConfigured: inventorySetup.isInventoryConfigured,
|
|
|
|
|
},
|
|
|
|
|
trainingOrchestration: {
|
|
|
|
|
status: trainingOrchestration.status,
|
|
|
|
|
progress: trainingOrchestration.progress,
|
|
|
|
|
currentStep: trainingOrchestration.currentStep,
|
|
|
|
|
estimatedTimeRemaining: trainingOrchestration.estimatedTimeRemaining,
|
|
|
|
|
job: trainingOrchestration.job,
|
|
|
|
|
logs: trainingOrchestration.logs,
|
|
|
|
|
metrics: trainingOrchestration.metrics,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Overall state
|
|
|
|
|
isLoading,
|
|
|
|
|
error,
|
|
|
|
|
user,
|
|
|
|
|
currentTenant,
|
|
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
nextStep,
|
|
|
|
|
previousStep,
|
|
|
|
|
goToStep,
|
|
|
|
|
updateStepData,
|
|
|
|
|
validateCurrentStep,
|
|
|
|
|
createTenant,
|
|
|
|
|
processSalesFile,
|
|
|
|
|
generateInventorySuggestions,
|
|
|
|
|
createInventoryFromSuggestions,
|
|
|
|
|
importSalesData,
|
|
|
|
|
startTraining,
|
|
|
|
|
completeOnboarding,
|
|
|
|
|
clearError,
|
|
|
|
|
reset,
|
2025-09-06 19:40:47 +02:00
|
|
|
};
|
2025-09-05 22:46:28 +02:00
|
|
|
};
|