Skip to content

Commit

Permalink
[Feat] QFEED-140 회원가입, 아이디찾기, 비밀번호 찾기 api 연동 (#75)
Browse files Browse the repository at this point in the history
* fix: qfeed-140 회원가입 오류 수정 - 비밀번호 포맷 확인 추가

* feat: qfeed-140 아이디찾기, 비밀번호 인증 페이지 구현

* feat: qfeed-140 비밀번호 재설정
  • Loading branch information
jymaeng1234 authored Dec 17, 2024
1 parent 63037bf commit f6ddb61
Show file tree
Hide file tree
Showing 19 changed files with 391 additions and 69 deletions.
1 change: 1 addition & 0 deletions src/api/queryKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const AUTH_KEYS = {
LOGOUT: 'logout',
REGISTER: 'register',
REFRESH: 'refresh',
RESETPASSWORD: 'resetpassword',
},
} as const;

Expand Down
41 changes: 41 additions & 0 deletions src/pages/IDRecovery/component/InputForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
StyledFormControl,
StyledFormErrorMessage,
StyledInput,
} from '@/pages/Login/components/LoginForm/LoginForm.styles';
import { VerificationWrapper } from '@/pages/Register/components/ConfirmForm/ConfirmForm.styles';
import { FormValues } from '@/pages/Register/type/formType';
import theme from '@/styles/theme';
import { FormLabel } from '@chakra-ui/react';
import { UseFormRegister, FieldErrors } from 'react-hook-form';

type EmailFormProps = {
register: UseFormRegister<FormValues>;
errors: FieldErrors<FormValues>;
};

export const InputForm = ({ register, errors }: EmailFormProps) => {
return (
<StyledFormControl isInvalid={!!errors.email}>
<FormLabel>이메일</FormLabel>
<VerificationWrapper>
<StyledInput
borderColor={theme.colors.primary}
focusBorderColor={theme.colors.primary}
color={theme.colors.gray[300]}
background={theme.colors.white}
placeholder="이메일을 입력해주세요."
{...register('email', {
required: '이메일을 입력해주세요',
pattern: {
value: /\S+@\S+\.\S+/,
message: '올바른 이메일 형식이 아닙니다',
},
})}
type="email"
/>
</VerificationWrapper>
<StyledFormErrorMessage>{errors.email?.message}</StyledFormErrorMessage>
</StyledFormControl>
);
};
25 changes: 25 additions & 0 deletions src/pages/IDRecovery/component/inputForm.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { STYLES } from '@/pages/Login/Constants/styles';
import styled from '@emotion/styled';
import theme from '@/styles/theme';
import { FormControl, FormErrorMessage, Input } from '@chakra-ui/react';

export const StyledInput = styled(Input)`
width: 100%;
max-width: ${STYLES.INPUT.MAX_WIDTH};
min-height: ${STYLES.INPUT.MIN_HEIGHT};
border-radius: ${STYLES.BUTTONLOGINFORM.BORDER_RADIUS};
color: ${theme.colors.gray[300]};
background: ${theme.colors.white};
padding: 0 ${STYLES.INPUT.PADDING};
`;

export const StyledFormControl = styled(FormControl)`
height: ${STYLES.FORM.CONTROL_HEIGHT};
position: relative;
margin-bottom: ${STYLES.FORM.MARGIN_BOTTOM};
`;

export const StyledFormErrorMessage = styled(FormErrorMessage)`
position: absolute;
bottom: 0;
`;
45 changes: 37 additions & 8 deletions src/pages/IDRecovery/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,70 @@ import { HeaderWithTitle } from '@/components/ui/HeaderWithTitle/HeaderWithTitle
import LoginButton from '@/pages/Login/components/LoginButton/LoginButton';
import { StyledHStack, TextButton } from '@/pages/Login/styles';
import { ContentWrapper } from '@/pages/PasswordRecovery/styles';
import { EmailForm } from '@/pages/Register/components/EmailForm/EmailForm';
import { FormValues } from '@/pages/Register/type/formType';
import theme from '@/styles/theme';
import styled from '@emotion/styled';
import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router';
import { useNavigation } from '@/hooks/useNavigation';
import { useCheckEmailExist } from '@/pages/Register/hooks/useCheckEmailExist';
import { InputForm } from '@/pages/IDRecovery/component/InputForm';
import { useState } from 'react';
import { ErrorMessage } from '@/pages/IDRecovery/styles';
import { EMAIL_REGEX } from '@/utils/registerRegex';

