/** * Forgot Password Client Component * * Handles client-side forgot password flow. */ 'use client'; import { useState } from 'react'; import { ForgotPasswordViewData } from '@/lib/builders/view-data/types/ForgotPasswordViewData'; import { ForgotPasswordTemplate } from '@/templates/auth/ForgotPasswordTemplate'; import { ForgotPasswordMutation } from '@/lib/mutations/auth/ForgotPasswordMutation'; import { ForgotPasswordViewModelBuilder } from '@/lib/builders/view-models/ForgotPasswordViewModelBuilder'; import { ForgotPasswordViewModel } from '@/lib/view-models/auth/ForgotPasswordViewModel'; import { ForgotPasswordFormValidation } from '@/lib/utilities/authValidation'; import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts'; export function ForgotPasswordClient({ viewData }: ClientWrapperProps) { // Build ViewModel from ViewData const [viewModel, setViewModel] = useState(() => ForgotPasswordViewModelBuilder.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]: { ...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 = { email: viewModel.formState.fields.email.value as string, }; // Validate form const validationErrors = ForgotPasswordFormValidation.validateForm(formData); if (validationErrors.length > 0) { setViewModel(prev => { const newFormState = { ...prev.formState, isValid: false, submitCount: prev.formState.submitCount + 1, fields: { ...prev.formState.fields, email: { ...prev.formState.fields.email, error: validationErrors.find(e => e.field === 'email')?.message, touched: true, }, }, }; return prev.withFormState(newFormState); }); return; } // Update submitting state setViewModel(prev => prev.withMutationState(true, null)); try { // Execute forgot password mutation const mutation = new ForgotPasswordMutation(); const result = await mutation.execute(formData); 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, data.magicLink || null)); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to send reset link'; setViewModel(prev => prev.withMutationState(false, errorMessage)); } }; // Build viewData for template const templateViewData: ForgotPasswordViewData = { ...viewData, showSuccess: viewModel.showSuccess, successMessage: viewModel.successMessage || undefined, magicLink: viewModel.magicLink || undefined, formState: viewModel.formState, isSubmitting: viewModel.isSubmitting, submitError: viewModel.submitError, }; return ( { if (!show) { // Reset to initial state setViewModel(() => ForgotPasswordViewModelBuilder.build(viewData)); } }, }} mutationState={{ isPending: viewModel.mutationPending, error: viewModel.mutationError, }} /> ); }