All files / src/viewmodels/auth useLoginViewModel.ts

89.39% Statements 59/66
60% Branches 6/10
100% Functions 1/1
89.39% Lines 59/66

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 851x 1x   1x 1x 1x 1x                       1x 1x 1x 1x   1x 3x 3x 3x 3x   3x 3x 2x 2x 2x 2x 3x 3x   3x 1x 1x 1x 1x   1x 1x         1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x     1x 1x 1x 1x 3x   3x 3x 3x 3x 3x 3x 3x 3x 3x  
import { useState, useCallback } from 'react';
import { login } from '@/services/authService';
import { AuthSessionResponse } from '@/models/auth';
import { ApiError } from '@/services/api';
import i18n from '@/i18n';
import { getAuthApiErrorMessage, getAuthApiFieldErrors } from '@/utils/authErrorPresentation';
import { validateUsername, validatePassword } from '@/utils/validators';
 
export interface LoginFormData {
  username: string;
  password: string;
}
 
export interface LoginFormErrors {
  username?: string | null;
  password?: string | null;
}
 
const INITIAL_FORM_DATA: LoginFormData = {
  username: '',
  password: '',
};
 
export function useLoginViewModel() {
  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) {
        const fieldErrors = getAuthApiFieldErrors(err);
        if (Object.keys(fieldErrors).length > 0) {
          setErrors((prev) => ({ ...prev, ...fieldErrors }));
          setApiError(null);
        } else {
          setApiError(getAuthApiErrorMessage(err));
        }
      } else {
        setApiError(i18n.t('errors.unexpected'));
      }
      return null;
    } finally {
      setIsLoading(false);
    }
  }, [formData]);
 
  return {
    formData,
    errors,
    isLoading,
    apiError,
    updateField,
    handleLogin,
  };
}