export const IDRecoveryPage = () => {
const navigate = useNavigate();
const { gotoLogin, gotoRegisterPage, gotoPasswordRecoveryPage } = useNavigation();
const {
register,
formState: { errors },
getValues,
} = useForm<FormValues>();
const { mutate: checkEmail } = useCheckEmailExist();
const [emailMessage, setEmailMessage] = useState<string>('');

const handleOnClick = () => {
alert('아이디 찾기 버튼 클릭');
const emailValue = getValues('email');

if (emailValue == '') {
setEmailMessage('이메일 계정을 입력해주세요');
return;
}

if (!EMAIL_REGEX.test(emailValue)) {
setEmailMessage('올바른 이메일 형식이 아닙니다.');
return;
}

checkEmail(emailValue, {
onSuccess: (isAvailable) => {
setEmailMessage(isAvailable ? '회원가입 이력이 없습니다.' : '이미 가입된 이메일입니다.');
},
onError: (error) => {
console.log(error);

setEmailMessage('올바른 이메일 형식이 아닙니다.');
},
});
};

const handleFindPassword = () => {
navigate('/account-recovery/password');
gotoPasswordRecoveryPage();
};

const handleLogin = () => {
navigate('/login');
gotoLogin();
};

const handleRegister = () => {
navigate('/register');
gotoRegisterPage();
};

return (
<Container>
<HeaderWithTitle title="아이디 찾기" />
<ContentWrapper>
<EmailForm register={register} errors={errors} onVerify={handleOnClick} />
<InputForm register={register} errors={errors} />
{emailMessage && <ErrorMessage>{emailMessage}</ErrorMessage>}{' '}
<LoginButton text="아이디 확인하기" onClick={handleOnClick} />
</ContentWrapper>

Expand Down
7 changes: 7 additions & 0 deletions src/pages/IDRecovery/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styled from '@emotion/styled';
export const ErrorMessage = styled.p`
size: 0.875rem;
color: red;
text-align: center;
width: 100%;
`;
41 changes: 41 additions & 0 deletions src/pages/PasswordRecovery/components/EmailForm/EmailForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
StyledFormControl,
StyledFormErrorMessage,
StyledInput,
} from '@/pages/Login/components/LoginForm/LoginForm.styles';
import { VerificationWrapper } from '@/pages/Register/components/ConfirmForm/ConfirmForm.styles';
import { FormValues } from '@/pages/Register/type/formType';
import theme from '@/styles/theme';
import { FormLabel } from '@chakra-ui/react';
import { UseFormRegister, FieldErrors } from 'react-hook-form';

type EmailFormProps = {
register: UseFormRegister<FormValues>;
errors: FieldErrors<FormValues>;
};

export const EmailForm = ({ register, errors }: EmailFormProps) => {
return (
<StyledFormControl isInvalid={!!errors.email}>
<FormLabel>이메일</FormLabel>
<VerificationWrapper>
<StyledInput
borderColor={theme.colors.primary}
focusBorderColor={theme.colors.primary}
color={theme.colors.gray[300]}
background={theme.colors.white}
placeholder="이메일을 입력해주세요."
{...register('email', {
required: '이메일을 입력해주세요',
pattern: {
value: /\S+@\S+\.\S+/,
message: '올바른 이메일 형식이 아닙니다',
},
})}
type="email"
/>
</VerificationWrapper>
<StyledFormErrorMessage>{errors.email?.message}</StyledFormErrorMessage>
</StyledFormControl>
);
};
78 changes: 66 additions & 12 deletions src/pages/PasswordRecovery/index.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,106 @@
import { HeaderWithTitle } from '@/components/ui/HeaderWithTitle/HeaderWithTitle';
import { useNavigation } from '@/hooks/useNavigation';
import { ErrorMessage } from '@/pages/IDRecovery/styles';
import { EmailForm } from '@/pages/PasswordRecovery/components/EmailForm/EmailForm';
import {
Container,
ContentWrapper,
StyledHStack,
TextButton,
} from '@/pages/PasswordRecovery/styles';
import { ConfirmForm } from '@/pages/Register/components/ConfirmForm/ConfirmForm';
import { EmailForm } from '@/pages/Register/components/EmailForm/EmailForm';
import LoginButton from '@/pages/Register/components/LoginButton/LoginButton';
import { useCheckEmailExist } from '@/pages/Register/hooks/useCheckEmailExist';
import { useSendEmail } from '@/pages/Register/hooks/useSendEmail';
import { useVerifyEmailCode } from '@/pages/Register/hooks/useVerifyEmailCode';
import { FormValues } from '@/pages/Register/type/formType';
import { EMAIL_REGEX } from '@/utils/registerRegex';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router';

export const PasswordRecoveryPage = () => {
const navigate = useNavigate();
const { gotoResetPasswordPage, gotoPasswordRecoveryPage, gotoLogin, gotoRegisterPage } =
useNavigation();

const {
register,
formState: { errors },
getValues,
} = useForm<FormValues>();

const { mutate: checkEmail } = useCheckEmailExist();
const { mutate: sendEmail } = useSendEmail();
const { mutate: verifyEmail } = useVerifyEmailCode();
const [email, setEmail] = useState('');
const [emailMessage, setEmailMessage] = useState<string>('');

const handleSendEmail = () => {
const emailValue = getValues('email');
setEmail(emailValue);

if (emailValue == '') {
setEmailMessage('가입한 이메일 주소를 입력하세요');
return;
}

if (!EMAIL_REGEX.test(emailValue)) {
setEmailMessage('올바른 이메일 형식이 아닙니다.');
return;
}

checkEmail(emailValue, {
onSuccess: (isAvailable) => {
if (!isAvailable) {
sendEmail(emailValue);
alert('인증 이메일이 전송되었습니다.');
} else {
setEmailMessage(`가입한 이력이 없습니다.`);
return;
}
},
onError: (error) => {
console.error('이메일 확인 실패:', error);
},
});
};

const handleVerify = () => {
const verificationCode = getValues('verificationCode');
console.log(`인증번호:${verificationCode}`);
navigate('/account-reset/password');
};

const handleSendEmail = () => {
alert('이메일을 보냈습니다');
verifyEmail(
{ email, code: verificationCode },
{
onSuccess: () => {
alert('이메일 인증이 완료되었습니다.');
localStorage.setItem('recoveryEmail', email);
localStorage.setItem('verificationCode', verificationCode);
gotoResetPasswordPage();
},
onError: () => {
alert('인증번호가 일치하지 않습니다.');
},
}
);
};

const handleFindEmail = () => {
navigate('/account-recovery/id');
gotoPasswordRecoveryPage();
};

const handleLogin = () => {
navigate('/login');
gotoLogin();
};

const handleRegister = () => {
navigate('/register');
gotoRegisterPage();
};

return (
<Container>
<HeaderWithTitle title="비밀번호 찾기" />
<ContentWrapper>
<EmailForm register={register} errors={errors} onVerify={handleVerify} />
<EmailForm register={register} errors={errors} />
{emailMessage && <ErrorMessage>{emailMessage}</ErrorMessage>}{' '}
<ConfirmForm register={register} errors={errors} onVerify={handleVerify} />
<LoginButton text="비밀번호 재설정 이메일 보내기" onClick={handleSendEmail} />
</ContentWrapper>
Expand Down
Loading

0 comments on commit f6ddb61

Please sign in to comment.