2025-09-05 17:49:48 +02:00
|
|
|
/**
|
|
|
|
|
* Onboarding React Query hooks
|
|
|
|
|
*/
|
|
|
|
|
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
|
|
|
|
import { onboardingService } from '../services/onboarding';
|
|
|
|
|
import { UserProgress, UpdateStepRequest } from '../types/onboarding';
|
|
|
|
|
import { ApiError } from '../client';
|
|
|
|
|
|
|
|
|
|
// Query Keys
|
|
|
|
|
export const onboardingKeys = {
|
|
|
|
|
all: ['onboarding'] as const,
|
|
|
|
|
progress: (userId: string) => [...onboardingKeys.all, 'progress', userId] as const,
|
|
|
|
|
steps: () => [...onboardingKeys.all, 'steps'] as const,
|
|
|
|
|
stepDetail: (stepName: string) => [...onboardingKeys.steps(), stepName] as const,
|
|
|
|
|
} as const;
|
|
|
|
|
|
|
|
|
|
// Queries
|
|
|
|
|
export const useUserProgress = (
|
|
|
|
|
userId: string,
|
|
|
|
|
options?: Omit<UseQueryOptions<UserProgress, ApiError>, 'queryKey' | 'queryFn'>
|
|
|
|
|
) => {
|
|
|
|
|
return useQuery<UserProgress, ApiError>({
|
|
|
|
|
queryKey: onboardingKeys.progress(userId),
|
|
|
|
|
queryFn: () => onboardingService.getUserProgress(userId),
|
|
|
|
|
enabled: !!userId,
|
|
|
|
|
staleTime: 30 * 1000, // 30 seconds
|
|
|
|
|
...options,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useAllSteps = (
|
|
|
|
|
options?: Omit<UseQueryOptions<Array<{
|
|
|
|
|
name: string;
|
|
|
|
|
description: string;
|
|
|
|
|
dependencies: string[];
|
|
|
|
|
estimated_time_minutes: number;
|
|
|
|
|
}>, ApiError>, 'queryKey' | 'queryFn'>
|
|
|
|
|
) => {
|
|
|
|
|
return useQuery<Array<{
|
|
|
|
|
name: string;
|
|
|
|
|
description: string;
|
|
|
|
|
dependencies: string[];
|
|
|
|
|
estimated_time_minutes: number;
|
|
|
|
|
}>, ApiError>({
|
|
|
|
|
queryKey: onboardingKeys.steps(),
|
|
|
|
|
queryFn: () => onboardingService.getAllSteps(),
|
|
|
|
|
staleTime: 10 * 60 * 1000, // 10 minutes
|
|
|
|
|
...options,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useStepDetails = (
|
|
|
|
|
stepName: string,
|
|
|
|
|
options?: Omit<UseQueryOptions<{
|
|
|
|
|
name: string;
|
|
|
|
|
description: string;
|
|
|
|
|
dependencies: string[];
|
|
|
|
|
estimated_time_minutes: number;
|
|
|
|
|
}, ApiError>, 'queryKey' | 'queryFn'>
|
|
|
|
|
) => {
|
|
|
|
|
return useQuery<{
|
|
|
|
|
name: string;
|
|
|
|
|
description: string;
|
|
|
|
|
dependencies: string[];
|
|
|
|
|
estimated_time_minutes: number;
|
|
|
|
|
}, ApiError>({
|
|
|
|
|
queryKey: onboardingKeys.stepDetail(stepName),
|
|
|
|
|
queryFn: () => onboardingService.getStepDetails(stepName),
|
|
|
|
|
enabled: !!stepName,
|
|
|
|
|
staleTime: 10 * 60 * 1000, // 10 minutes
|
|
|
|
|
...options,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Mutations
|
|
|
|
|
export const useUpdateStep = (
|
|
|
|
|
options?: UseMutationOptions<UserProgress, ApiError, { userId: string; stepData: UpdateStepRequest }>
|
|
|
|
|
) => {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
|
|
|
|
|
return useMutation<UserProgress, ApiError, { userId: string; stepData: UpdateStepRequest }>({
|
|
|
|
|
mutationFn: ({ userId, stepData }) => onboardingService.updateStep(userId, stepData),
|
|
|
|
|
onSuccess: (data, { userId }) => {
|
|
|
|
|
// Update progress cache
|
|
|
|
|
queryClient.setQueryData(onboardingKeys.progress(userId), data);
|
|
|
|
|
},
|
|
|
|
|
...options,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useMarkStepCompleted = (
|
|
|
|
|
options?: UseMutationOptions<
|
2025-09-29 07:54:25 +02:00
|
|
|
UserProgress,
|
|
|
|
|
ApiError,
|
2025-09-05 17:49:48 +02:00
|
|
|
{ userId: string; stepName: string; data?: Record<string, any> }
|
|
|
|
|
>
|
|
|
|
|
) => {
|
|
|
|
|
const queryClient = useQueryClient();
|
2025-09-29 07:54:25 +02:00
|
|
|
|
2025-09-05 17:49:48 +02:00
|
|
|
return useMutation<
|
2025-09-29 07:54:25 +02:00
|
|
|
UserProgress,
|
|
|
|
|
ApiError,
|
2025-09-05 17:49:48 +02:00
|
|
|
{ userId: string; stepName: string; data?: Record<string, any> }
|
|
|
|
|
>({
|
2025-09-29 07:54:25 +02:00
|
|
|
mutationFn: ({ userId, stepName, data }) =>
|
2025-09-05 17:49:48 +02:00
|
|
|
onboardingService.markStepCompleted(userId, stepName, data),
|
|
|
|
|
onSuccess: (data, { userId }) => {
|
2025-09-29 07:54:25 +02:00
|
|
|
// Update progress cache with new data
|
2025-09-05 17:49:48 +02:00
|
|
|
queryClient.setQueryData(onboardingKeys.progress(userId), data);
|
2025-09-29 07:54:25 +02:00
|
|
|
|
|
|
|
|
// Invalidate the query to ensure fresh data on next access
|
|
|
|
|
queryClient.invalidateQueries({ queryKey: onboardingKeys.progress(userId) });
|
|
|
|
|
},
|
|
|
|
|
onError: (error, { userId, stepName }) => {
|
|
|
|
|
console.error(`Failed to complete step ${stepName} for user ${userId}:`, error);
|
|
|
|
|
|
|
|
|
|
// Invalidate queries on error to ensure we get fresh data
|
|
|
|
|
queryClient.invalidateQueries({ queryKey: onboardingKeys.progress(userId) });
|
2025-09-05 17:49:48 +02:00
|
|
|
},
|
2025-09-29 07:54:25 +02:00
|
|
|
// Prevent duplicate requests by using the step name as a mutation key
|
|
|
|
|
mutationKey: (variables) => ['markStepCompleted', variables?.userId, variables?.stepName],
|
2025-09-05 17:49:48 +02:00
|
|
|
...options,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useResetProgress = (
|
|
|
|
|
options?: UseMutationOptions<UserProgress, ApiError, string>
|
|
|
|
|
) => {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
|
|
|
|
|
return useMutation<UserProgress, ApiError, string>({
|
|
|
|
|
mutationFn: (userId: string) => onboardingService.resetProgress(userId),
|
|
|
|
|
onSuccess: (data, userId) => {
|
|
|
|
|
// Update progress cache
|
|
|
|
|
queryClient.setQueryData(onboardingKeys.progress(userId), data);
|
|
|
|
|
},
|
|
|
|
|
...options,
|
|
|
|
|
});
|
|
|
|
|
};
|