di usage in website
This commit is contained in:
@@ -22,7 +22,10 @@ import Button from '@/components/ui/Button';
|
||||
import Input from '@/components/ui/Input';
|
||||
import Heading from '@/components/ui/Heading';
|
||||
import CountrySelect from '@/components/ui/CountrySelect';
|
||||
import { useServices } from '@/lib/services/ServiceProvider';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
import { useCompleteOnboarding } from '@/hooks/onboarding/useCompleteOnboarding';
|
||||
import { useGenerateAvatars } from '@/hooks/onboarding/useGenerateAvatars';
|
||||
import { useValidateFacePhoto } from '@/hooks/onboarding/useValidateFacePhoto';
|
||||
|
||||
// ============================================================================
|
||||
// TYPES
|
||||
@@ -163,9 +166,8 @@ function StepIndicator({ currentStep }: { currentStep: number }) {
|
||||
export default function OnboardingWizard() {
|
||||
const router = useRouter();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const { onboardingService, sessionService } = useServices();
|
||||
const { session } = useAuth();
|
||||
const [step, setStep] = useState<OnboardingStep>(1);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [errors, setErrors] = useState<FormErrors>({});
|
||||
|
||||
// Form state
|
||||
@@ -270,6 +272,19 @@ export default function OnboardingWizard() {
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
const validateFacePhotoMutation = useValidateFacePhoto({
|
||||
onSuccess: () => {
|
||||
setAvatarInfo(prev => ({ ...prev, isValidating: false }));
|
||||
},
|
||||
onError: (error) => {
|
||||
setErrors(prev => ({
|
||||
...prev,
|
||||
facePhoto: error.message || 'Face validation failed'
|
||||
}));
|
||||
setAvatarInfo(prev => ({ ...prev, facePhoto: null, isValidating: false }));
|
||||
},
|
||||
});
|
||||
|
||||
const validateFacePhoto = async (photoData: string) => {
|
||||
setAvatarInfo(prev => ({ ...prev, isValidating: true }));
|
||||
setErrors(prev => {
|
||||
@@ -278,7 +293,7 @@ export default function OnboardingWizard() {
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await onboardingService.validateFacePhoto(photoData);
|
||||
const result = await validateFacePhotoMutation.mutateAsync(photoData);
|
||||
|
||||
if (!result.isValid) {
|
||||
setErrors(prev => ({
|
||||
@@ -286,8 +301,6 @@ export default function OnboardingWizard() {
|
||||
facePhoto: result.errorMessage || 'Face validation failed'
|
||||
}));
|
||||
setAvatarInfo(prev => ({ ...prev, facePhoto: null, isValidating: false }));
|
||||
} else {
|
||||
setAvatarInfo(prev => ({ ...prev, isValidating: false }));
|
||||
}
|
||||
} catch (error) {
|
||||
// For now, just accept the photo if validation fails
|
||||
@@ -295,31 +308,8 @@ export default function OnboardingWizard() {
|
||||
}
|
||||
};
|
||||
|
||||
const generateAvatars = async () => {
|
||||
if (!avatarInfo.facePhoto) {
|
||||
setErrors({ ...errors, facePhoto: 'Please upload a photo first' });
|
||||
return;
|
||||
}
|
||||
|
||||
setAvatarInfo(prev => ({ ...prev, isGenerating: true, generatedAvatars: [], selectedAvatarIndex: null }));
|
||||
setErrors(prev => {
|
||||
const { avatar, ...rest } = prev;
|
||||
return rest;
|
||||
});
|
||||
|
||||
try {
|
||||
// Get current user ID from session
|
||||
const session = await sessionService.getSession();
|
||||
if (!session?.user?.userId) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
|
||||
const result = await onboardingService.generateAvatars(
|
||||
session.user.userId,
|
||||
avatarInfo.facePhoto,
|
||||
avatarInfo.suitColor
|
||||
);
|
||||
|
||||
const generateAvatarsMutation = useGenerateAvatars({
|
||||
onSuccess: (result) => {
|
||||
if (result.success && result.avatarUrls) {
|
||||
setAvatarInfo(prev => ({
|
||||
...prev,
|
||||
@@ -330,15 +320,56 @@ export default function OnboardingWizard() {
|
||||
setErrors(prev => ({ ...prev, avatar: result.errorMessage || 'Failed to generate avatars' }));
|
||||
setAvatarInfo(prev => ({ ...prev, isGenerating: false }));
|
||||
}
|
||||
} catch (error) {
|
||||
},
|
||||
onError: () => {
|
||||
setErrors(prev => ({ ...prev, avatar: 'Failed to generate avatars. Please try again.' }));
|
||||
setAvatarInfo(prev => ({ ...prev, isGenerating: false }));
|
||||
},
|
||||
});
|
||||
|
||||
const generateAvatars = async () => {
|
||||
if (!avatarInfo.facePhoto) {
|
||||
setErrors({ ...errors, facePhoto: 'Please upload a photo first' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!session?.user?.userId) {
|
||||
setErrors({ ...errors, submit: 'User not authenticated' });
|
||||
return;
|
||||
}
|
||||
|
||||
setAvatarInfo(prev => ({ ...prev, isGenerating: true, generatedAvatars: [], selectedAvatarIndex: null }));
|
||||
setErrors(prev => {
|
||||
const { avatar, ...rest } = prev;
|
||||
return rest;
|
||||
});
|
||||
|
||||
try {
|
||||
await generateAvatarsMutation.mutateAsync({
|
||||
userId: session.user.userId,
|
||||
facePhotoData: avatarInfo.facePhoto,
|
||||
suitColor: avatarInfo.suitColor,
|
||||
});
|
||||
} catch (error) {
|
||||
// Error handling is done in the mutation's onError callback
|
||||
}
|
||||
};
|
||||
|
||||
const completeOnboardingMutation = useCompleteOnboarding({
|
||||
onSuccess: () => {
|
||||
// TODO: Handle avatar assignment separately if needed
|
||||
router.push('/dashboard');
|
||||
router.refresh();
|
||||
},
|
||||
onError: (error) => {
|
||||
setErrors({
|
||||
submit: error.message || 'Failed to create profile',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (loading) return;
|
||||
|
||||
// Validate step 2 - must have selected an avatar
|
||||
if (!validateStep(2)) {
|
||||
@@ -350,35 +381,26 @@ export default function OnboardingWizard() {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setErrors({});
|
||||
|
||||
try {
|
||||
// Note: The current API doesn't support avatarUrl in onboarding
|
||||
// This would need to be handled separately or the API would need to be updated
|
||||
const result = await onboardingService.completeOnboarding({
|
||||
await completeOnboardingMutation.mutateAsync({
|
||||
firstName: personalInfo.firstName.trim(),
|
||||
lastName: personalInfo.lastName.trim(),
|
||||
displayName: personalInfo.displayName.trim(),
|
||||
country: personalInfo.country,
|
||||
timezone: personalInfo.timezone || undefined,
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
// TODO: Handle avatar assignment separately if needed
|
||||
router.push('/dashboard');
|
||||
router.refresh();
|
||||
} else {
|
||||
throw new Error(result.errorMessage || 'Failed to create profile');
|
||||
}
|
||||
} catch (error) {
|
||||
setErrors({
|
||||
submit: error instanceof Error ? error.message : 'Failed to create profile',
|
||||
});
|
||||
setLoading(false);
|
||||
// Error handling is done in the mutation's onError callback
|
||||
}
|
||||
};
|
||||
|
||||
// Loading state comes from the mutations
|
||||
const loading = completeOnboardingMutation.isPending ||
|
||||
generateAvatarsMutation.isPending ||
|
||||
validateFacePhotoMutation.isPending;
|
||||
|
||||
const getCountryFlag = (countryCode: string): string => {
|
||||
const code = countryCode.toUpperCase();
|
||||
if (code.length === 2) {
|
||||
|
||||
Reference in New Issue
Block a user