Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/components/Modal/EmailExistModal.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className='h-[220px] w-[327px] rounded-[8px] bg-white px-[28px] py-[32px] md:h-[250px] md:w-[540px]'>
<div className='relative flex size-full items-center justify-center'>
<h1 className='mb-[15px] text-[16px] text-black-33 md:text-[18px]'>중복된 이메일 입니다.</h1>
<ModalActionButton
className='absolute bottom-0 right-1/2 translate-x-1/2 md:right-0 md:translate-x-0'
onClick={handleActionButton}
>
확인
</ModalActionButton>
</div>
</div>
);
}
27 changes: 27 additions & 0 deletions src/components/Modal/SignupSuccessModal.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className='h-[220px] w-[327px] rounded-[8px] bg-white px-[28px] py-[32px] md:h-[250px] md:w-[540px]'>
<div className='relative flex size-full items-center justify-center'>
<h1 className='mb-[15px] text-[16px] text-black-33 md:text-[18px]'>가입이 완료되었습니다!</h1>
<ModalActionButton
className='absolute bottom-0 right-1/2 translate-x-1/2 md:right-0 md:translate-x-0'
onClick={() => {
dispatch(closeModal());
router.push('/signin');
}}
>
로그인
</ModalActionButton>
</div>
</div>
);
}
26 changes: 26 additions & 0 deletions src/components/Modal/TextModal.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLButtonElement>;
modalProps: TextModalProps;
}) {
return (
<div className='h-[220px] w-[327px] rounded-[8px] bg-white px-[28px] py-[32px] md:h-[250px] md:w-[540px]'>
<div className='relative flex size-full items-center justify-center'>
<h1 className='mb-[15px] text-[16px] text-black-33 md:text-[18px]'>{modalProps.text}</h1>
<ModalActionButton
className='absolute bottom-0 right-1/2 translate-x-1/2 md:right-0 md:translate-x-0'
onClick={handleCloseModal}
>
확인
</ModalActionButton>
</div>
</div>
);
}
18 changes: 14 additions & 4 deletions src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ 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';
import {
ColumnDeleteModalProps,
ColumnModifyModalProps,
DeleteDashboardModalProps,
EmailExistModalProps,
InviteMemberModalProps,
NewColumnModalProps,
TextModalProps,
} from '@/types/Modal.interface';

export default function Modal() {
Expand Down Expand Up @@ -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':
Expand All @@ -64,6 +66,10 @@ export default function Modal() {
case 'columnModifySuccess':
case 'columnModifyFailed':
return <NotificationModal handleCloseModal={handleCloseModal} notificationText={NOTIFICATION_TEXT_OBJ[type]} />;
case 'textModal':
return modalProps ? (
<TextModal handleCloseModal={handleCloseModal} modalProps={modalProps as TextModalProps} />
) : null;
case 'newDashboard':
return <NewDashboardModal handleCloseModal={handleCloseModal} />;
case 'deleteDashboard':
Expand All @@ -89,6 +95,10 @@ export default function Modal() {
return modalProps ? (
<ColumnModifyModal handleCloseModal={handleCloseModal} modalProps={modalProps as ColumnModifyModalProps} />
) : null;
case 'emailExists':
return modalProps ? <EmailExistModal modalProps={modalProps as EmailExistModalProps} /> : null;
case 'signupSuccess':
return <SignUpSuccessModal />;
default:
return <DefaultModal handleCloseModal={handleCloseModal} />;
}
Expand All @@ -98,7 +108,7 @@ export default function Modal() {
<>
{type && (
<div
className='fixed inset-0 flex items-center justify-center bg-black-17 bg-opacity-[0.3] backdrop-blur-[2px]'
className='fixed inset-0 z-50 flex items-center justify-center bg-black-17 bg-opacity-[0.3] backdrop-blur-[2px]'
onClick={handleOutsideClick}
>
{renderModalContent()}
Expand Down
1 change: 0 additions & 1 deletion src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// 알림 모달용 상수
export const NOTIFICATION_TEXT_OBJ = {
pwdNotEqual: '비밀번호가 일치하지 않습니다.',
signupSuccess: '가입이 완료되었습니다!',
emailExists: '이미 사용 중인 이메일입니다.',
curPwdNotEqual: '현재 비밀번호가 틀렸습니다.',
columnDeleteConfirm: '컬럼의 모든 카드가 삭제됩니다.',
Expand Down
44 changes: 27 additions & 17 deletions src/containers/signin&signup/SignUpForm.tsx
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -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<TSignUpInputs>({
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: '회원가입을 실패하였습니다.' } });
}
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/pages/signup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default function SignUp() {
<div className='w-[350px] items-center justify-center md:w-[520px]'>
<TopLogoSection text='첫 방문을 환영합니다!' />
<SignUpForm />
<p className='text-black_33 my-[50px] text-center'>
<p className='my-[50px] text-center text-black-33'>
이미 가입하셨나요?{' '}
<Link href='/signin' className='text-violet underline'>
로그인하기
Expand Down
12 changes: 12 additions & 0 deletions src/types/Modal.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down