/** * Reset Password Client Component * * Handles client-side reset password flow. */ 'use client'; import { useState } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { ResetPasswordViewData } from '@/lib/builders/view-data/types/ResetPasswordViewData'; import { ResetPasswordTemplate } from '@/templates/auth/ResetPasswordTemplate'; import { ResetPasswordMutation } from '@/lib/mutations/auth/ResetPasswordMutation'; import { ResetPasswordViewModelBuilder } from '@/lib/builders/view-models/ResetPasswordViewModelBuilder'; import { ResetPasswordViewModel } from '@/lib/view-models/auth/ResetPasswordViewModel'; import { ResetPasswordFormValidation } from '@/lib/utilities/authValidation'; import { routes } from '@/lib/routing/RouteConfig'; import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts'; export function ResetPasswordClient({ viewData }: ClientWrapperProps) { const router = useRouter(); const searchParams = useSearchParams(); // Build ViewModel from ViewData const [viewModel, setViewModel] = useState(() => ResetPasswordViewModelBuilder.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 = { newPassword: viewModel.formState.fields.newPassword.value as string, confirmPassword: viewModel.formState.fields.confirmPassword.value as string, }; // Validate form const validationErrors = ResetPasswordFormValidation.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 { const token = searchParams.get('token'); if (!token) { setViewModel(prev => prev.withMutationState(false, 'Invalid reset link')); return; } // Execute reset password mutation const mutation = new ResetPasswordMutation(); const result = await mutation.execute({ token, newPassword: formData.newPassword, }); if (result.isErr()) { const error = result.getError(); setViewModel(prev => prev.withMutationState(false, error)); return; } // Success const data = result.unwrap(); setViewModel(prev => prev.withSuccess(data.message)); // Redirect to login after a delay setTimeout(() => { router.push(routes.auth.login); }, 3000); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to reset password'; 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: ResetPasswordViewData = { ...viewData, showSuccess: viewModel.showSuccess, successMessage: viewModel.successMessage || undefined, formState: viewModel.formState, isSubmitting: viewModel.isSubmitting, submitError: viewModel.submitError, }; return ( { if (!show) { // Reset to initial state setViewModel(() => ResetPasswordViewModelBuilder.build(viewData)); } }, setShowPassword: togglePassword, setShowConfirmPassword: toggleConfirmPassword, }} uiState={{ showPassword: viewModel.uiState.showPassword, showConfirmPassword: viewModel.uiState.showConfirmPassword, }} mutationState={{ isPending: viewModel.mutationPending, error: viewModel.mutationError, }} /> ); }