/** * Signup Client Component * * Handles client-side signup flow. */ 'use client'; import { useState } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useAuth } from '@/components/auth/AuthContext'; import { SignupViewData } from '@/lib/builders/view-data/types/SignupViewData'; import { SignupTemplate } from '@/templates/auth/SignupTemplate'; import { SignupMutation } from '@/lib/mutations/auth/SignupMutation'; import { SignupViewModelBuilder } from '@/lib/builders/view-models/SignupViewModelBuilder'; import { SignupViewModel } from '@/lib/view-models/auth/SignupViewModel'; import { SignupFormValidation } from '@/lib/utilities/authValidation'; import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts'; export function SignupClient({ viewData }: ClientWrapperProps) { const router = useRouter(); const searchParams = useSearchParams(); const { refreshSession } = useAuth(); // Build ViewModel from ViewData const [viewModel, setViewModel] = useState(() => SignupViewModelBuilder.build(viewData) ); // Handle form field changes const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setViewModel(prev => { const newFormState = { ...prev.formState, fields: { ...prev.formState.fields, [name as keyof typeof prev.formState.fields]: { ...prev.formState.fields[name as keyof typeof prev.formState.fields], value, touched: true, error: undefined, }, }, }; return prev.withFormState(newFormState); }); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const formData = { firstName: viewModel.formState.fields.firstName.value as string, lastName: viewModel.formState.fields.lastName.value as string, email: viewModel.formState.fields.email.value as string, password: viewModel.formState.fields.password.value as string, confirmPassword: viewModel.formState.fields.confirmPassword.value as string, }; // Validate form const validationErrors = SignupFormValidation.validateForm(formData); if (validationErrors.length > 0) { setViewModel(prev => { const newFormState = { ...prev.formState, isValid: false, submitCount: prev.formState.submitCount + 1, fields: { ...prev.formState.fields, ...validationErrors.reduce((acc, error) => ({ ...acc, [error.field]: { ...prev.formState.fields[error.field as keyof typeof prev.formState.fields], error: error.message, touched: true, }, }), {}), }, }; return prev.withFormState(newFormState); }); return; } // Update submitting state setViewModel(prev => prev.withMutationState(true, null)); try { // Generate display name const displayName = SignupFormValidation.generateDisplayName(formData.firstName, formData.lastName); // Execute signup mutation const mutation = new SignupMutation(); const result = await mutation.execute({ email: formData.email, password: formData.password, displayName, }); if (result.isErr()) { const error = result.getError(); setViewModel(prev => prev.withMutationState(false, error)); return; } // Success - refresh session and redirect await refreshSession(); const returnTo = searchParams.get('returnTo') ?? '/onboarding'; router.push(returnTo); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Signup failed'; setViewModel(prev => prev.withMutationState(false, errorMessage)); } }; // Toggle password visibility const togglePassword = () => { setViewModel(prev => prev.withUIState({ ...prev.uiState, showPassword: !prev.uiState.showPassword, })); }; const toggleConfirmPassword = () => { setViewModel(prev => prev.withUIState({ ...prev.uiState, showConfirmPassword: !prev.uiState.showConfirmPassword, })); }; // Build viewData for template const templateViewData: SignupViewData = { ...viewData, formState: viewModel.formState, isSubmitting: viewModel.isSubmitting, submitError: viewModel.submitError, }; return ( ); }