Skip to content

Commit

Permalink
Merge pull request #61 from teamViNO/feature-058
Browse files Browse the repository at this point in the history
Feature 058
  • Loading branch information
whistleJs authored Feb 13, 2024
2 parents e37247a + 5ded347 commit 8598973
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 8 deletions.
5 changes: 5 additions & 0 deletions src/apis/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
LoginResponse,
MyInfoResponse,
UpdateMyInfoRequest,
UpdatePasswordRequest,
} from '@/models/user';
import {
AlarmResponse,
Expand Down Expand Up @@ -62,3 +63,7 @@ export const getMyInfoAPI = () => {
export const updateMyInfoAPI = (data: UpdateMyInfoRequest) => {
return axios.put<APIBaseResponse>(PREFIX + '/myPage/setInfo', data);
};

export const updatePasswordAPI = (data: UpdatePasswordRequest) => {
return axios.put<APIBaseResponse>(PREFIX + '/myPage/updatePassword', data);
};
2 changes: 1 addition & 1 deletion src/components/ProfilePage/Account/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ const Account = () => {
<div className="input-box disabled">{userInfo?.email}</div>
</div>

<ChangePassword />
<ChangePassword onRefresh={refreshMyInfo} />
</Box>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import useBoolean from '@/hooks/useBoolean';

import ChangePasswordModal from './ChangePasswordModal';

const ChangePassword = () => {
type Props = {
onRefresh: () => void;
};

const ChangePassword = ({ onRefresh }: Props) => {
const [isOpen, , open, close] = useBoolean(false);

return (
Expand All @@ -18,7 +22,7 @@ const ChangePassword = () => {
</button>
</div>

{isOpen && <ChangePasswordModal onClose={close} />}
{isOpen && <ChangePasswordModal onClose={close} onRefresh={onRefresh} />}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,160 @@
import { useMemo, useState } from 'react';

import CloseIcon from '@/assets/icons/close.svg?react';
import TransformationIcon from '@/assets/icons/transformation.svg?react';

import { Modal } from '@/components/common';

import { ModalBox } from '@/styles/ProfilePage';

import { ValidatePassword, validatePassword } from '@/utils/validation';
import { updatePasswordAPI } from '@/apis/user';

type Props = {
onClose: () => void;
onRefresh: () => void;
};

const ChangePasswordModal = ({ onClose }: Props) => {
return <Modal onClose={onClose}>asdf</Modal>;
const ChangePasswordModal = ({ onClose, onRefresh }: Props) => {
const [oldPassword, setOldPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');

const [validateNewPassword, setValidateNewPassword] =
useState<ValidatePassword | null>(null);
const [validateConfirmPassword, setValidateConfirmPassword] = useState(true);

const isDisabledSubmitButton = useMemo(() => {
return !(
oldPassword !== '' &&
validateNewPassword?.ALPHA_UPPER &&
validateNewPassword?.LENGTH &&
validateNewPassword?.NUMBER &&
validateNewPassword?.SPECIAL_CHAR &&
validateConfirmPassword
);
}, [oldPassword, validateNewPassword, validateConfirmPassword]);

const handleChangeNewPassword: React.ChangeEventHandler<HTMLInputElement> = ({
target: { value },
}) => {
setNewPassword(value);
setValidateNewPassword(validatePassword(value));
setValidateConfirmPassword(value === newPassword);
};

const handleChangeConfirmPassword: React.ChangeEventHandler<
HTMLInputElement
> = ({ target: { value } }) => {
setConfirmPassword(value);
setValidateConfirmPassword(value === newPassword);
};

const getHelpStyle = (test: undefined | boolean) => {
if (test === undefined) return '';
return test ? 'active' : 'error';
};

const handleClickSubmitButton = async () => {
try {
await updatePasswordAPI({
old_password: oldPassword,
new_password: newPassword,
confirm_password: confirmPassword,
});
} catch (e) {
console.error(e);
}

onClose();
onRefresh();
};

return (
<Modal onClose={onClose}>
<ModalBox>
<div className="box">
<div className="close">
<CloseIcon width={28} height={28} onClick={onClose} />
</div>

<div className="content">
<TransformationIcon width={56} height={56} />

<h1 className="title">비밀번호 변경</h1>
</div>
</div>

<div className="box" style={{ gap: 40 }}>
<div className="input-group">
<span className="input-title">기존 비밀번호</span>

<input
type="password"
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
/>
</div>

<div className="input-group">
<span className="input-title">비밀번호</span>

<input
type="password"
value={newPassword}
onChange={handleChangeNewPassword}
/>

<div className="input-help">
<span className={getHelpStyle(validateNewPassword?.LENGTH)}>
*8자 이상으로 입력
</span>

<span className={getHelpStyle(validateNewPassword?.ALPHA_UPPER)}>
*대문자 사용
</span>

<span className={getHelpStyle(validateNewPassword?.NUMBER)}>
*숫자 사용
</span>

<span className={getHelpStyle(validateNewPassword?.SPECIAL_CHAR)}>
*특수문자 사용
</span>
</div>
</div>

<div className="input-group">
<span className="input-title">비밀번호 재입력</span>

<input
type="password"
value={confirmPassword}
onChange={handleChangeConfirmPassword}
/>

<div className="input-help">
{validateConfirmPassword ? (
<span>비밀번호 확인을 위해 다시 한 번 입력해주세요</span>
) : (
<span className="error">
비밀번호가 일치하지 않아요 다시 입력해주세요
</span>
)}
</div>
</div>

<button
className="submit"
disabled={isDisabledSubmitButton}
onClick={handleClickSubmitButton}
>
변경하기
</button>
</div>
</ModalBox>
</Modal>
);
};

export default ChangePasswordModal;
4 changes: 3 additions & 1 deletion src/components/ProfilePage/LogoutModal/LogoutModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ const LogoutModal = ({ onClose }: Props) => {
</div>
</div>

<button onClick={handleClickLogoutButton}>로그아웃 하기</button>
<button className="submit" onClick={handleClickLogoutButton}>
로그아웃 하기
</button>
</ModalBox>
</Modal>
);
Expand Down
6 changes: 6 additions & 0 deletions src/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ export interface UpdateMyInfoRequest {
nick_name: string;
gender: string;
}

export interface UpdatePasswordRequest {
old_password: string;
new_password: string;
confirm_password: string;
}
54 changes: 53 additions & 1 deletion src/styles/ProfilePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,55 @@ export const ModalBox = styled.div`
${(props) => props.theme.typography.Body1};
}
}
& > .input-group {
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
& .input-title {
color: ${(props) => props.theme.color.gray400};
${(props) => props.theme.typography.Body1};
}
& .input-help {
display: flex;
gap: 4px;
padding-left: 16px;
& > span {
color: ${(props) => props.theme.color.gray300};
transition: 0.1s;
${(props) => props.theme.typography.Body3};
&.active {
color: ${(props) => props.theme.color.gray500};
}
&.error {
color: ${(props) => props.theme.color.red};
}
}
}
& input {
padding: 15px 20px;
width: 100%;
height: 56px;
border-radius: 12px;
border: 1.5px solid ${(props) => props.theme.color.gray200};
outline: none;
transition: 0.1s;
&:focus {
border: 2px solid ${(props) => props.theme.color.gray500};
}
}
}
}
& > button {
& button.submit {
width: 100%;
height: 58px;
background-color: ${(props) => props.theme.color.gray500};
Expand All @@ -261,6 +307,12 @@ export const ModalBox = styled.div`
color: white;
cursor: pointer;
outline: none;
transition: 0.1s;
${(props) => props.theme.typography.Body1};
&:disabled {
background-color: ${(props) => props.theme.color.gray100};
color: ${(props) => props.theme.color.gray300};
}
}
`;
20 changes: 19 additions & 1 deletion src/utils/validation.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
export type ValidatePassword = {
ALPHA_UPPER: boolean;
ALPHA_LOWER: boolean;
SPECIAL_CHAR: boolean;
NUMBER: boolean;
LENGTH: boolean;
};

export const validateNickname = (nickname: string) =>
!/^[a-zA-Z가-힣\s]{1,7}$/g.test(nickname);
!/^[a-zA-Z가-힣\d\s]{1,7}$/g.test(nickname);

export const validatePassword = (password: string): ValidatePassword => {
return {
ALPHA_UPPER: /[A-Z]/g.test(password),
ALPHA_LOWER: /[a-z]/g.test(password),
SPECIAL_CHAR: /[#?!@$%^&*-]/g.test(password),
NUMBER: /\d/g.test(password),
LENGTH: 8 <= password.length && password.length <= 13,
};
};

0 comments on commit 8598973

Please sign in to comment.