Files
bakery-ia/frontend/src/hooks/business/onboarding/useOnboarding.ts

306 lines
9.7 KiB
TypeScript
Raw Normal View History

/**
* 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';
interface OnboardingActions {
// Navigation
nextStep: () => boolean;
previousStep: () => boolean;
goToStep: (stepIndex: number) => boolean;
// Data Management
updateStepData: (stepId: string, data: Partial<OnboardingData>) => void;
validateCurrentStep: () => string | null;
// Step-specific Actions
createTenant: (bakeryData: BakeryRegistration) => Promise<boolean>;
processSalesFile: (file: File, onProgress?: ProgressCallback) => Promise<boolean>;
generateInventorySuggestions: (productList: string[]) => Promise<ProductSuggestionResponse[] | null>;
createInventoryFromSuggestions: (suggestions: ProductSuggestionResponse[]) => Promise<boolean>;
importSalesData: (salesData: any, inventoryMapping: { [productName: string]: string }) => Promise<boolean>;
startTraining: (options?: {
products?: string[];
startDate?: string;
endDate?: string;
}) => Promise<boolean>;
// Completion
completeOnboarding: () => Promise<boolean>;
// Utilities
clearError: () => void;
reset: () => void;
}
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();
// Navigation actions
const nextStep = useCallback((): boolean => {
const validation = validateCurrentStep();
if (validation) {
return false;
}
if (flow.nextStep()) {
flow.markStepCompleted(flow.currentStep - 1);
return true;
}
return false;
}, [flow]);
const previousStep = useCallback((): boolean => {
return flow.previousStep();
}, [flow]);
const goToStep = useCallback((stepIndex: number): boolean => {
return flow.goToStep(stepIndex);
}, [flow]);
// 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) {
updateStepData('setup', { bakery: bakeryData });
}
return success;
}, [tenantCreation, updateStepData]);
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,
} satisfies ReturnType<typeof useOnboardingFlow> &
ReturnType<typeof useOnboardingData> &
{ [key: string]: any } &
OnboardingActions;
};