/** * Forgot Password ViewModel * * Client-side state management for forgot password flow. * Immutable, class-based, contains only UI state. */ export interface ForgotPasswordFormField { value: string; error?: string; touched: boolean; validating: boolean; } export interface ForgotPasswordFormState { fields: { email: ForgotPasswordFormField; }; isValid: boolean; isSubmitting: boolean; submitError?: string; submitCount: number; } export class ForgotPasswordViewModel { constructor( public readonly returnTo: string, public readonly formState: ForgotPasswordFormState, public readonly showSuccess: boolean = false, public readonly successMessage: string | null = null, public readonly magicLink: string | null = null, public readonly mutationPending: boolean = false, public readonly mutationError: string | null = null ) {} withFormState(formState: ForgotPasswordFormState): ForgotPasswordViewModel { return new ForgotPasswordViewModel( this.returnTo, formState, this.showSuccess, this.successMessage, this.magicLink, this.mutationPending, this.mutationError ); } withSuccess(successMessage: string, magicLink: string | null = null): ForgotPasswordViewModel { return new ForgotPasswordViewModel( this.returnTo, this.formState, true, successMessage, magicLink, false, null ); } withMutationState(pending: boolean, error: string | null): ForgotPasswordViewModel { return new ForgotPasswordViewModel( this.returnTo, this.formState, this.showSuccess, this.successMessage, this.magicLink, pending, error ); } get isSubmitting(): boolean { return this.formState.isSubmitting || this.mutationPending; } get submitError(): string | undefined { return this.formState.submitError || this.mutationError || undefined; } }