diff --git a/src/auth/components/SigninModal.tsx b/src/auth/components/SigninModal.tsx index 5b3dd32..9ea9341 100644 --- a/src/auth/components/SigninModal.tsx +++ b/src/auth/components/SigninModal.tsx @@ -2,7 +2,7 @@ import { GitHubIcon, GoogleIcon, Loading } from '@shared/components/Icons'; import { Modal, Stack, Button } from '@jdesignlab/react'; import { useMachine } from '@xstate/react'; import { Mail } from '@jdesignlab/react-icons'; -import { EmailPasswordForm } from './EmailPasswordForm'; +import { EmailPasswordForm } from './action/EmailPasswordForm'; import { useSigninWithProvider } from '../hooks/useSigninWithProvider'; import { signMachine } from '../machines/signMachine'; diff --git a/src/auth/components/SignupModal.tsx b/src/auth/components/SignupModal.tsx index 7f4657d..a85cf14 100644 --- a/src/auth/components/SignupModal.tsx +++ b/src/auth/components/SignupModal.tsx @@ -1,7 +1,7 @@ import { Modal, Button } from '@jdesignlab/react'; import { useMachine } from '@xstate/react'; -import { UserProfileForm } from './UserProfileForm'; -import { EmailPasswordForm } from './EmailPasswordForm'; +import { UserProfileForm } from './action/UserProfileForm'; +import { EmailPasswordForm } from './action/EmailPasswordForm'; import { signMachine } from '../machines/signMachine'; import { useSetUserAuthData } from '../hooks/useSetUserAuthData'; diff --git a/src/auth/components/action/AuthLoginButtons.tsx b/src/auth/components/action/AuthLoginButtons.tsx new file mode 100644 index 0000000..5bd3a6f --- /dev/null +++ b/src/auth/components/action/AuthLoginButtons.tsx @@ -0,0 +1,54 @@ +import { GitHubIcon, GoogleIcon, Loading } from '@shared/components/Icons'; +import { useActor } from '@xstate/react'; +import { Button, Flex } from '@jdesignlab/react'; +import { Mail } from '@jdesignlab/react-icons'; +import { useSigninWithProvider } from '../../hooks/useSigninWithProvider'; +import type { SignMachineType } from '../../machines/signMachine'; +import type { InterpreterFrom } from 'xstate'; + +interface Props { + signMachine: InterpreterFrom; +} + +export const AuthLoginButtons = (props: Props) => { + const { signMachine } = props; + const [, refSend] = useActor(signMachine); + const { isLoading: loadingWithGitHubLogin, mutate: signinGithub } = useSigninWithProvider('GITHUB', refSend); + const { isLoading: loadingWithGoogleLogin, mutate: signinGoogle } = useSigninWithProvider('GOOGLE', refSend); + return ( + + + + + + ); +}; diff --git a/src/auth/components/EmailPasswordForm.tsx b/src/auth/components/action/EmailPasswordForm.tsx similarity index 66% rename from src/auth/components/EmailPasswordForm.tsx rename to src/auth/components/action/EmailPasswordForm.tsx index ba230a8..8e6a37c 100644 --- a/src/auth/components/EmailPasswordForm.tsx +++ b/src/auth/components/action/EmailPasswordForm.tsx @@ -1,12 +1,12 @@ -import { Text, TextInput, Button, Modal } from '@jdesignlab/react'; +import { Text, TextInput, Button } from '@jdesignlab/react'; import { useForm } from 'react-hook-form'; import { useActor } from '@xstate/react'; -import { Flex } from '../styles/Profile'; -import { useAccountEmailWithPassword } from '../hooks/useAccountEmailWithPassword'; -import { useCreateUserMutation } from '../hooks/useCreateUserMutation'; +import { Flex } from '../../styles/Profile'; +import { useAccountEmailWithPassword } from '../../hooks/useAccountEmailWithPassword'; +import { useCreateUserMutation } from '../../hooks/useCreateUserMutation'; import type { InterpreterFrom } from 'xstate'; -import type { SignMachineType } from '../machines/signMachine'; -import type { EmailPasswordField } from '../types'; +import type { SignMachineType } from '../../machines/signMachine'; +import type { EmailPasswordField } from '../../types'; interface Props { signMachine: InterpreterFrom; @@ -61,24 +61,22 @@ export const EmailPasswordForm = (props: Props) => { Password {errors.password && {errors.password.message as string}} - - - {!signup && ( - - )} - - - + )} + + ); }; diff --git a/src/auth/components/UserProfileForm.tsx b/src/auth/components/action/UserProfileForm.tsx similarity index 88% rename from src/auth/components/UserProfileForm.tsx rename to src/auth/components/action/UserProfileForm.tsx index cccbc89..56504b6 100644 --- a/src/auth/components/UserProfileForm.tsx +++ b/src/auth/components/action/UserProfileForm.tsx @@ -4,13 +4,13 @@ import { Loading } from '@shared/components/Icons'; import { useForm } from 'react-hook-form'; import { useActor } from '@xstate/react'; import { TextInput, Button, Modal } from '@jdesignlab/react'; -import { useProfileUpdate } from '../hooks/useProfileUpdate'; -import { useSetUserAuthData } from '../hooks/useSetUserAuthData'; -import { parseUserInfo } from '../parseUserInfo'; -import { Flex } from '../styles/Profile'; +import { useProfileUpdate } from '../../hooks/useProfileUpdate'; +import { useSetUserAuthData } from '../../hooks/useSetUserAuthData'; +import { parseUserInfo } from '../../parseUserInfo'; +import { Flex } from '../../styles/Profile'; import type { InterpreterFrom } from 'xstate'; -import type { SignMachineType } from '../machines/signMachine'; -import type { UserProfile } from '../types'; +import type { SignMachineType } from '../../machines/signMachine'; +import type { UserProfile } from '../../types'; interface Props { signupMachineRef: InterpreterFrom; diff --git a/src/auth/styles/signPageStyle.ts b/src/auth/styles/signPageStyle.ts new file mode 100644 index 0000000..a011190 --- /dev/null +++ b/src/auth/styles/signPageStyle.ts @@ -0,0 +1,22 @@ +import { css } from '@emotion/react'; + +export const signFormContainer = css({ + height: 'fit-content', + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + padding: '24px 16px' +}); + +export const title = css({ + textAlign: 'center' +}); + +export const signButtonWrapper = css({ + display: 'flex', + marginTop: '32px', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center' +}); diff --git a/src/layout/components/Header.tsx b/src/layout/components/Header.tsx index 1e86b01..cb2f5c5 100644 --- a/src/layout/components/Header.tsx +++ b/src/layout/components/Header.tsx @@ -1,6 +1,4 @@ import { useSignout } from '@auth/hooks/useSignout'; -import { SigninModal } from '@auth/components/SigninModal'; -import { SignupModal } from '@auth/components/SignupModal'; import { ErrorModal } from '@shared/components/ErrorModal'; import { Layout } from '@shared/components/dataDisplay/FlexLayout'; import { headerWrapper, headerContents } from '@layout/styles/headerStyle'; @@ -8,7 +6,7 @@ import { useGetUserInfoById } from '@challenge/hooks/useGetUserInfoById'; import { Avatar } from '@shared/components/dataDisplay/Avatar'; import { Loading } from '@shared/components/Icons/Loading'; import { CompositionBoundaryReactQuery } from '@shared/boundaries'; -import { Dropdown, Text } from '@jdesignlab/react'; +import { Button, Dropdown, Text } from '@jdesignlab/react'; import { useRouter } from 'next/router'; import Image from 'next/image'; import { useSession } from 'next-auth/react'; @@ -19,6 +17,15 @@ export const Header = () => { const moveToMain = () => { router.push('/'); }; + + const moveToSigninPage = () => { + router.push('/login'); + }; + + const moveToSignupPage = () => { + router.push('/signup'); + }; + const { data } = useSession(); const { userInfo } = useGetUserInfoById(data?.user?.uid); const { mutate: signout } = useSignout(); @@ -86,8 +93,10 @@ export const Header = () => { ) : ( - - + + )} diff --git a/src/pages/login.tsx b/src/pages/login.tsx new file mode 100644 index 0000000..7775771 --- /dev/null +++ b/src/pages/login.tsx @@ -0,0 +1,41 @@ +import * as Style from '@auth/styles/signPageStyle'; +import { CompositionBoundaryReactQuery } from '@shared/boundaries'; +import { ErrorModal } from '@shared/components/ErrorModal'; +import { signMachine } from '@auth/machines/signMachine'; +import { AuthLoginButtons } from '@auth/components/action/AuthLoginButtons'; +import { Loading } from '@shared/components/Icons'; +import { EmailPasswordForm } from '@auth/components/action/EmailPasswordForm'; +import { Button, Text } from '@jdesignlab/react'; +import { useMachine } from '@xstate/react'; +import { useRouter } from 'next/router'; + +const Login = () => { + const [state, , service] = useMachine(signMachine); + const { value: signState } = state; + const router = useRouter(); + + const moveToLoginPage = () => { + router.push('/signup'); + }; + + return ( + } error={(props) => }> +
+
+ + 로그인 + + +
+
+ {signState === 'selection' && } + {signState === 'email' && } +
+
+
+ ); +}; + +export default Login; diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx new file mode 100644 index 0000000..a33a4cb --- /dev/null +++ b/src/pages/signup.tsx @@ -0,0 +1,66 @@ +import * as Style from '@auth/styles/signPageStyle'; +import { Loading } from '@shared/components/Icons'; +import { CompositionBoundaryReactQuery } from '@shared/boundaries'; +import { ErrorModal } from '@shared/components/ErrorModal'; +import { EmailPasswordForm } from '@auth/components/action/EmailPasswordForm'; +import { signMachine } from '@auth/machines/signMachine'; +import { UserProfileForm } from '@auth/components/action/UserProfileForm'; +import { useSetUserAuthData } from '@auth/hooks/useSetUserAuthData'; +import { Button, Text } from '@jdesignlab/react'; +import { useMachine } from '@xstate/react'; +import { useRouter } from 'next/router'; + +const SignUpPage = () => { + const [state, send, service] = useMachine(signMachine); + const { value: signState, context } = state; + const userInfo = context.user; + const { updateUserData } = useSetUserAuthData(); + const router = useRouter(); + + const moveToPage = (url: string, callback?: () => void) => { + if (callback) { + callback(); + } + router.push(url); + }; + + const onUpdateUserData = () => { + if (userInfo) { + updateUserData(userInfo); + } + send('CLEAR'); + }; + + if (state.matches('done')) { + moveToPage('/'); + } + + return ( + } error={(props) => }> +
+
+ + 회원가입 + + +
+
+ {signState === 'registry' ? ( + + ) : ( + + )} +
+
+
+ ); +}; + +export default SignUpPage; diff --git a/src/shared/constants/firebaseError.ts b/src/shared/constants/firebaseError.ts index 235e596..92835fa 100644 --- a/src/shared/constants/firebaseError.ts +++ b/src/shared/constants/firebaseError.ts @@ -15,6 +15,7 @@ export type FirebaseErrorCode = | 'auth/invalid-display-name' | 'auth/invalid-dynamic-link-domain' | 'auth/invalid-email' + | 'auth/wrong-password' | 'auth/invalid-email-verified' | 'auth/invalid-hash-algorithm' | 'auth/invalid-hash-block-size' @@ -119,5 +120,6 @@ export const CustomFirebaseError: Record = { 'auth/uid-already-exists': '제공된 uid를 기존 사용자가 이미 사용하고 있습니다. 각 사용자마다 uid가 고유해야 합니다.', 'auth/unauthorized-continue-uri': '연결 URL의 도메인이 허용 목록에 포함되어 있지 않습니다. Firebase Console에서 도메인을 허용해야 합니다.', - 'auth/user-not-found': '제공된 식별자에 해당하는 기존 사용자 레코드가 없습니다.' + 'auth/user-not-found': '제공된 식별자에 해당하는 기존 사용자 레코드가 없습니다.', + 'auth/wrong-password': '비밀번호가 맞지 않습니다.' };