Skip to content

Commit

Permalink
Merge branch 'dev' into feature-022
Browse files Browse the repository at this point in the history
  • Loading branch information
sehyun0518 committed Feb 13, 2024
2 parents 79c058e + faf7dd0 commit 916534c
Show file tree
Hide file tree
Showing 14 changed files with 355 additions and 69 deletions.
2 changes: 1 addition & 1 deletion src/apis/config/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const axiosInstance = axios.create({ baseURL });

axiosInstance.interceptors.request.use((config) => {
config.withCredentials = true;
config.headers['Access-Control-Allow-Origin'] = '*';
// config.headers['Access-Control-Allow-Origin'] = '*';

if (localStorage.vino) {
const storage = JSON.parse(localStorage.vino);
Expand Down
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
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { Dropdown } from '@/styles/SummaryPage';

import DropdownItem from './DropdownItem';
import { useRecoilValue } from 'recoil';
import { categoryState } from '@/stores/category';
import React from 'react';
import { ISelectedCategoryProps } from 'types/category';

// 임시 타입
interface Item {
id: number;
text: string;
items?: Item[];
interface ICategoryDropdownProp {
setSelectedCategory: React.Dispatch<
React.SetStateAction<ISelectedCategoryProps>
>;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
}

type Props = {
categoryList: Item[];
};

const CategoryDropdown = ({ categoryList }: Props) => {
const CategoryDropdown = ({
setSelectedCategory,
setIsOpen,
}: ICategoryDropdownProp) => {
const categories = useRecoilValue(categoryState);
return (
<Dropdown onClick={(e) => e.stopPropagation()}>
<ul>
{categoryList.map((category) => (
<DropdownItem key={category.id} category={category} />
{categories.map((category) => (
<DropdownItem
key={category.categoryId}
category={category}
setSelectedCategory={setSelectedCategory}
setIsOpen={setIsOpen}
/>
))}
</ul>
</Dropdown>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,50 +1,81 @@
import { useState } from 'react';

import DownIcon from '@/assets/icons/down.svg?react';
import { IFolderProps, ISelectedCategoryProps } from 'types/category';
import { DropdownTopCategoryName } from '@/styles/SummaryPage';

// 임시 타입
interface Item {
id: number;
text: string;
items?: Item[];
interface ICategoryDropdownProp {
category: IFolderProps;
setSelectedCategory: React.Dispatch<
React.SetStateAction<ISelectedCategoryProps>
>;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
}

type Props = {
category: Item;
};
interface IItemClickProps {
name: string;
categoryId: number;
}

const DropdownItem = ({ category }: Props) => {
const DropdownItem = ({
category,
setSelectedCategory,
setIsOpen,
}: ICategoryDropdownProp) => {
const [isShow, setIsShow] = useState(false);

const dynamicStyles = {
icon: {
transform: isShow ? 'rotateZ(180deg)' : 'rotateZ(0deg)',
},
subCategory: {
height: isShow ? (category.items?.length || 0) * 46 : 0,
height: isShow ? (category.subFolders.length || 0) * 46 : 0,
},
};

const handleItemClick = async (id: number) => {
const handleItemClick = async ({ name, categoryId }: IItemClickProps) => {
setSelectedCategory({
name,
categoryId,
});
// API 요청
console.log(id);
console.log('API 요청');
setIsOpen(false);
};

return (
<>
<li onClick={() => setIsShow(!isShow)}>
<DownIcon width={18} height={18} style={dynamicStyles.icon} />

{category.text}
<li>
<DownIcon
width={18}
height={18}
style={dynamicStyles.icon}
onClick={() => setIsShow(!isShow)}
/>
<DropdownTopCategoryName
onClick={() =>
handleItemClick({
name: category.name,
categoryId: category.categoryId,
})
}
>
{category.name}
</DropdownTopCategoryName>
</li>

<ul style={dynamicStyles.subCategory}>
{category.items?.map((subCategory) => (
{category.subFolders.map((subFolder) => (
<li
key={subCategory.id}
onClick={() => handleItemClick(subCategory.id)}
key={subFolder.categoryId}
onClick={() =>
handleItemClick({
name: subFolder.name,
categoryId: subFolder.categoryId,
})
}
>
{subCategory.text}
{subFolder.name}
</li>
))}
</ul>
Expand Down
Loading

0 comments on commit 916534c

Please sign in to comment.