Skip to content
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

Feat: #65 회원가입 폼 UI 수정 및 관련 로직 수정 #66

Merged
merged 35 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0543bb0
Feat: #35 ID 찾기 페이지 마크업 및 스타일링
Yoonyesol Jul 20, 2024
a9a7ae9
Merge branch 'develop' of https://github.com/GU-99/grow-up-fe into fe…
Yoonyesol Jul 20, 2024
cf0f406
Feat: #35 PW 찾기 페이지 마크업 및 스타일링
Yoonyesol Jul 20, 2024
0a9ced4
Refactor: #35 로그인, ID, PW 찾기 페이지 중복 추출 컴포넌트 제작 및 적용
Yoonyesol Jul 20, 2024
9f75700
Refactor: #35 인증 페이지 전반적으로 겹치는 스타일링 분리
Yoonyesol Jul 20, 2024
e5d334a
Formatting: #35 로그인 페이지의 email 필드를 id 필드로 변경
Yoonyesol Jul 20, 2024
42182f4
UI: #35 공용 버튼에서 그림자 제거
Yoonyesol Jul 20, 2024
5ff21d5
UI: #35 로그인, ID/PW 찾기 페이지 리뷰 반영 디자인 수정
Yoonyesol Jul 22, 2024
f4dd9e7
Feat: #35 닉네임 validation 추가 및 기존 validation, 정규식 관련 코드 수정
Yoonyesol Jul 22, 2024
994aa58
Refactor: #35 폼 하단 공통 링크 컴포넌트 중복제거 리팩토링
Yoonyesol Jul 27, 2024
fe9f804
Refactor: #35 회원가입 페이지 비밀번호 확인 validate 분리 및 공용Form 컴포넌트 props 타입 수정
Yoonyesol Aug 3, 2024
1ad11bc
Merge branch 'develop' of https://github.com/GU-99/grow-up-fe into fe…
Yoonyesol Aug 3, 2024
a74a3fd
UI: #65 변경된 회원가입 로직에 따라 UI 변경 및 폼 전송 로직 수정
Yoonyesol Aug 3, 2024
2f3c083
Feat: #65 링크 추가 key값 부여방식과 링크 삭제 로직 변경
Yoonyesol Aug 3, 2024
fbc6d5f
Feat: #65 회원가입 이메일 인증 초시계 구현
Yoonyesol Aug 3, 2024
3b72a5b
Feat: #65 이미지 전송 로직 추가
Yoonyesol Aug 3, 2024
1b2907e
Feat: #65 회원가입 form 전송 로직 수정 및 회원가입 API 적용
Yoonyesol Aug 3, 2024
f80b9e2
Fix: #65 이미지 제한 크기 수정
Yoonyesol Aug 5, 2024
81faf44
Fix: #65 기능명세서에 따라 등록 가능 링크의 개수를 수정
Yoonyesol Aug 5, 2024
ff57fdc
Merge branch 'develop' of https://github.com/GU-99/grow-up-fe into fe…
Yoonyesol Aug 5, 2024
1e97e33
Fix: #65 병합 컨플릭트 해결
Yoonyesol Aug 5, 2024
f604a9c
Refactor: #65 Timer 컴포넌트 리뷰 반영 리팩토링
Yoonyesol Aug 6, 2024
a516cc7
Refactor: #65 이미지파일 관련 상수 분리 및 early return 패턴과 삼항연산자 적용
Yoonyesol Aug 7, 2024
a59faa8
Feat: #65 안내메시지 노출 방식을 toast 메시지로 변경 및 단위 상수분리
Yoonyesol Aug 7, 2024
dbd00a2
Refactor: #65 회원가입 폼의 인증요청과 회원가입 버튼 분리 및 인증과 폼제출 로직 분리
Yoonyesol Aug 8, 2024
1cd3e38
Feat: #65 form 전송 함수의 이미지 전송 api, 폼 전송 api 에러처리 로직 수정
Yoonyesol Aug 8, 2024
73588e7
Refactor: #65 유저 설정값 상수화 및 정규표현식 수정
Yoonyesol Aug 8, 2024
1f758e5
Merge branch 'develop' of https://github.com/GU-99/grow-up-fe into fe…
Yoonyesol Aug 8, 2024
1cf7603
Merge branch 'develop' of https://github.com/GU-99/grow-up-fe into fe…
Yoonyesol Aug 8, 2024
85aa861
Fix: #65 정규식 관련 코드 수정
Yoonyesol Aug 10, 2024
466a908
Comment: #65 회원가입 로직 관련 주석 추가
Yoonyesol Aug 10, 2024
b9ea800
Feature: #65 이미지 상수를 명시적으로 변경 및 파일사이즈 string 변환 함수 생성 적용
Yoonyesol Aug 10, 2024
1e53ed0
Refactor: #65 유저 인증 폼 관련 유효성 검증 상수 분리
Yoonyesol Aug 10, 2024
32f0b70
Refactor: #65 회원가입 폼 submit 버튼 변경을 삼항연산자로 처리
Yoonyesol Aug 10, 2024
1fa65ec
Merge branch 'develop' into feature/#65-modify-signup-ui
Yoonyesol Aug 12, 2024
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
32 changes: 32 additions & 0 deletions src/components/common/Timer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useState, useEffect } from 'react';

