Start integrating the onboarding flow with backend 7
This commit is contained in:
306
frontend/src/hooks/business/onboarding/useOnboarding.ts
Normal file
306
frontend/src/hooks/business/onboarding/useOnboarding.ts
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
Reference in New Issue
Block a user