From b7aabd97f06d7d71c62420b29ac1ecb481e4d6e7 Mon Sep 17 00:00:00 2001 From: Daeyang lee Date: Fri, 28 Jun 2024 20:00:11 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20feat(#7):=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EB=AA=A8=EB=8B=AC=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Modal/SignupSuccessModal.tsx | 27 ++++++++++++++++++++ src/components/Modal/index.tsx | 6 +++-- src/constants/index.ts | 1 - src/containers/signin&signup/SignUpForm.tsx | 28 ++++++++------------- src/pages/signup/index.tsx | 2 +- 5 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 src/components/Modal/SignupSuccessModal.tsx 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/index.tsx b/src/components/Modal/index.tsx index f3161ce5..2d17800e 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -9,6 +9,7 @@ import InviteMemberModal from './InviteMemberModal'; import NewColumnModal from './NewColumnModal'; import NewDashboardModal from './NewDashboardModal'; import NotificationModal from './NotificationModal'; +import SignUpSuccessModal from './signupSuccessModal'; import { NOTIFICATION_TEXT_OBJ } from '@/constants'; import { modalSelector, closeModal } from '@/store/reducers/modalSlice'; @@ -52,7 +53,6 @@ export default function Modal() { const renderModalContent = () => { switch (type) { case 'pwdNotEqual': - case 'signupSuccess': case 'emailExists': case 'curPwdNotEqual': case 'newDashboardSuccess': @@ -89,6 +89,8 @@ export default function Modal() { return modalProps ? ( ) : null; + case 'signupSuccess': + return ; default: return ; } @@ -98,7 +100,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..f633ab31 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,29 @@ 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 }, } = 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' }); + } else { + console.error('회원가입에 실패했습니다:', error); + } } }; 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() {
-

+

이미 가입하셨나요?{' '} 로그인하기 From 9f38dbdb65b1425557fe96f845ea399b1de64060 Mon Sep 17 00:00:00 2001 From: Daeyang lee Date: Sat, 29 Jun 2024 09:58:42 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20feat(#7):=20email=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=8B=9C=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EB=B9=84=EC=9A=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/containers/signin&signup/SignUpForm.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/containers/signin&signup/SignUpForm.tsx b/src/containers/signin&signup/SignUpForm.tsx index f633ab31..deb54a7d 100644 --- a/src/containers/signin&signup/SignUpForm.tsx +++ b/src/containers/signin&signup/SignUpForm.tsx @@ -35,6 +35,7 @@ export default function SignUpForm() { register, handleSubmit, formState: { errors, isValid }, + resetField, } = useForm({ resolver: yupResolver(schema), mode: 'onChange', @@ -47,6 +48,7 @@ export default function SignUpForm() { } catch (error) { if (error instanceof AxiosError && error.response?.status === 409) { openModal({ type: 'emailExists' }); + resetField('email'); } else { console.error('회원가입에 실패했습니다:', error); } From 952d6f73a915654585ea88fa1725b533b706c05d Mon Sep 17 00:00:00 2001 From: Daeyang lee Date: Sat, 29 Jun 2024 17:34:01 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20feat(#7):=20=EC=9D=B4=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20input=20focus=20=EB=B0=8F=20=EB=B9=84=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0,=20=EC=9D=BC=EB=B0=98=20=ED=85=8D=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=A0=84=EB=8B=AC=20=EB=B0=9B=EB=8A=94=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Modal/EmailExistModal.tsx | 35 +++++++++++++++++++++ src/components/Modal/TextModal.tsx | 26 +++++++++++++++ src/components/Modal/index.tsx | 12 +++++-- src/containers/signin&signup/SignUpForm.tsx | 20 ++++++++++-- src/types/Modal.interface.ts | 12 +++++++ 5 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/components/Modal/EmailExistModal.tsx create mode 100644 src/components/Modal/TextModal.tsx 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/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 2d17800e..ee689777 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -5,11 +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'; @@ -17,8 +19,10 @@ import { ColumnDeleteModalProps, ColumnModifyModalProps, DeleteDashboardModalProps, + EmailExistModalProps, InviteMemberModalProps, NewColumnModalProps, + TextModalProps, } from '@/types/Modal.interface'; export default function Modal() { @@ -49,11 +53,9 @@ export default function Modal() { handleCloseModal(); } }; - const renderModalContent = () => { switch (type) { case 'pwdNotEqual': - 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,8 @@ export default function Modal() { return modalProps ? ( ) : null; + case 'emailExists': + return modalProps ? : null; case 'signupSuccess': return ; default: diff --git a/src/containers/signin&signup/SignUpForm.tsx b/src/containers/signin&signup/SignUpForm.tsx index deb54a7d..a116d06b 100644 --- a/src/containers/signin&signup/SignUpForm.tsx +++ b/src/containers/signin&signup/SignUpForm.tsx @@ -36,6 +36,7 @@ export default function SignUpForm() { handleSubmit, formState: { errors, isValid }, resetField, + setFocus, } = useForm({ resolver: yupResolver(schema), mode: 'onChange', @@ -47,10 +48,23 @@ export default function SignUpForm() { openModal({ type: 'signupSuccess' }); } catch (error) { if (error instanceof AxiosError && error.response?.status === 409) { - openModal({ type: 'emailExists' }); - resetField('email'); + 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 { - console.error('회원가입에 실패했습니다:', error); + openModal({ type: 'textModal', modalProps: { text: '회원가입을 실패하였습니다.' } }); } } }; 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 {