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

UI: #69 유저 정보 설정 페이지 UI 구현 #73

Merged
merged 10 commits into from
Aug 16, 2024
Merged
9,007 changes: 0 additions & 9,007 deletions package-lock.json

This file was deleted.

27 changes: 27 additions & 0 deletions src/components/sidebar/ListSetting.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NavLink } from 'react-router-dom';

type ListSettingProps = {
navList: {
label: string;
route: string;
}[];
};

export default function ListSetting({ navList }: ListSettingProps) {
return (
<ul>
{navList.map((item) => (
<li key={item.label} className="relative cursor-pointer border-b bg-white hover:brightness-90">
<NavLink
to={`/setting/${item.route}`}
className={({ isActive }) =>
`flex h-30 flex-col justify-center border-l-4 px-10 ${isActive ? 'border-l-main' : 'border-l-transparent'}`
Seok93 marked this conversation as resolved.
Show resolved Hide resolved
}
>
<span>{item.label}</span>
</NavLink>
</li>
))}
</ul>
);
}
2 changes: 1 addition & 1 deletion src/constants/formValidationRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const USER_AUTH_VALIDATION_RULES = deepFreeze({
},
pattern: {
value: PASSWORD_REGEX,
message: '영문자, 숫자, 기호를 조합해 비밀번호를 생성해주세요.',
message: '영문자, 숫자, 기호를 조합해 비밀번호를 입력해주세요.',
},
},
PASSWORD_CONFIRM: (password: string) => ({
Expand Down
48 changes: 43 additions & 5 deletions src/layouts/page/SettingLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
import { Outlet } from 'react-router-dom';
import { Outlet, useLocation } from 'react-router-dom';
import ListSidebar from '@components/sidebar/ListSidebar';
import { USER_INFO_DUMMY } from '@mocks/mockData';
import ListSetting from '@/components/sidebar/ListSetting';

const navList = [
{
label: '개인정보 변경',
route: 'user',
},
{
label: '비밀번호 변경',
route: 'password',
},
{
label: 'My Teams',
route: 'teams',
},
];

export default function SettingLayout() {
const location = useLocation();

const getTitle = () => {
const currentPath = location.pathname.split('/').slice(-1)[0];
const currentNavItem = navList.find((item) => item.route === currentPath);

return currentNavItem ? currentNavItem.label : '이메일 인증';
};

return (
<>
<h3>Setting Layout</h3>
<Outlet />
</>
<section className="flex h-full p-15">
<ListSidebar title={`${USER_INFO_DUMMY.nickname} 님의 정보`}>
<ListSetting navList={navList} />
</ListSidebar>
<section className="flex w-2/3 flex-col border border-list bg-contents-box">
<header className="flex h-30 items-center justify-between border-b p-10">
<div>
<small className="font-bold text-category">{getTitle()}</small>
</div>
</header>
<div className="h-screen overflow-auto">
<Outlet />
</div>
</section>
</section>
);
}
11 changes: 11 additions & 0 deletions src/mocks/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ export const USER_DUMMY = [
},
];

export const USER_INFO_DUMMY = {
provider: 'LOCAL',
userId: 1,
id: 'test123',
email: '[email protected]',
nickname: 'momoco',
profileUrl: '',
bio: "Hi, I'm Momoco!",
links: ['[email protected]'],
};

export const TEAM_DUMMY: Team[] = [
{
teamId: 1,
Expand Down
119 changes: 119 additions & 0 deletions src/pages/setting/UserAuthenticatePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import useToast from '@/hooks/useToast';
import ValidationInput from '@/components/common/ValidationInput';
import { USER_AUTH_VALIDATION_RULES } from '@/constants/formValidationRules';
import Timer from '@/components/common/Timer';
import { EmailVerificationForm } from '@/types/UserType';

// TODO: 회원가입 폼과 겹치는 로직 컴포넌트로 분리
function UserAuthenticatePage() {
const [isVerificationRequested, setIsVerificationRequested] = useState(false);
const [isTimerVisible, setIsTimerVisible] = useState(false);
const { toastSuccess, toastError } = useToast();

const {
register,
handleSubmit,
setError,
watch,
formState: { errors, isSubmitting },
} = useForm<EmailVerificationForm>({
mode: 'onChange',
});

// 이메일 인증번호 요청 함수
const requestCode = () => {
if (!isVerificationRequested) {
setIsVerificationRequested(true);
toastSuccess('인증번호가 발송되었습니다. 이메일을 확인해 주세요.');
setIsTimerVisible(true);
}
};

// 인증번호 체크 함수
const verifyCode = (code: string) => {
if (code === '1234') {
return true;
}

// 인증번호 불일치
setError('code', {
type: 'manual',
message: '인증번호가 일치하지 않습니다.',
});
return false;
};
Seok93 marked this conversation as resolved.
Show resolved Hide resolved

// 타이머 만료
const handleTimerTimeout = () => {
setIsTimerVisible(false);
setIsVerificationRequested(false);
toastError('인증 시간이 만료되었습니다. 다시 시도해 주세요.');
};

const onSubmit = async (data: EmailVerificationForm) => {
console.log(data);

const verifyResult = verifyCode(watch('code'));
if (!verifyResult) return toastError('인증번호가 유효하지 않습니다. 다시 시도해 주세요.');

// TODO: 인증 성공 후 전역 상태관리 및 리다이렉트 로직 작성
};

return (
<div className="flex h-full items-center justify-center">
<div className="flex w-full max-w-300 flex-col gap-20">
<p className="text-center text-sm text-emphasis">
개인정보 변경을 위한 이메일 인증 단계입니다.
<br />
인증요청 버튼 클릭 후, 이메일로 발송된 인증번호를 입력해주세요.
</p>
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-8">
{/* 이메일 */}
<ValidationInput
label="이메일"
errors={errors.email?.message}
register={register('email', USER_AUTH_VALIDATION_RULES.EMAIL)}
/>

{isVerificationRequested && (
<ValidationInput
label="인증번호"
errors={errors.code?.message}
register={register('code', USER_AUTH_VALIDATION_RULES.CERTIFICATION)}
/>
)}

{/* 인증 요청 및 확인 버튼 */}
<div className="flex flex-col gap-8 text-center">
{!isVerificationRequested ? (
<button
type="submit"
className="flex h-30 items-center justify-center rounded-lg bg-sub px-8 font-bold"
onClick={handleSubmit(requestCode)}
>
<span>인증요청</span>
</button>
) : (
<button
type="submit"
className="relative flex h-30 items-center justify-center rounded-lg bg-sub px-8 font-bold"
disabled={isSubmitting}
>
{isTimerVisible && (
<div className="absolute left-10">
<Timer time={180} onTimeout={handleTimerTimeout} />
</div>
)}
<span>확인</span>
</button>
)}
</div>
</form>
</div>
</div>
);
}

export default UserAuthenticatePage;
52 changes: 52 additions & 0 deletions src/pages/setting/UserPasswordSettingPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useForm } from 'react-hook-form';
import ValidationInput from '@/components/common/ValidationInput';
import { USER_AUTH_VALIDATION_RULES } from '@/constants/formValidationRules';
import { EditPasswordForm } from '@/types/UserType';

export default function UserPasswordSettingPage() {
const {
register,
handleSubmit,
watch,
formState: { errors, isSubmitting },
} = useForm<EditPasswordForm>({
mode: 'onChange',
});

const onSubmit = (data: EditPasswordForm) => {
console.log(data);
};

return (
<div className="flex h-full items-center justify-center">
<form onSubmit={handleSubmit(onSubmit)} className="flex w-full max-w-300 flex-col gap-8">
{/* 현재 비밀번호 */}
<ValidationInput
label="현재 비밀번호"
errors={errors.password?.message}
register={register('password', USER_AUTH_VALIDATION_RULES.PASSWORD)}
/>

{/* 신규 비밀번호 */}
<ValidationInput
label="신규 비밀번호"
errors={errors.newPassword?.message}
register={register('newPassword', USER_AUTH_VALIDATION_RULES.PASSWORD)}
/>

{/* 신규 비밀번호 확인 */}
<ValidationInput
label="신규 비밀번호 확인"
errors={errors.checkNewPassword?.message}
register={register('checkNewPassword', USER_AUTH_VALIDATION_RULES.PASSWORD_CONFIRM(watch('newPassword')))}
/>

<div className="flex flex-col text-center">
<button type="submit" className="auth-btn" disabled={isSubmitting}>
비밀번호 변경
</button>
</div>
</form>
</div>
);
}
6 changes: 3 additions & 3 deletions src/pages/user/SearchIdPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useForm } from 'react-hook-form';
import ValidationInput from '@/components/common/ValidationInput';
import { USER_AUTH_VALIDATION_RULES } from '@/constants/formValidationRules';
import { SearchIDForm } from '@/types/UserType';
import { EmailVerificationForm } from '@/types/UserType';
import AuthForm from '@/components/user/authForm/AuthForm';
import FooterLinks from '@/components/user/authForm/FooterLinks';

Expand All @@ -10,15 +10,15 @@ export default function SearchIdPage() {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<SearchIDForm>({
} = useForm<EmailVerificationForm>({
mode: 'onChange',
defaultValues: {
email: '',
code: '',
},
});

const onSubmit = (data: SearchIDForm) => {
const onSubmit = (data: EmailVerificationForm) => {
console.log(data);
};

Expand Down
6 changes: 3 additions & 3 deletions src/pages/user/SearchPasswordPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function SearchPasswordPage() {
} = useForm<SearchPasswordForm>({
mode: 'onChange',
defaultValues: {
userId: '',
id: '',
email: '',
code: '',
},
Expand All @@ -28,8 +28,8 @@ export default function SearchPasswordPage() {
{/* 아이디 */}
<ValidationInput
placeholder="아이디"
errors={errors.userId?.message}
register={register('userId', USER_AUTH_VALIDATION_RULES.ID)}
errors={errors.id?.message}
register={register('id', USER_AUTH_VALIDATION_RULES.ID)}
/>

{/* 이메일 */}
Expand Down
6 changes: 3 additions & 3 deletions src/pages/user/SignInPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function SignInPage() {
} = useForm({
mode: 'onChange',
defaultValues: {
userId: '',
id: '',
password: '',
},
});
Expand All @@ -30,8 +30,8 @@ export default function SignInPage() {
{/* 아이디 */}
<ValidationInput
placeholder="아이디"
errors={errors.userId?.message}
register={register('userId', USER_AUTH_VALIDATION_RULES.ID)}
errors={errors.id?.message}
register={register('id', USER_AUTH_VALIDATION_RULES.ID)}
/>

{/* 비밀번호 */}
Expand Down
Loading