-
Notifications
You must be signed in to change notification settings - Fork 2
[feat] 회원가입 반응형 UI #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7eb6f6e
2660a27
b21b008
b695df4
370137a
29cba70
652c07f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,15 @@ | ||
| // c:\Users\shinwookKang\Desktop\CheckMo\FE\src\components\base-ui\Join\steps\EmailVerification\EmailVerification.tsx | ||
|
|
||
| "use client"; | ||
|
|
||
| import React from "react"; | ||
| import JoinHeader from "../../JoinHeader"; | ||
| import JoinButton from "../../JoinButton"; | ||
| import JoinInput from "../../JoinInput"; | ||
| import { useEmailVerification } from "../useEmailVerification"; | ||
| import Toast from "@/components/common/Toast"; | ||
|
|
||
| interface EmailVerificationProps { | ||
| onNext?: () => void; | ||
| onNext: () => void; | ||
| } | ||
|
|
||
| const EmailVerification: React.FC<EmailVerificationProps> = ({ onNext }) => { | ||
|
|
@@ -22,16 +25,18 @@ const EmailVerification: React.FC<EmailVerificationProps> = ({ onNext }) => { | |
| isVerified, | ||
| handleVerify, | ||
| showToast, | ||
| setShowToast, | ||
| isToastVisible, | ||
| formatTime, | ||
| } = useEmailVerification(); | ||
|
|
||
| return ( | ||
| <div className="relative flex flex-col items-center w-[766px] px-[56px] py-[99px] bg-white rounded-[8px]"> | ||
| <div className="relative flex flex-col items-center mx-auto w-full max-w-[766px] bg-white rounded-[8px] px-6 py-10 md:px-[40px] md:py-[60px] lg:px-[56px] lg:py-[99px]"> | ||
| <JoinHeader title="이메일 인증" /> | ||
| <div className="flex flex-col w-full mt-[90px] mb-[130px]"> | ||
| {/* 이메일 입력 섹션 */} | ||
| <div className="flex flex-col items-center w-[526px] gap-[30px] mx-auto"> | ||
|
|
||
| {/* Content Wrapper: Mobile(mt-10 mb-10) -> Tablet(mt-[60px] mb-[80px]) -> Desktop(mt-[90px] mb-[130px]) */} | ||
| <div className="flex flex-col w-full mt-10 mb-10 md:mt-[60px] md:mb-[80px] lg:mt-[90px] lg:mb-[130px]"> | ||
| {/* Email Input Section */} | ||
| <div className="flex flex-col items-center w-full max-w-[526px] gap-[30px] mx-auto"> | ||
| <JoinInput | ||
| label="이메일" | ||
| type="email" | ||
|
|
@@ -44,7 +49,7 @@ const EmailVerification: React.FC<EmailVerificationProps> = ({ onNext }) => { | |
| <button | ||
| onClick={startTimer} | ||
| disabled={!isEmailValid} | ||
| className={`flex justify-center items-center w-[284px] h-[48px] rounded-[8px] text-[14px] font-semibold leading-[145%] tracking-[-0.014px] ${ | ||
| className={`flex justify-center items-center w-full max-w-[284px] h-[48px] rounded-[8px] text-[14px] font-semibold leading-[145%] tracking-[-0.014px] ${ | ||
| isEmailValid | ||
| ? "bg-[#BBAA9B] text-[#FFF]" | ||
| : "bg-[#DADADA] text-[#8D8D8D]" | ||
|
|
@@ -63,8 +68,8 @@ const EmailVerification: React.FC<EmailVerificationProps> = ({ onNext }) => { | |
| </div> | ||
| </div> | ||
|
|
||
| {/* 인증번호 입력 섹션 */} | ||
| <div className="flex flex-col items-center w-[526px] gap-[30px] mx-auto mt-[53px]"> | ||
| {/* Verification Code Section */} | ||
| <div className="flex flex-col items-center w-full max-w-[526px] gap-[30px] mx-auto mt-[53px]"> | ||
| <JoinInput | ||
| label="인증번호" | ||
| type="text" | ||
|
|
@@ -76,7 +81,7 @@ const EmailVerification: React.FC<EmailVerificationProps> = ({ onNext }) => { | |
| <button | ||
| onClick={handleVerify} | ||
| disabled={!isCodeValid} | ||
| className={`flex justify-center items-center w-[284px] h-[48px] rounded-[8px] text-[14px] font-semibold leading-[145%] tracking-[-0.014px] ${ | ||
| className={`flex justify-center items-center w-full max-w-[284px] h-[48px] rounded-[8px] text-[14px] font-semibold leading-[145%] tracking-[-0.014px] ${ | ||
| isCodeValid | ||
| ? "bg-[#BBAA9B] text-[#FFF]" | ||
| : "bg-[#DADADA] text-[#8D8D8D]" | ||
|
|
@@ -89,15 +94,25 @@ const EmailVerification: React.FC<EmailVerificationProps> = ({ onNext }) => { | |
| </button> | ||
| </div> | ||
| </div> | ||
| {/* 하단 버튼 (임시로 클릭 시 다음 단계 이동 동작 연결) */} | ||
| <JoinButton onClick={onNext} disabled={!isVerified}> | ||
|
|
||
| <JoinButton | ||
| onClick={onNext} | ||
| disabled={!isVerified} | ||
| className="w-full md:w-[526px]" | ||
| > | ||
| 다음 | ||
| </JoinButton> | ||
|
|
||
| {showToast && ( | ||
| <Toast | ||
| message="인증이 완료되었습니다." | ||
| onClose={() => setShowToast(false)} | ||
| /> | ||
| <div | ||
| className={`absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50 inline-flex justify-center items-center h-auto py-4 px-8 md:h-[88px] md:px-[138px] bg-[#31111D99] rounded-[24px] backdrop-blur-[1px] transition-opacity duration-300 ${ | ||
| isToastVisible ? "opacity-100" : "opacity-0" | ||
| }`} | ||
| > | ||
| <span className="text-[#FFF] text-[16px] md:text-[18px] font-medium leading-[135%] tracking-[-0.018px] whitespace-nowrap"> | ||
| 인증이 완료되었습니다. | ||
| </span> | ||
| </div> | ||
| )} | ||
|
Comment on lines
106
to
116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 인증 완료 시 보여주는 Toast UI를 이 컴포넌트 내에 직접 구현하셨네요. 기존에 |
||
| </div> | ||
| ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,20 @@ | ||
| import { useState, useEffect } from "react"; | ||
| import { useState } from "react"; | ||
|
|
||
| export const usePasswordEntry = () => { | ||
| const [password, setPassword] = useState(""); | ||
| const [confirmPassword, setConfirmPassword] = useState(""); | ||
| const [isValid, setIsValid] = useState(false); | ||
|
|
||
| useEffect(() => { | ||
| // 6-12자, 영문 최소 1자, 특수문자 최소 1자 | ||
| const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[!@#$%^&*]).{6,12}$/; | ||
| const isPasswordValid = passwordRegex.test(password); | ||
| const isMatch = password === confirmPassword; | ||
| // 6-12자, 영문 최소 1자, 특수문자 최소 1자 | ||
| const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[!@#$%^&*]).{6,12}$/; | ||
|
|
||
| setIsValid(isPasswordValid && isMatch && password.length > 0); | ||
| }, [password, confirmPassword]); | ||
| // Derived State (No useEffect) | ||
| const isComplexityValid = passwordRegex.test(password); | ||
| const isMatch = password === confirmPassword; | ||
| const isValid = | ||
| isComplexityValid && | ||
| isMatch && | ||
| password.length > 0 && | ||
| confirmPassword.length > 0; | ||
|
Comment on lines
+8
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| setPassword(e.target.value); | ||
|
|
@@ -26,6 +28,8 @@ export const usePasswordEntry = () => { | |
| password, | ||
| confirmPassword, | ||
| isValid, | ||
| isComplexityValid, | ||
| isMatch, | ||
| handlePasswordChange, | ||
| handleConfirmChange, | ||
| }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모바일 뷰에서
h-[720px]로 고정 높이를 사용하고 계신데, 이 경우 화면이 작은 모바일 기기나 사용자가 시스템 폰트 크기를 키웠을 때 콘텐츠가 잘리거나 레이아웃이 깨질 위험이 있습니다.min-h-[720px]를 사용하시거나, 높이를 고정하지 않고 콘텐츠에 따라 유연하게 조절되도록 하는 것이 더 안전한 방법일 것 같습니다.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 바꾸면 더 좋을듯해요!