Add onboardin steps improvements
This commit is contained in:
@@ -9,6 +9,16 @@ export { useData } from './useData';
|
||||
export { useTraining } from './useTraining';
|
||||
export { useForecast } from './useForecast';
|
||||
export { useNotification } from './useNotification';
|
||||
export { useOnboarding, useOnboardingStep } from './useOnboarding';
|
||||
|
||||
// Import hooks for combined usage
|
||||
import { useAuth } from './useAuth';
|
||||
import { useTenant } from './useTenant';
|
||||
import { useData } from './useData';
|
||||
import { useTraining } from './useTraining';
|
||||
import { useForecast } from './useForecast';
|
||||
import { useNotification } from './useNotification';
|
||||
import { useOnboarding } from './useOnboarding';
|
||||
|
||||
// Combined hook for common operations
|
||||
export const useApiHooks = () => {
|
||||
@@ -18,6 +28,7 @@ export const useApiHooks = () => {
|
||||
const training = useTraining();
|
||||
const forecast = useForecast();
|
||||
const notification = useNotification();
|
||||
const onboarding = useOnboarding();
|
||||
|
||||
return {
|
||||
auth,
|
||||
@@ -26,5 +37,6 @@ export const useApiHooks = () => {
|
||||
training,
|
||||
forecast,
|
||||
notification,
|
||||
onboarding,
|
||||
};
|
||||
};
|
||||
194
frontend/src/api/hooks/useOnboarding.ts
Normal file
194
frontend/src/api/hooks/useOnboarding.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
// frontend/src/api/hooks/useOnboarding.ts
|
||||
/**
|
||||
* Onboarding Hook
|
||||
* React hook for managing user onboarding flow and progress
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { onboardingService } from '../services/onboarding.service';
|
||||
import type { UserProgress, UpdateStepRequest } from '../services/onboarding.service';
|
||||
|
||||
export interface UseOnboardingReturn {
|
||||
progress: UserProgress | null;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
currentStep: string | null;
|
||||
nextStep: string | null;
|
||||
completionPercentage: number;
|
||||
isFullyComplete: boolean;
|
||||
|
||||
// Actions
|
||||
updateStep: (data: UpdateStepRequest) => Promise<void>;
|
||||
completeStep: (stepName: string, data?: Record<string, any>) => Promise<void>;
|
||||
resetStep: (stepName: string) => Promise<void>;
|
||||
getNextStep: () => Promise<string>;
|
||||
completeOnboarding: () => Promise<void>;
|
||||
canAccessStep: (stepName: string) => Promise<boolean>;
|
||||
refreshProgress: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const useOnboarding = (): UseOnboardingReturn => {
|
||||
const [progress, setProgress] = useState<UserProgress | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Derived state
|
||||
const currentStep = progress?.current_step || null;
|
||||
const nextStep = progress?.next_step || null;
|
||||
const completionPercentage = progress?.completion_percentage || 0;
|
||||
const isFullyComplete = progress?.fully_completed || false;
|
||||
|
||||
// Load initial progress
|
||||
const loadProgress = async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const userProgress = await onboardingService.getUserProgress();
|
||||
setProgress(userProgress);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Failed to load onboarding progress';
|
||||
setError(message);
|
||||
console.error('Onboarding progress load error:', err);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Update step
|
||||
const updateStep = async (data: UpdateStepRequest) => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const updatedProgress = await onboardingService.updateStep(data);
|
||||
setProgress(updatedProgress);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Failed to update step';
|
||||
setError(message);
|
||||
throw err; // Re-throw so calling component can handle it
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Complete step with data
|
||||
const completeStep = async (stepName: string, data?: Record<string, any>) => {
|
||||
await updateStep({
|
||||
step_name: stepName,
|
||||
completed: true,
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
// Reset step
|
||||
const resetStep = async (stepName: string) => {
|
||||
await updateStep({
|
||||
step_name: stepName,
|
||||
completed: false
|
||||
});
|
||||
};
|
||||
|
||||
// Get next step
|
||||
const getNextStep = async (): Promise<string> => {
|
||||
try {
|
||||
const result = await onboardingService.getNextStep();
|
||||
return result.step;
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Failed to get next step';
|
||||
setError(message);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
// Complete entire onboarding
|
||||
const completeOnboarding = async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
await onboardingService.completeOnboarding();
|
||||
await loadProgress(); // Refresh progress after completion
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : 'Failed to complete onboarding';
|
||||
setError(message);
|
||||
throw err;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if user can access step
|
||||
const canAccessStep = async (stepName: string): Promise<boolean> => {
|
||||
try {
|
||||
const result = await onboardingService.canAccessStep(stepName);
|
||||
return result.can_access;
|
||||
} catch (err) {
|
||||
console.error('Can access step check failed:', err);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Refresh progress
|
||||
const refreshProgress = async () => {
|
||||
await loadProgress();
|
||||
};
|
||||
|
||||
// Load progress on mount
|
||||
useEffect(() => {
|
||||
loadProgress();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
progress,
|
||||
isLoading,
|
||||
error,
|
||||
currentStep,
|
||||
nextStep,
|
||||
completionPercentage,
|
||||
isFullyComplete,
|
||||
updateStep,
|
||||
completeStep,
|
||||
resetStep,
|
||||
getNextStep,
|
||||
completeOnboarding,
|
||||
canAccessStep,
|
||||
refreshProgress,
|
||||
};
|
||||
};
|
||||
|
||||
// Helper hook for specific steps
|
||||
export const useOnboardingStep = (stepName: string) => {
|
||||
const onboarding = useOnboarding();
|
||||
|
||||
const stepStatus = onboarding.progress?.steps.find(
|
||||
step => step.step_name === stepName
|
||||
);
|
||||
|
||||
const isCompleted = stepStatus?.completed || false;
|
||||
const stepData = stepStatus?.data || {};
|
||||
const completedAt = stepStatus?.completed_at;
|
||||
|
||||
const completeThisStep = async (data?: Record<string, any>) => {
|
||||
await onboarding.completeStep(stepName, data);
|
||||
};
|
||||
|
||||
const resetThisStep = async () => {
|
||||
await onboarding.resetStep(stepName);
|
||||
};
|
||||
|
||||
const canAccessThisStep = async (): Promise<boolean> => {
|
||||
return await onboarding.canAccessStep(stepName);
|
||||
};
|
||||
|
||||
return {
|
||||
...onboarding,
|
||||
stepName,
|
||||
isCompleted,
|
||||
stepData,
|
||||
completedAt,
|
||||
completeThisStep,
|
||||
resetThisStep,
|
||||
canAccessThisStep,
|
||||
};
|
||||
};
|
||||
@@ -28,6 +28,7 @@ export {
|
||||
useForecast,
|
||||
useNotification,
|
||||
useApiHooks,
|
||||
useOnboarding,
|
||||
} from './hooks';
|
||||
|
||||
// Export WebSocket functionality
|
||||
|
||||
@@ -11,6 +11,7 @@ import { DataService } from './data.service';
|
||||
import { TrainingService } from './training.service';
|
||||
import { ForecastingService } from './forecasting.service';
|
||||
import { NotificationService } from './notification.service';
|
||||
import { OnboardingService } from './onboarding.service';
|
||||
|
||||
// Create service instances
|
||||
export const authService = new AuthService();
|
||||
@@ -19,9 +20,10 @@ export const dataService = new DataService();
|
||||
export const trainingService = new TrainingService();
|
||||
export const forecastingService = new ForecastingService();
|
||||
export const notificationService = new NotificationService();
|
||||
export const onboardingService = new OnboardingService();
|
||||
|
||||
// Export the classes as well
|
||||
export { AuthService, TenantService, DataService, TrainingService, ForecastingService, NotificationService };
|
||||
export { AuthService, TenantService, DataService, TrainingService, ForecastingService, NotificationService, OnboardingService };
|
||||
|
||||
// Import base client
|
||||
export { apiClient } from '../client';
|
||||
@@ -37,6 +39,7 @@ export const api = {
|
||||
training: trainingService,
|
||||
forecasting: forecastingService,
|
||||
notification: notificationService,
|
||||
onboarding: onboardingService,
|
||||
} as const;
|
||||
|
||||
// Service status checking
|
||||
|
||||
92
frontend/src/api/services/onboarding.service.ts
Normal file
92
frontend/src/api/services/onboarding.service.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
// frontend/src/api/services/onboarding.service.ts
|
||||
/**
|
||||
* Onboarding Service
|
||||
* Handles user progress tracking and onboarding flow management
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client';
|
||||
|
||||
export interface OnboardingStepStatus {
|
||||
step_name: string;
|
||||
completed: boolean;
|
||||
completed_at?: string;
|
||||
data?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface UserProgress {
|
||||
user_id: string;
|
||||
steps: OnboardingStepStatus[];
|
||||
current_step: string;
|
||||
next_step?: string;
|
||||
completion_percentage: number;
|
||||
fully_completed: boolean;
|
||||
last_updated: string;
|
||||
}
|
||||
|
||||
export interface UpdateStepRequest {
|
||||
step_name: string;
|
||||
completed: boolean;
|
||||
data?: Record<string, any>;
|
||||
}
|
||||
|
||||
export class OnboardingService {
|
||||
private baseEndpoint = '/users/me/onboarding';
|
||||
|
||||
/**
|
||||
* Get user's current onboarding progress
|
||||
*/
|
||||
async getUserProgress(): Promise<UserProgress> {
|
||||
return apiClient.get(`${this.baseEndpoint}/progress`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific onboarding step
|
||||
*/
|
||||
async updateStep(data: UpdateStepRequest): Promise<UserProgress> {
|
||||
return apiClient.put(`${this.baseEndpoint}/step`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark step as completed with optional data
|
||||
*/
|
||||
async completeStep(stepName: string, data?: Record<string, any>): Promise<UserProgress> {
|
||||
return this.updateStep({
|
||||
step_name: stepName,
|
||||
completed: true,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a step (mark as incomplete)
|
||||
*/
|
||||
async resetStep(stepName: string): Promise<UserProgress> {
|
||||
return this.updateStep({
|
||||
step_name: stepName,
|
||||
completed: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next required step for user
|
||||
*/
|
||||
async getNextStep(): Promise<{ step: string; data?: Record<string, any> }> {
|
||||
return apiClient.get(`${this.baseEndpoint}/next-step`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete entire onboarding process
|
||||
*/
|
||||
async completeOnboarding(): Promise<{ success: boolean; message: string }> {
|
||||
return apiClient.post(`${this.baseEndpoint}/complete`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can access a specific step
|
||||
*/
|
||||
async canAccessStep(stepName: string): Promise<{ can_access: boolean; reason?: string }> {
|
||||
return apiClient.get(`${this.baseEndpoint}/can-access/${stepName}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const onboardingService = new OnboardingService();
|
||||
Reference in New Issue
Block a user