All files / src/viewmodels/auth useLoginViewModel.ts

100% Statements 33/33
100% Branches 3/3
100% Functions 6/6
100% Lines 30/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 891x 1x   1x 1x                                               1x         1x 24x 24x 24x 24x   24x   13x 13x 13x         24x 8x         14x 8x 2x 2x     6x 6x 6x 6x         5x 4x   1x   5x   6x       24x                  
import { useState, useCallback } from 'react';
import { login } from '@/services/authService';
import { AuthSessionResponse } from '@/models/auth';
import { ApiError } from '@/services/api';
import { validateUsername, validatePassword } from '@/utils/validators';
 
export interface LoginFormData {
  username: string;
  password: string;
}
 
export interface LoginFormErrors {
  username?: string | null;
  password?: string | null;
}
 
export interface LoginViewModel {
  formData: LoginFormData;
  errors: LoginFormErrors;
  isLoading: boolean;
  apiError: string | null;
  updateField: <K extends keyof LoginFormData>(
    field: K,
    value: LoginFormData[K],
  ) => void;
  handleLogin: () => Promise<AuthSessionResponse | null>;
}
 
const INITIAL_FORM_DATA: LoginFormData = {
  username: '',
  password: '',
};
 
export function useLoginViewModel(): LoginViewModel {
  const [formData, setFormData] = useState<LoginFormData>(INITIAL_FORM_DATA);
  const [errors, setErrors] = useState<LoginFormErrors>({});
  const [isLoading, setIsLoading] = useState(false);
  const [apiError, setApiError] = useState<string | null>(null);
 
  const updateField = useCallback(
    <K extends keyof LoginFormData>(field: K, value: LoginFormData[K]) => {
      setFormData((prev) => ({ ...prev, [field]: value }));
      setErrors((prev) => ({ ...prev, [field]: null }));
      setApiError(null);
    },
    [],
  );
 
  const handleLogin = useCallback(async (): Promise<AuthSessionResponse | null> => {
    const newErrors: LoginFormErrors = {
      username: validateUsername(formData.username),
      password: validatePassword(formData.password),
    };
 
    const hasErrors = Object.values(newErrors).some((e) => e != null);
    if (hasErrors) {
      setErrors(newErrors);
      return null;
    }
 
    setIsLoading(true);
    setApiError(null);
    try {
      return await login({
        username: formData.username,
        password: formData.password,
      });
    } catch (err) {
      if (err instanceof ApiError) {
        setApiError(err.message);
      } else {
        setApiError('An unexpected error occurred. Please try again.');
      }
      return null;
    } finally {
      setIsLoading(false);
    }
  }, [formData]);
 
  return {
    formData,
    errors,
    isLoading,
    apiError,
    updateField,
    handleLogin,
  };
}