type TimerProps = {
time: number;
onTimeout: () => void;
};

export default function Timer({ time, onTimeout }: TimerProps) {
const [timeLeft, setTimeLeft] = useState(time);

useEffect(() => {
if (timeLeft === 0) {
onTimeout();
return;
}
const timer = setInterval(() => {
setTimeLeft((prevTime) => prevTime - 1);
}, 1000);

return () => clearInterval(timer);
}, [timeLeft, onTimeout]);

const formatTime = (seconds: number) => {
const minutes = Math.floor(seconds / 60)
.toString()
.padStart(2, '0');
const remainingSeconds = (seconds % 60).toString().padStart(2, '0');
return `${minutes}:${remainingSeconds}`;
};

return <p className="text-sm font-regular text-error">{formatTime(timeLeft)}</p>;
}
48 changes: 24 additions & 24 deletions src/constants/formValidationRules.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Validator from '@utils/Validator';
import { deepFreeze } from '@utils/deepFreeze';
import { EMAIL_REGEX, ID_REGEX, NICKNAME_REGEX, PASSWORD_REGEX, PHONE_REGEX } from '@constants/regex';

import type { Project } from '@/types/ProjectType';
import type { Task } from '@/types/TaskType';
import { EMAIL_REGEX, ID_REGEX, NICKNAME_REGEX, PASSWORD_REGEX } from './regex';
import { USER_SETTINGS } from './userSettings';
import { Project } from '@/types/ProjectType';
import { Task } from '@/types/TaskType';

type ValidateOption = { [key: string]: (value: string) => string | boolean };

Expand Down Expand Up @@ -55,30 +55,30 @@ export const STATUS_VALIDATION_RULES = deepFreeze({
Validator.isDuplicatedName(colorList, value) ? '이미 사용중인 색상입니다.' : true,
},
}),
});

