2025-09-05 17:49:48 +02:00
|
|
|
/**
|
|
|
|
|
* Onboarding Service - Mirror backend onboarding endpoints
|
2025-09-07 17:25:30 +02:00
|
|
|
* Frontend and backend step names now match directly!
|
2025-09-05 17:49:48 +02:00
|
|
|
*/
|
|
|
|
|
import { apiClient } from '../client';
|
|
|
|
|
import { UserProgress, UpdateStepRequest } from '../types/onboarding';
|
|
|
|
|
|
2025-09-07 17:25:30 +02:00
|
|
|
// Frontend step order for navigation (matches backend ONBOARDING_STEPS)
|
|
|
|
|
export const FRONTEND_STEP_ORDER = [
|
|
|
|
|
'setup', // Step 1: Basic bakery setup and tenant creation
|
|
|
|
|
'smart-inventory-setup', // Step 2: Sales data upload and inventory configuration
|
|
|
|
|
'suppliers', // Step 3: Suppliers configuration (optional)
|
|
|
|
|
'ml-training', // Step 4: AI model training
|
|
|
|
|
'completion' // Step 5: Onboarding completed
|
|
|
|
|
];
|
|
|
|
|
|
2025-09-05 17:49:48 +02:00
|
|
|
export class OnboardingService {
|
2025-09-06 19:40:47 +02:00
|
|
|
private readonly baseUrl = '/users/me/onboarding';
|
2025-09-05 17:49:48 +02:00
|
|
|
|
|
|
|
|
async getUserProgress(userId: string): Promise<UserProgress> {
|
2025-09-06 19:40:47 +02:00
|
|
|
// Backend uses current user from auth token, so userId parameter is ignored
|
|
|
|
|
return apiClient.get<UserProgress>(`${this.baseUrl}/progress`);
|
2025-09-05 17:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async updateStep(userId: string, stepData: UpdateStepRequest): Promise<UserProgress> {
|
2025-09-06 19:40:47 +02:00
|
|
|
// Backend uses current user from auth token, so userId parameter is ignored
|
|
|
|
|
return apiClient.put<UserProgress>(`${this.baseUrl}/step`, stepData);
|
2025-09-05 17:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async markStepCompleted(
|
|
|
|
|
userId: string,
|
|
|
|
|
stepName: string,
|
|
|
|
|
data?: Record<string, any>
|
|
|
|
|
): Promise<UserProgress> {
|
2025-09-06 19:40:47 +02:00
|
|
|
// Backend uses current user from auth token, so userId parameter is ignored
|
|
|
|
|
// Backend expects UpdateStepRequest format for completion
|
2025-09-07 22:54:14 +02:00
|
|
|
const requestBody = {
|
2025-09-05 17:49:48 +02:00
|
|
|
step_name: stepName,
|
2025-09-06 19:40:47 +02:00
|
|
|
completed: true,
|
2025-09-05 17:49:48 +02:00
|
|
|
data: data,
|
2025-09-07 22:54:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
console.log(`🔄 API call to mark step "${stepName}" as completed:`, requestBody);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const response = await apiClient.put<UserProgress>(`${this.baseUrl}/step`, requestBody);
|
|
|
|
|
console.log(`✅ Step "${stepName}" marked as completed successfully:`, response);
|
|
|
|
|
return response;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`❌ API error marking step "${stepName}" as completed:`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
2025-09-05 17:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async resetProgress(userId: string): Promise<UserProgress> {
|
2025-09-06 19:40:47 +02:00
|
|
|
// Note: Backend doesn't have a reset endpoint, this might need to be implemented
|
|
|
|
|
// For now, we'll throw an error
|
|
|
|
|
throw new Error('Reset progress functionality not implemented in backend');
|
2025-09-05 17:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getStepDetails(stepName: string): Promise<{
|
|
|
|
|
name: string;
|
|
|
|
|
description: string;
|
|
|
|
|
dependencies: string[];
|
|
|
|
|
estimated_time_minutes: number;
|
|
|
|
|
}> {
|
2025-09-06 19:40:47 +02:00
|
|
|
// This endpoint doesn't exist in backend, we'll need to implement it or mock it
|
|
|
|
|
throw new Error('getStepDetails functionality not implemented in backend');
|
2025-09-05 17:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getAllSteps(): Promise<Array<{
|
|
|
|
|
name: string;
|
|
|
|
|
description: string;
|
|
|
|
|
dependencies: string[];
|
|
|
|
|
estimated_time_minutes: number;
|
|
|
|
|
}>> {
|
2025-09-06 19:40:47 +02:00
|
|
|
// This endpoint doesn't exist in backend, we'll need to implement it or mock it
|
|
|
|
|
throw new Error('getAllSteps functionality not implemented in backend');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getNextStep(): Promise<{ step: string; completed?: boolean }> {
|
|
|
|
|
// This endpoint exists in backend
|
|
|
|
|
return apiClient.get(`${this.baseUrl}/next-step`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async canAccessStep(stepName: string): Promise<{ can_access: boolean; reason?: string }> {
|
|
|
|
|
// This endpoint exists in backend
|
|
|
|
|
return apiClient.get(`${this.baseUrl}/can-access/${stepName}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async completeOnboarding(): Promise<{ success: boolean; message: string }> {
|
|
|
|
|
// This endpoint exists in backend
|
|
|
|
|
return apiClient.post(`${this.baseUrl}/complete`);
|
2025-09-05 17:49:48 +02:00
|
|
|
}
|
2025-09-07 17:25:30 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper method to mark a step as completed (now direct mapping)
|
|
|
|
|
*/
|
|
|
|
|
async markStepAsCompleted(
|
|
|
|
|
stepId: string,
|
|
|
|
|
data?: Record<string, any>
|
|
|
|
|
): Promise<UserProgress> {
|
|
|
|
|
try {
|
|
|
|
|
return await this.markStepCompleted('', stepId, data);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`Error marking step ${stepId} as completed:`, error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper method to get the next step based on backend progress
|
|
|
|
|
*/
|
|
|
|
|
async getNextStepId(): Promise<string> {
|
|
|
|
|
try {
|
|
|
|
|
const result = await this.getNextStep();
|
|
|
|
|
return result.step || 'setup';
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error getting next step:', error);
|
|
|
|
|
return 'setup';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper method to determine which step the user should resume from
|
|
|
|
|
*/
|
|
|
|
|
async getResumeStep(): Promise<{ stepId: string; stepIndex: number }> {
|
|
|
|
|
try {
|
|
|
|
|
const progress = await this.getUserProgress('');
|
|
|
|
|
|
|
|
|
|
// If fully completed, go to completion
|
|
|
|
|
if (progress.fully_completed) {
|
|
|
|
|
return { stepId: 'completion', stepIndex: FRONTEND_STEP_ORDER.indexOf('completion') };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the current step from backend
|
|
|
|
|
const currentStep = progress.current_step;
|
|
|
|
|
|
|
|
|
|
// If current step is user_registered, start from setup
|
|
|
|
|
const resumeStep = currentStep === 'user_registered' ? 'setup' : currentStep;
|
|
|
|
|
|
|
|
|
|
// Find the step index in our frontend order
|
|
|
|
|
let stepIndex = FRONTEND_STEP_ORDER.indexOf(resumeStep);
|
|
|
|
|
if (stepIndex === -1) {
|
|
|
|
|
stepIndex = 0; // Default to first step
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { stepId: FRONTEND_STEP_ORDER[stepIndex], stepIndex };
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error determining resume step:', error);
|
|
|
|
|
return { stepId: 'setup', stepIndex: 0 };
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-05 17:49:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const onboardingService = new OnboardingService();
|