126 lines
3.5 KiB
TypeScript
126 lines
3.5 KiB
TypeScript
'use client';
|
|
|
|
import { OnboardingTemplate } from '@/templates/onboarding/OnboardingTemplate';
|
|
import { routes } from '@/lib/routing/RouteConfig';
|
|
import { completeOnboardingAction } from '@/app/actions/completeOnboardingAction';
|
|
import { generateAvatarsAction } from '@/app/actions/generateAvatarsAction';
|
|
import { useAuth } from '@/components/auth/AuthContext';
|
|
import { useState } from 'react';
|
|
import { PersonalInfo } from '@/components/onboarding/PersonalInfoStep';
|
|
import { AvatarInfo } from '@/components/onboarding/AvatarStep';
|
|
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
|
import { ViewData } from '@/lib/contracts/view-data/ViewData';
|
|
|
|
type OnboardingStep = 1 | 2;
|
|
|
|
interface FormErrors {
|
|
[key: string]: string | undefined;
|
|
firstName?: string;
|
|
lastName?: string;
|
|
displayName?: string;
|
|
country?: string;
|
|
facePhoto?: string;
|
|
avatar?: string;
|
|
submit?: string;
|
|
}
|
|
|
|
export function OnboardingWizardClient({ viewData: initialViewData }: Partial<ClientWrapperProps<ViewData>>) {
|
|
const { session } = useAuth();
|
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
const [step, setStep] = useState<OnboardingStep>(1);
|
|
const [errors, setErrors] = useState<FormErrors>({});
|
|
|
|
// Form state
|
|
const [personalInfo, setPersonalInfo] = useState<PersonalInfo>({
|
|
firstName: '',
|
|
lastName: '',
|
|
displayName: '',
|
|
country: '',
|
|
timezone: '',
|
|
});
|
|
|
|
const [avatarInfo, setAvatarInfo] = useState<AvatarInfo>({
|
|
facePhoto: null,
|
|
suitColor: 'blue',
|
|
generatedAvatars: [],
|
|
selectedAvatarIndex: null,
|
|
isGenerating: false,
|
|
isValidating: false,
|
|
});
|
|
|
|
const handleCompleteOnboarding = async (input: {
|
|
firstName: string;
|
|
lastName: string;
|
|
displayName: string;
|
|
country: string;
|
|
timezone?: string;
|
|
}) => {
|
|
setIsProcessing(true);
|
|
try {
|
|
const result = await completeOnboardingAction(input);
|
|
|
|
if (result.isErr()) {
|
|
setIsProcessing(false);
|
|
return { success: false, error: result.getError() };
|
|
}
|
|
|
|
window.location.href = routes.protected.dashboard;
|
|
return { success: true };
|
|
} catch (error) {
|
|
setIsProcessing(false);
|
|
return { success: false, error: 'Failed to complete onboarding' };
|
|
}
|
|
};
|
|
|
|
const handleGenerateAvatars = async (params: {
|
|
facePhotoData: string;
|
|
suitColor: string;
|
|
}) => {
|
|
if (!session?.user?.userId) {
|
|
return { success: false, error: 'Not authenticated' };
|
|
}
|
|
|
|
setIsProcessing(true);
|
|
try {
|
|
const result = await generateAvatarsAction({
|
|
userId: session.user.userId,
|
|
facePhotoData: params.facePhotoData,
|
|
suitColor: params.suitColor,
|
|
});
|
|
|
|
if (result.isErr()) {
|
|
setIsProcessing(false);
|
|
return { success: false, error: result.getError() };
|
|
}
|
|
|
|
const data = result.unwrap();
|
|
setIsProcessing(false);
|
|
return { success: true, data };
|
|
} catch (error) {
|
|
setIsProcessing(false);
|
|
return { success: false, error: 'Failed to generate avatars' };
|
|
}
|
|
};
|
|
|
|
return (
|
|
<OnboardingTemplate
|
|
viewData={{
|
|
onCompleted: () => {
|
|
window.location.href = routes.protected.dashboard;
|
|
},
|
|
onCompleteOnboarding: handleCompleteOnboarding,
|
|
onGenerateAvatars: handleGenerateAvatars,
|
|
isProcessing: isProcessing,
|
|
step,
|
|
setStep,
|
|
errors,
|
|
setErrors,
|
|
personalInfo,
|
|
setPersonalInfo,
|
|
avatarInfo,
|
|
setAvatarInfo,
|
|
}}
|
|
/>
|
|
);
|
|
}
|