diff --git a/src/components/Modal/EmailExistModal.tsx b/src/components/Modal/EmailExistModal.tsx new file mode 100644 index 00000000..75409c22 --- /dev/null +++ b/src/components/Modal/EmailExistModal.tsx @@ -0,0 +1,35 @@ +import { useDispatch } from 'react-redux'; + +import ModalActionButton from '@/components/Button/ModalActionButton'; +import { closeModal } from '@/store/reducers/modalSlice'; + +export default function EmailExistModal({ + modalProps, +}: { + modalProps: { + onResetField: () => void; + onSetFocus: () => void; + }; +}) { + const dispatch = useDispatch(); + + const handleActionButton = () => { + dispatch(closeModal()); + modalProps.onResetField(); + modalProps.onSetFocus(); + }; + + return ( +
+
+

중복된 이메일 입니다.

+ + 확인 + +
+
+ ); +} diff --git a/src/components/Modal/SignupSuccessModal.tsx b/src/components/Modal/SignupSuccessModal.tsx new file mode 100644 index 00000000..bfc9a2dc --- /dev/null +++ b/src/components/Modal/SignupSuccessModal.tsx @@ -0,0 +1,27 @@ +import { useRouter } from 'next/router'; +import { useDispatch } from 'react-redux'; + +import ModalActionButton from '@/components/Button/ModalActionButton'; +import { closeModal } from '@/store/reducers/modalSlice'; + +export default function SignUpSuccessModal() { + const router = useRouter(); + const dispatch = useDispatch(); + + return ( +
+
+

가입이 완료되었습니다!

+ { + dispatch(closeModal()); + router.push('/signin'); + }} + > + 로그인 + +
+
+ ); +} diff --git a/src/components/Modal/TextModal.tsx b/src/components/Modal/TextModal.tsx new file mode 100644 index 00000000..d14c923e --- /dev/null +++ b/src/components/Modal/TextModal.tsx @@ -0,0 +1,26 @@ +import { MouseEventHandler } from 'react'; + +import ModalActionButton from '@/components/Button/ModalActionButton'; +import { TextModalProps } from '@/types/Modal.interface'; + +export default function TextModal({ + handleCloseModal, + modalProps, +}: { + handleCloseModal: MouseEventHandler; + modalProps: TextModalProps; +}) { + return ( +
+
+

{modalProps.text}

+ + 확인 + +
+
+ ); +} diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index f3161ce5..ee689777 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -5,10 +5,13 @@ import ColumnDeleteModal from './ColumnDeleteModal'; import ColumnModifyModal from './ColumnModifyModal'; import DefaultModal from './DefaultModal'; import DeleteDashboardModal from './DeleteDashboardModal'; +import EmailExistModal from './EmailExistModal'; import InviteMemberModal from './InviteMemberModal'; import NewColumnModal from './NewColumnModal'; import NewDashboardModal from './NewDashboardModal'; import NotificationModal from './NotificationModal'; +import SignUpSuccessModal from './signupSuccessModal'; +import TextModal from './TextModal'; import { NOTIFICATION_TEXT_OBJ } from '@/constants'; import { modalSelector, closeModal } from '@/store/reducers/modalSlice'; @@ -16,8 +19,10 @@ import { ColumnDeleteModalProps, ColumnModifyModalProps, DeleteDashboardModalProps, + EmailExistModalProps, InviteMemberModalProps, NewColumnModalProps, + TextModalProps, } from '@/types/Modal.interface'; export default function Modal() { @@ -48,12 +53,9 @@ export default function Modal() { handleCloseModal(); } }; - const renderModalContent = () => { switch (type) { case 'pwdNotEqual': - case 'signupSuccess': - case 'emailExists': case 'curPwdNotEqual': case 'newDashboardSuccess': case 'newDashboardFailed': @@ -64,6 +66,10 @@ export default function Modal() { case 'columnModifySuccess': case 'columnModifyFailed': return ; + case 'textModal': + return modalProps ? ( + + ) : null; case 'newDashboard': return ; case 'deleteDashboard': @@ -89,6 +95,10 @@ export default function Modal() { return modalProps ? ( ) : null; + case 'emailExists': + return modalProps ? : null; + case 'signupSuccess': + return ; default: return ; } @@ -98,7 +108,7 @@ export default function Modal() { <> {type && (
{renderModalContent()} diff --git a/src/constants/index.ts b/src/constants/index.ts index a0ea975d..664066dd 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,7 +1,6 @@ // 알림 모달용 상수 export const NOTIFICATION_TEXT_OBJ = { pwdNotEqual: '비밀번호가 일치하지 않습니다.', - signupSuccess: '가입이 완료되었습니다!', emailExists: '이미 사용 중인 이메일입니다.', curPwdNotEqual: '현재 비밀번호가 틀렸습니다.', columnDeleteConfirm: '컬럼의 모든 카드가 삭제됩니다.', diff --git a/src/containers/signin&signup/SignUpForm.tsx b/src/containers/signin&signup/SignUpForm.tsx index 4bd7059f..a116d06b 100644 --- a/src/containers/signin&signup/SignUpForm.tsx +++ b/src/containers/signin&signup/SignUpForm.tsx @@ -1,11 +1,12 @@ import { yupResolver } from '@hookform/resolvers/yup'; -import { useRouter } from 'next/router'; -import { useState, useEffect } from 'react'; +import { AxiosError } from 'axios'; +import { useState } from 'react'; import { useForm } from 'react-hook-form'; import * as yup from 'yup'; import PwdInputWithLabel from '@/containers/signin&signup/PwdInputWithLabel'; import TextInputWithLabel from '@/containers/signin&signup/TextInputWithLabel'; +import useModal from '@/hooks/useModal'; import { postSignUp } from '@/services/postService'; export type TSignUpInputs = { @@ -26,36 +27,45 @@ const schema = yup.object().shape({ }); export default function SignUpForm() { + const { openModal } = useModal(); + const [checkTerms, setCheckTerms] = useState(false); - const router = useRouter(); const { register, handleSubmit, - watch, - trigger, formState: { errors, isValid }, + resetField, + setFocus, } = useForm({ resolver: yupResolver(schema), mode: 'onChange', }); - const password = watch('password'); - const passwordConfirmation = watch('passwordConfirmation'); - - useEffect(() => { - if (password?.length > 0) { - trigger('passwordConfirmation'); - } - }, [password, passwordConfirmation, trigger]); - const onSubmit = async (data: TSignUpInputs) => { try { await postSignUp(data); - router.push('/signin'); // 회원가입이 성공되면 로그인 페이지로 리다이렉트 + openModal({ type: 'signupSuccess' }); } catch (error) { - console.error('회원가입에 실패했습니다:', error); - // 에러 처리 로직 추가 가능 + if (error instanceof AxiosError && error.response?.status === 409) { + openModal({ + type: 'emailExists', + modalProps: { + onResetField: () => { + resetField('email'); + }, + onSetFocus: () => { + setFocus('email'); + }, + }, + }); + } else if (error instanceof AxiosError) { + if (error.response?.data.message) { + openModal({ type: 'textModal', modalProps: { text: error.response.data.message } }); + } + } else { + openModal({ type: 'textModal', modalProps: { text: '회원가입을 실패하였습니다.' } }); + } } }; diff --git a/src/pages/signup/index.tsx b/src/pages/signup/index.tsx index d28ad6a2..9e2ea62a 100644 --- a/src/pages/signup/index.tsx +++ b/src/pages/signup/index.tsx @@ -9,7 +9,7 @@ export default function SignUp() {
-

+

이미 가입하셨나요?{' '} 로그인하기 diff --git a/src/types/Modal.interface.ts b/src/types/Modal.interface.ts index 7b4ea0cb..3ed804d6 100644 --- a/src/types/Modal.interface.ts +++ b/src/types/Modal.interface.ts @@ -18,11 +18,23 @@ export interface DeleteDashboardModalProps { dashboardId: number; } +export interface TextModalProps { + text: string; +} + +export interface EmailExistModalProps { + onResetField: () => void; + onSetFocus: () => void; +} + export type ModalProps = | ColumnModifyModalProps | ColumnDeleteModalProps | NewColumnModalProps | InviteMemberModalProps + | DeleteDashboardModalProps + | EmailExistModalProps + | TextModalProps | null; export interface ModalState {