-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
3,070 additions
and
125 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
import React, { ChangeEvent, Dispatch, useEffect, useRef, useState } from 'react'; | ||
import ToBack from '../shared/sign/ToBack'; | ||
import { SignupBtnStatus } from '@/models/signupBtnStatus'; | ||
import { motion } from 'framer-motion'; | ||
import { useMutation } from 'react-query'; | ||
import { invertSecond } from '@/utils/invertSecond'; | ||
import { emailauthrequest, emailauthverify } from '@/api/auth/auth.post.api'; | ||
|
||
interface EmailCertificationProps { | ||
setStep: Dispatch<React.SetStateAction<number>>; | ||
} | ||
|
||
const EmailCertification = ({ setStep }: EmailCertificationProps) => { | ||
const [userEmail, setUserEmail] = useState<string>(''); | ||
const [emailValid, setEmailValid] = useState(false); | ||
const [btnStatus, setBtnStatus] = useState<SignupBtnStatus>('FIRST'); | ||
const [isRequest, setIsRequest] = useState(false); | ||
const [validNumber, setValidNumber] = useState<string>(''); | ||
const [validTime, setValidTime] = useState<number>(300); | ||
const [isError, setIsError] = useState(false); | ||
const [emailError, setEmailError] = useState(false); | ||
const inputRef = useRef<HTMLInputElement>(null); | ||
const startRef = useRef<HTMLInputElement>(null); | ||
|
||
const { mutateAsync: emailRequest } = useMutation((email: string) => { | ||
return emailauthrequest({ emailAddress: email }); | ||
}); | ||
|
||
const { mutateAsync: emailVerify } = useMutation( | ||
({ emailAddress, code }: { emailAddress: string; code: number }) => { | ||
return emailauthverify({ emailAddress, code }); | ||
} | ||
); | ||
|
||
const handleEmailChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
setUserEmail(e.target.value); | ||
const emailRegEx = | ||
/^[A-Za-z0-9]([-_.]?[A-Za-z0-9])*@[A-Za-z0-9]([-_.]?[A-Za-z0-9])*\.[A-Za-z]{2,3}$/; | ||
const isValid = emailRegEx.test(e.target.value); | ||
setEmailValid(isValid); | ||
}; | ||
|
||
const handleValidNumberChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
const regex = e.target.value.replace(/[^0-9]/g, ''); | ||
setValidNumber(regex); | ||
}; | ||
|
||
useEffect(() => { | ||
startRef.current?.focus(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (emailValid) { | ||
setBtnStatus('SECOND'); | ||
} else { | ||
setBtnStatus('FIRST'); | ||
} | ||
}, [emailValid]); | ||
|
||
useEffect(() => { | ||
if (isRequest) { | ||
inputRef.current?.focus(); | ||
} | ||
}, [isRequest]); | ||
|
||
useEffect(() => { | ||
let timeoutId: NodeJS.Timeout; | ||
if (isError || emailError) { | ||
timeoutId = setTimeout(() => { | ||
setIsError(false); | ||
setEmailError(false); | ||
}, 4000); | ||
} | ||
return () => clearTimeout(timeoutId); | ||
}, [isError, emailError]); | ||
|
||
useEffect(() => { | ||
let intervalId: NodeJS.Timeout; | ||
|
||
if (isRequest && validTime > 0) { | ||
intervalId = setInterval(() => { | ||
setValidTime((prevTime) => prevTime - 1); | ||
}, 1000); | ||
} | ||
|
||
return () => clearInterval(intervalId); | ||
}, [isRequest, validTime]); | ||
|
||
const handleClick = async () => { | ||
if (btnStatus == 'SECOND') { | ||
const { status } = (await emailRequest(userEmail)) as { status: string }; | ||
if (status == 'SUCCESS') { | ||
setIsRequest(true); | ||
setBtnStatus('THIRD'); | ||
} | ||
if (status == 'FAIL') { | ||
setUserEmail(''); | ||
setEmailError(true); | ||
return; | ||
} | ||
} | ||
if (btnStatus == 'THIRD') { | ||
if (validNumber.length != 6) { | ||
setValidNumber(''); | ||
setIsError(true); | ||
inputRef.current?.focus(); | ||
return; | ||
} | ||
const { status } = (await emailVerify({ | ||
emailAddress: userEmail, | ||
code: Number(validNumber) | ||
})) as { status: string }; | ||
|
||
if (status == 'SUCCESS') { | ||
setStep((prev) => prev + 1); | ||
} | ||
if (status == 'FAIL') { | ||
setValidNumber(''); | ||
setIsError(true); | ||
inputRef.current?.focus(); | ||
return; | ||
} | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="max-w-[360px] mx-auto"> | ||
<ToBack /> | ||
|
||
<motion.div | ||
initial={{ opacity: 0, translateX: -90 }} | ||
transition={{ | ||
duration: 0.4, | ||
ease: 'easeInOut', | ||
delay: 0.3 | ||
}} | ||
animate={{ | ||
opacity: 1, | ||
translateX: 0 | ||
}}> | ||
<div className="text-black text-[22px] font-semibold font-pretendard leading-[30.80px] mt-[24px] ml-4"> | ||
가입한 이메일을 <br /> | ||
입력해주세요. | ||
</div> | ||
</motion.div> | ||
|
||
<motion.div | ||
initial={{ opacity: 0, translateX: -90 }} | ||
transition={{ | ||
duration: 0.4, | ||
ease: 'easeInOut', | ||
delay: 0.6 | ||
}} | ||
animate={{ | ||
opacity: 1, | ||
translateX: 0 | ||
}}> | ||
<div className="mt-[70px] border-b border-neutral-300 ml-4"> | ||
<div className="flex mb-[13px]"> | ||
<label | ||
htmlFor="email" | ||
className="text-neutral-600 text-base font-semibold font-pretendard"> | ||
이메일 | ||
</label> | ||
<div className="w-1.5 h-1.5 bg-yellow-400 rounded-full" /> | ||
</div> | ||
<div className="pb-2 flex"> | ||
<div className="flex-grow flex items-center"> | ||
<input | ||
id="email" | ||
style={{ backgroundColor: isRequest ? 'white' : '' }} | ||
disabled={isRequest} | ||
type="text" | ||
className="outline-none" | ||
placeholder="이메일을 입력해주세요." | ||
value={userEmail} | ||
onChange={handleEmailChange} | ||
ref={startRef} | ||
/> | ||
</div> | ||
<button | ||
disabled={btnStatus == 'FIRST'} | ||
className={`w-[83px] h-[31px] px-3.5 py-1.5 rounded border justify-center items-center gap-2.5 flex text-center text-sm font-normal font-pretendard ${ | ||
btnStatus === 'FIRST' | ||
? 'bg-white text-zinc-400' | ||
: 'bg-space-purple text-white' | ||
}`} | ||
onClick={handleClick}> | ||
{btnStatus == 'THIRD' ? '인증확인' : '인증전송'} | ||
</button> | ||
</div> | ||
</div> | ||
{emailError ? ( | ||
<div className="flex ml-4"> | ||
<div className="text-red-700 text-xs font-normal font-pretendard leading-tight"> | ||
*등록되지 않은 이메일입니다. | ||
</div> | ||
</div> | ||
) : ( | ||
'' | ||
)} | ||
</motion.div> | ||
|
||
{isRequest && ( | ||
<> | ||
<div className="mt-[48px] border-b border-neutral-300 ml-4"> | ||
{isError ? ( | ||
<div className="flex flex-row-reverse"> | ||
<div className="text-red-700 text-xs font-normal font-pretendard leading-tight"> | ||
*올바르지 않은 코드입니다. | ||
</div> | ||
</div> | ||
) : ( | ||
<div className="pt-[15px]" /> | ||
)} | ||
<div className="pb-2 flex"> | ||
<div className="flex-grow flex"> | ||
<input | ||
disabled={validTime == 0} | ||
type="tel" | ||
maxLength={6} | ||
className="outline-none" | ||
placeholder="6자리 숫자 입력 " | ||
ref={inputRef} | ||
value={validNumber} | ||
onChange={handleValidNumberChange} | ||
/> | ||
</div> | ||
<div className="text-red-700 text-base font-medium font-pretendard"> | ||
{invertSecond(validTime)} | ||
</div> | ||
</div> | ||
</div> | ||
<div className="flex items-center mt-2 gap-2 ml-4"> | ||
<div className="w-3.5 h-3.5 relative"> | ||
<div className="w-3.5 h-3.5 left-0 top-0 absolute bg-slate-200 rounded-full"> | ||
<img src="/sign/emailerror.png" alt="" /> | ||
</div> | ||
</div> | ||
<div className="text-neutral-400 text-sm font-normal font-pretendard leading-tight"> | ||
이메일로 발송된 코드를 입력해주세요. | ||
</div> | ||
</div> | ||
</> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default EmailCertification; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import Link from 'next/link'; | ||
import React from 'react'; | ||
import { IoIosArrowRoundBack } from 'react-icons/io'; | ||
|
||
const ToBack = () => { | ||
return ( | ||
<div className="w-[80px] mt-[60px]"> | ||
<Link href={'/sign'}> | ||
<IoIosArrowRoundBack size={80} /> | ||
</Link> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ToBack; |
Oops, something went wrong.