export const USER_AUTH_VALIDATION_RULES = deepFreeze({
EMAIL: {
required: '이메일 인증을 진행해 주세요.',
maxLength: {
value: USER_SETTINGS.MAX_EMAIL_LENGTH,
message: `이메일은 최대 ${USER_SETTINGS.MAX_EMAIL_LENGTH}자까지 입력 가능합니다.`,
},
pattern: {
value: EMAIL_REGEX,
message: '이메일 형식에 맞지 않습니다.',
},
},
CERTIFICATION: { required: '인증번호를 입력해 주세요.' },
PHONE: {
required: '휴대폰 번호 인증을 진행해 주세요.',
pattern: {
value: PHONE_REGEX,
message: '휴대폰 번호를 정확히 입력해 주세요.',
},
},
NICKNAME: {
required: '닉네임을 입력해 주세요.',
minLength: {
value: 2,
message: '닉네임은 최소 2자 이상이어야 합니다.',
value: USER_SETTINGS.MIN_NICKNAME_LENGTH,
message: `닉네임은 최소 ${USER_SETTINGS.MIN_NICKNAME_LENGTH}자 이상이어야 합니다.`,
},
maxLength: {
value: 20,
message: '닉네임은 최대 20자까지 입력 가능합니다.',
value: USER_SETTINGS.MAX_NICKNAME_LENGTH,
message: `닉네임은 최대 ${USER_SETTINGS.MAX_NICKNAME_LENGTH}자까지 입력 가능합니다.`,
},
pattern: {
value: NICKNAME_REGEX,
Expand All @@ -88,16 +88,16 @@ export const STATUS_VALIDATION_RULES = deepFreeze({
PASSWORD: {
required: '비밀번호를 입력해 주세요.',
minLength: {
value: 8,
message: '비밀번호는 최소 8자 이상이어야 합니다.',
value: USER_SETTINGS.MIN_PW_LENGTH,
message: `비밀번호는 최소 ${USER_SETTINGS.MIN_PW_LENGTH}자 이상이어야 합니다.`,
},
maxLength: {
value: 16,
message: '비밀번호는 최대 16자 이하여야 합니다.',
value: USER_SETTINGS.MAX_PW_LENGTH,
message: `비밀번호는 최대 ${USER_SETTINGS.MAX_PW_LENGTH}자 이하여야 합니다.`,
},
pattern: {
value: PASSWORD_REGEX,
message: '비밀번호는 영문자, 숫자, 기호를 모두 포함해야 합니다.',
message: '영문자, 숫자, 기호를 조합해 비밀번호를 생성해주세요.',
},
},
PASSWORD_CONFIRM: (password: string) => ({
Expand All @@ -107,12 +107,12 @@ export const STATUS_VALIDATION_RULES = deepFreeze({
ID: {
required: '아이디를 입력해 주세요.',
minLength: {
value: 2,
message: '아이디는 최소 2자 이상이어야 합니다.',
value: USER_SETTINGS.MIN_ID_LENGTH,
message: `아이디는 최소 ${USER_SETTINGS.MIN_ID_LENGTH}자 이상이어야 합니다.`,
},
maxLength: {
value: 255,
message: '아이디는 최대 255자 이하여야 합니다.',
value: USER_SETTINGS.MAX_ID_LENGTH,
message: `아이디는 최대 ${USER_SETTINGS.MAX_ID_LENGTH}자 이하여야 합니다.`,
},
pattern: {
value: ID_REGEX,
Expand Down
13 changes: 9 additions & 4 deletions src/constants/regex.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
export const EMAIL_REGEX = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}(?:\.[a-z]{2,})?$/i;
export const PASSWORD_REGEX =
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[~`!@#$%^&*()_\-+={[}\]|\\:;"'<,>.?/])[A-Za-z\d~`!@#$%^&*()_\-+={[}\]|\\:;"'<,>.?/]{8,16}$/;
import { USER_SETTINGS } from './userSettings';

export const EMAIL_REGEX = /^[a-z0-9._%+-]+@[a-z0-9-]+\.[a-z]{2,3}(?:\.[a-z]{2,3})?$/i;
export const PHONE_REGEX = /^01([0|1|6|7|8|9])-?([0-9]{3,4})-?([0-9]{4})$/;
export const NICKNAME_REGEX = /^[a-zA-Z0-9가-힣]+$/;
export const PASSWORD_REGEX = new RegExp(
`^(?=.*[A-Za-z])(?=.*\\d)(?=.*[~\`!@#$%^&*()_\\-+={[}\\]|\\\\:;"'<,>.?/])[A-Za-z\\d~\`!@#$%^&*()_\\-+={[}\\]|\\\\:;"'<,>.?/]{${USER_SETTINGS.MIN_PW_LENGTH},${USER_SETTINGS.MAX_PW_LENGTH}}$`,
);
export const NICKNAME_REGEX = new RegExp(
`^[a-zA-Z0-9가-힣]{${USER_SETTINGS.MIN_NICKNAME_LENGTH},${USER_SETTINGS.MAX_NICKNAME_LENGTH}}$`,
);
export const ID_REGEX = /^[a-z0-9._+@가-힣-]+(?:@[a-z0-9.-]+\.[a-z]{2,}(?:\.[a-z]{2,}))?$/i;
10 changes: 10 additions & 0 deletions src/constants/units.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const KB = 1024;
export const MB = 1024 * KB;
export const GB = 1024 * MB;

export const fileSizeUnits = Object.freeze([
{ unit: 'GB', value: GB },
{ unit: 'MB', value: MB },
{ unit: 'KB', value: KB },
{ unit: 'B', value: 1 },
]);
13 changes: 13 additions & 0 deletions src/constants/userSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { MB } from './units';

export const USER_SETTINGS = Object.freeze({
MAX_IMAGE_SIZE: 2 * MB,
MAX_LINK_COUNT: 5,
MIN_ID_LENGTH: 2,
MAX_ID_LENGTH: 32,
MIN_PW_LENGTH: 8,
MAX_PW_LENGTH: 16,
MIN_NICKNAME_LENGTH: 2,
MAX_NICKNAME_LENGTH: 20,
MAX_EMAIL_LENGTH: 128,
});
6 changes: 3 additions & 3 deletions src/pages/user/SearchIdPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useForm } from 'react-hook-form';
import ValidationInput from '@/components/common/ValidationInput';
import { STATUS_VALIDATION_RULES } from '@/constants/formValidationRules';
import { USER_AUTH_VALIDATION_RULES } from '@/constants/formValidationRules';
import { SearchIDForm } from '@/types/UserType';
import AuthForm from '@/components/user/authForm/AuthForm';
import FooterLinks from '@/components/user/authForm/FooterLinks';
Expand Down Expand Up @@ -30,14 +30,14 @@ export default function SearchIdPage() {
buttonLabel="인증번호 발송"
placeholder="이메일"
errors={errors.email?.message}
register={register('email', STATUS_VALIDATION_RULES.EMAIL)}
register={register('email', USER_AUTH_VALIDATION_RULES.EMAIL)}
/>

{/* 이메일 인증 */}
<ValidationInput
placeholder="인증번호"
errors={errors.code?.message}
register={register('code', STATUS_VALIDATION_RULES.CERTIFICATION)}
register={register('code', USER_AUTH_VALIDATION_RULES.CERTIFICATION)}
/>

<div className="flex flex-col text-center">
Expand Down
12 changes: 6 additions & 6 deletions src/pages/user/SearchPasswordPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useForm } from 'react-hook-form';
import ValidationInput from '@/components/common/ValidationInput';
import { STATUS_VALIDATION_RULES } from '@/constants/formValidationRules';
import { USER_AUTH_VALIDATION_RULES } from '@/constants/formValidationRules';
import AuthForm from '@/components/user/authForm/AuthForm';
import FooterLinks from '@/components/user/authForm/FooterLinks';
import { SearchPasswordForm } from '@/types/UserType';
Expand All @@ -13,7 +13,7 @@ export default function SearchPasswordPage() {
} = useForm<SearchPasswordForm>({
mode: 'onChange',
defaultValues: {
id: '',
userId: '',
email: '',
code: '',
},
Expand All @@ -28,8 +28,8 @@ export default function SearchPasswordPage() {
{/* 아이디 */}
<ValidationInput
placeholder="아이디"
errors={errors.id?.message}
register={register('id', STATUS_VALIDATION_RULES.ID)}
errors={errors.userId?.message}
register={register('userId', USER_AUTH_VALIDATION_RULES.ID)}
/>

{/* 이메일 */}
Expand All @@ -38,14 +38,14 @@ export default function SearchPasswordPage() {
buttonLabel="인증번호 발송"
placeholder="이메일"
errors={errors.email?.message}
register={register('email', STATUS_VALIDATION_RULES.EMAIL)}
register={register('email', USER_AUTH_VALIDATION_RULES.EMAIL)}
/>

{/* 이메일 인증 */}
<ValidationInput
placeholder="인증번호"
errors={errors.code?.message}
register={register('code', STATUS_VALIDATION_RULES.CERTIFICATION)}
register={register('code', USER_AUTH_VALIDATION_RULES.CERTIFICATION)}
/>

<div className="flex flex-col text-center">
Expand Down
12 changes: 6 additions & 6 deletions src/pages/user/SignInPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Kakao from '@assets/social_kakao_icon.svg';
import Google from '@assets/social_google_icon.svg';
import { UserSignInForm } from '@/types/UserType';
import ValidationInput from '@/components/common/ValidationInput';
import { STATUS_VALIDATION_RULES } from '@/constants/formValidationRules';
import { USER_AUTH_VALIDATION_RULES } from '@/constants/formValidationRules';
import AuthForm from '@/components/user/authForm/AuthForm';
import FooterLinks from '@/components/user/authForm/FooterLinks';

Expand All @@ -15,7 +15,7 @@ export default function SignInPage() {
} = useForm({
mode: 'onChange',
defaultValues: {
id: '',
userId: '',
password: '',
},
});
Expand All @@ -26,20 +26,20 @@ export default function SignInPage() {

return (
<>
<AuthForm onSubmit={handleSubmit(onSubmit)} styles="mt-40">
<AuthForm onSubmit={handleSubmit(onSubmit)} marginTop="mt-40">
{/* 아이디 */}
<ValidationInput
placeholder="아이디"
errors={errors.id?.message}
register={register('id', STATUS_VALIDATION_RULES.ID)}
errors={errors.userId?.message}
register={register('userId', USER_AUTH_VALIDATION_RULES.ID)}
/>

{/* 비밀번호 */}
<ValidationInput
placeholder="비밀번호 (영문자, 숫자, 기호 포함 8~16자리)"
type="password"
errors={errors.password?.message}
register={register('password', STATUS_VALIDATION_RULES.PASSWORD)}
register={register('password', USER_AUTH_VALIDATION_RULES.PASSWORD)}
/>

<div className="flex flex-col text-center">
Expand Down
Loading