All files / src/components/pages/auth/login/login-form index.tsx

72.44% Statements 71/98
100% Branches 6/6
80% Functions 4/5
72.44% Lines 71/98

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 89 90 91 92 93 94 95 96 97 98 991x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 1x 1x 1x 1x 1x 1x 1x 16x 16x 16x 16x 16x 16x 16x 16x 16x                                                       16x 16x 16x 16x 16x 1x 1x 1x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x  
'use client';
 
import { useEffect } from 'react';
 
import { useForm, useStore } from '@tanstack/react-form';
 
import { EmailField, PasswordField } from '@/components/pages/auth/fields';
import { useLogin } from '@/hooks/use-auth';
import { normalizePath } from '@/lib/auth/utils';
import { loginSchema } from '@/lib/schema/auth';
 
import { AuthSubmitButton } from '../../auth-button';
import { LoginSnsButton } from '../login-sns-button';
 
export const LoginForm = () => {
  const { handleLogin, loginError, clearLoginError } = useLogin();
 
  const form = useForm({
    defaultValues: {
      email: '',
      password: '',
    },
    validators: {
      onSubmit: loginSchema,
      onChange: loginSchema,
    },
    onSubmit: async ({ value, formApi }) => {
      const payload = {
        email: value.email,
        password: value.password,
      };
 
      await handleLogin(payload, formApi);
    },
  });
 
  const { email, password } = useStore(form.baseStore, (state) => state.values);
 
  useEffect(() => {
    clearLoginError();
  }, [email, password, clearLoginError]);
 
  const handleGoogleLogin = () => {
    const GOOGLE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID;

    if (!GOOGLE_CLIENT_ID) {
      console.error('NEXT_PUBLIC_GOOGLE_CLIENT_ID is missing');
      return;
    }

    const currentPathParam = new URLSearchParams(window.location.search).get('path');
    sessionStorage.setItem('post_login_path', normalizePath(currentPathParam));

    const redirectUri = `${window.location.origin}/auth/google/callback`;

    const state = crypto.randomUUID();
    sessionStorage.setItem('google_oauth_state', state);

    const url = new URL('https://accounts.google.com/o/oauth2/v2/auth');
    url.search = new URLSearchParams({
      client_id: GOOGLE_CLIENT_ID,
      redirect_uri: redirectUri,
      response_type: 'code',
      scope: 'openid email profile',
      state,
      prompt: 'select_account',
    }).toString();

    window.location.assign(url.toString());
  };
 
  return (
    <form
      className='flex-col-center w-full gap-8'
      onSubmit={(e) => {
        e.preventDefault();
        void form.handleSubmit();
      }}
    >
      <div className='flex-col-center w-full gap-4'>
        <form.Field children={(field) => <EmailField field={field} />} name='email' />
        <form.Field
          children={(field) => <PasswordField field={field} passwordType='loginPassword' />}
          name='password'
        />
      </div>
 
      <div className='flex-col-center w-full gap-2'>
        <form.Subscribe
          children={(state) => <AuthSubmitButton state={state} type='login' />}
          selector={(state) => state}
        />
        <LoginSnsButton onClick={handleGoogleLogin}>Google로 로그인하기</LoginSnsButton>
        {loginError && <p className='text-error-500 text-text-sm-medium'>{loginError}</p>}
      </div>
    </form>
  );
};