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

Refactor: Header 컴포넌트 및 하위 컴포넌트들 리팩토링 #7

Merged
merged 32 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d475b41
refactor: Alarm 컴포넌트 import 오류 및 Image 태그 수정
ccwnc Apr 14, 2024
e170e5b
refactor: Alarm 컴포넌트 prop을 범용적으로 사용할 수 있도록 수정
ccwnc Apr 14, 2024
7d2f01c
refactor: AlarmCard 컴포넌트를 범용적으로 사용할 수 있도록 수정
ccwnc Apr 14, 2024
85ff969
refactor: AlarmCard 컴포넌 prop 수정
ccwnc Apr 15, 2024
63b672d
refactor: AlarmList 컴포넌트를 범용적으로 사용할 수 있도록 수정
ccwnc Apr 15, 2024
d62eed2
refactor: HeaderSigninButton 컴포넌트 import 오류 수정
ccwnc Apr 15, 2024
67138a6
refactor: HeaderSignupButton 컴포넌트 import 오류 수정
ccwnc Apr 15, 2024
9a188e3
refactor: DrawerMenu 컴포넌트 import 오류 수정
ccwnc Apr 15, 2024
f74a4b1
refactor: DrawerMenu 컴포넌트를 범용적으로 사용할 수 있도록 prop 수정
ccwnc Apr 15, 2024
d765056
refactor: EmptyAlarm 컴포넌트 import 오류 수정
ccwnc Apr 15, 2024
b01630e
refactor: UserMenu 컴포넌트 import 오류 수정
ccwnc Apr 15, 2024
6bd63a6
refactor: UserMenu 컴포넌트를 범용적으로 사용할 수 있도록 수정
ccwnc Apr 15, 2024
1ae0214
refactor: HeaderProfile 컴포넌트 import 오류 수정
ccwnc Apr 15, 2024
bcb56ae
refactor: Header 컴포넌트 import 오류 수정
ccwnc Apr 15, 2024
003a0a7
refactor: DrawerMenu 컴포넌트 불필요한 import 제거
ccwnc Apr 15, 2024
fab9813
refactor: Alarm 컴포넌트 불필요한 import 제거
ccwnc Apr 15, 2024
a0af0ef
refactor: HeaderProfile 컴포넌트 불필요한 import 제거
ccwnc Apr 15, 2024
5b811a1
refactor: UserMenu 컴포넌트 불필요한 import 제거
ccwnc Apr 15, 2024
9c0418a
refactor: Menu 컴포넌트를 범용적으로 사용할 수 있도록 수정
ccwnc Apr 15, 2024
50b7487
refactor: import 수정
ccwnc Apr 15, 2024
cc5be4b
refactor: AlarmCard 컴포넌트 추가 수정
ccwnc Apr 15, 2024
3c43c8b
refactor: AlarmList 컴포넌트를 범용적으로 사용할 수 있도록 추가 수정
ccwnc Apr 15, 2024
4502005
refactor: Alarm 컴포넌트 url 및 alt 수정
ccwnc Apr 15, 2024
b676ccd
refactor: Menu 컴포넌트를 범용적으로 사용할 수 있도록 추가 수정
ccwnc Apr 15, 2024
b7c4090
refactor: HeaderProfile 컴포넌트 url 및 alt 수정
ccwnc Apr 15, 2024
4c92901
refactor: DrawerMenu 컴포넌트를 범용적으로 사용할 수 있도록 추가 수정
ccwnc Apr 15, 2024
87a4560
refactor: Header 컴포넌트를 범용적으로 사용할 수 있도록 추가 수정
ccwnc Apr 15, 2024
cd6cc32
refactor: import문 수정
ccwnc Apr 15, 2024
0bd0dd9
refactor: import문 수정
ccwnc Apr 16, 2024
1e30f89
refactor: EmptyAlarm 컴포넌트의 알림 문구룰 prop으로 받도록 수정
ccwnc Apr 16, 2024
ef34fa6
refactor: AlarmList 컴포넌트의 prop인 notifications 타입 수정
ccwnc Apr 16, 2024
92a4b9a
refactor: path를 prop으로 받도록 수정
ccwnc Apr 16, 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
2 changes: 1 addition & 1 deletion src/components/Avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import classNames from 'classnames/bind';

import { SVGS } from '@/constants/index';
import { SVGS } from '@/constants';

import styles from './Avatar.module.scss';

Expand Down
2 changes: 1 addition & 1 deletion src/components/Pagination/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import classNames from 'classnames/bind';

import { SVGS } from '@/constants/index';
import { SVGS } from '@/constants';

import usePagination from '@/hooks/usePagination';

Expand Down
16 changes: 6 additions & 10 deletions src/components/header/Alarm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import Image from 'next/image';

import { MouseEventHandler, RefObject } from 'react';
import { RefObject } from 'react';

import classNames from 'classnames/bind';

Expand All @@ -14,23 +12,21 @@ const { full, empty } = SVGS.alarm;
type AlarmProps = {
isAlarmExisted: boolean;
isActivated: boolean;
onClick: MouseEventHandler<HTMLButtonElement>;
alarmRef: RefObject<HTMLButtonElement>;
onClick: () => void;
alarmRef?: RefObject<HTMLButtonElement>;
};

const Alarm = ({ isAlarmExisted, isActivated, onClick, alarmRef }: AlarmProps) => {
const { url, alt } = isAlarmExisted ? full : empty;

return (
<button className={cx('frame-outer')} onClick={onClick} ref={alarmRef}>
<div className={cx('dot', 'dot-top', { 'dot-activated': isActivated })}></div>
<div className={cx('dot', 'dot-right', { 'dot-activated': isActivated })}></div>
<div className={cx('dot', 'dot-bottom', { 'dot-activated': isActivated })}></div>
<div className={cx('dot', 'dot-left', { 'dot-activated': isActivated })}></div>
<div className={cx('frame-inner', { 'frame-inner-activated': isActivated })}>
{isAlarmExisted ? (
<Image src={full.url} alt={full.alt} width={20} height={20} />
) : (
<Image src={empty.url} alt={empty.alt} width={20} height={20} />
)}
<img src={url} alt={alt} width={20} height={20} />
</div>
</button>
);
Expand Down
84 changes: 21 additions & 63 deletions src/components/header/AlarmCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import Image from 'next/image';

import { useMutation, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import classNames from 'classnames/bind';

import { deleteMyNotification } from '@/apis/queryFunctions';
import { QUERY_KEYS } from '@/apis/queryKeys';
import { API_ERROR_MESSAGE, SVGS } from '@/constants';
import { getElapsedTimeToKST, splitTitleByDelimiter } from '@/utils';
import { SVGS } from '@/constants';

import { ConfirmModal, ModalButton } from '@/components/commons/modals';
import useToggleButton from '@/hooks/useToggleButton';

import styles from './AlarmCard.module.scss';
Expand All @@ -21,66 +13,32 @@ type AlarmCardProps = {
id: number;
content: string;
createdAt: string;
onClick: (id: number) => void;
};

const AlarmCard = ({ id, content, createdAt }: AlarmCardProps) => {
const AlarmCard = ({ id, content, createdAt, onClick }: AlarmCardProps) => {
const { isVisible: hoverState, handleToggleClick: handleToggleState } = useToggleButton();
const { isVisible: is404Open, handleToggleClick: handle404Click } = useToggleButton();

const { title, MaxCount } = splitTitleByDelimiter(content);
const refinedContent = title + (MaxCount.match(/\(.*$/)?.[0] ?? '');

const queryClient = useQueryClient();

const { mutate: deleteAlarmMutation } = useMutation({
mutationFn: deleteMyNotification,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.myNotifications.get],
});
},
onError: (error) => {
if ((error as AxiosError)?.response?.status === 404) {
handle404Click();
}
},
});

const handleDeleteNotification = (id: number) => {
deleteAlarmMutation(id);
};

return (
<>
<div className={cx('alarm-card')}>
<div className={cx('alarm-card-container')}>
<p className={cx('alarm-card-content')}>{refinedContent}</p>
<button
className={cx('alarm-card-delete')}
onMouseEnter={handleToggleState}
onMouseLeave={handleToggleState}
onClick={() => handleDeleteNotification(id)}
>
<Image
src={hoverState ? hover.url : normal.url}
alt={hoverState ? hover.alt : normal.alt}
width={24}
height={24}
/>
</button>
</div>
<span className={cx('alarm-card-created-at')}>{getElapsedTimeToKST(createdAt)}</span>
<div className={cx('alarm-card')}>
<div className={cx('alarm-card-container')}>
<p className={cx('alarm-card-content')}>{content}</p>
<button
className={cx('alarm-card-delete')}
onMouseEnter={handleToggleState}
onMouseLeave={handleToggleState}
onClick={() => onClick(id)}
>
<img
src={hoverState ? hover.url : normal.url}
alt={hoverState ? hover.alt : normal.alt}
width={24}
height={24}
/>
</button>
</div>
<ConfirmModal
warning
openModal={is404Open}
onClose={handle404Click}
state='ERROR'
title={'알림 삭제에 실패하였습니다'}
desc={API_ERROR_MESSAGE.notification[404]}
renderButton={<ModalButton onClick={handle404Click}>닫기</ModalButton>}
></ConfirmModal>
</>
<span className={cx('alarm-card-created-at')}>{createdAt}</span>
</div>
);
};

Expand Down
117 changes: 42 additions & 75 deletions src/components/header/AlarmList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,62 @@
import { RefObject } from 'react';

import { useMutation, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import classNames from 'classnames/bind';

import { deleteMyNotifications } from '@/apis/queryFunctions';
import { QUERY_KEYS } from '@/apis/queryKeys';
import { API_ERROR_MESSAGE } from '@/constants';

import { ConfirmModal, ModalButton } from '@/components/commons/modals';
import AlarmCard from '@/components/layout/header/AlarmCard';
import EmptyAlarm from '@/components/layout/header/EmptyAlarm';
import useToggleButton from '@/hooks/useToggleButton';

import { NotificationResponse } from '@/types';
import AlarmCard from '@/components/header/AlarmCard';
import EmptyAlarm from '@/components/header/EmptyAlarm';

import styles from './AlarmList.module.scss';

const cx = classNames.bind(styles);

type Notification = {
id: number;
content: string;
createdAt: string;
};

type AlarmListProps = {
notifications: NotificationResponse[];
notifications: Notification[];
totalCount: number;
alarmListRef: RefObject<HTMLDivElement>;
handleDelete: (id: number) => void;
handleDeleteAll: () => void;
emptyAlarmText: string;
};

const AlarmList = ({ notifications, totalCount, alarmListRef }: AlarmListProps) => {
const { isVisible: is404Open, handleToggleClick: handle404Click } = useToggleButton();

const queryClient = useQueryClient();

const { mutate: deleteAlarmMutations } = useMutation({
mutationFn: deleteMyNotifications,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.myNotifications.get],
});
},
onError: (error) => {
if ((error as AxiosError)?.response?.status === 404) {
handle404Click();
}
},
});

const extractednotificationIds = (notifications: NotificationResponse[]): number[] => {
return notifications.map((notification) => notification.id);
};

const handleDeleteAllNotifications = () => {
if (notifications.length > 0) {
deleteAlarmMutations(extractednotificationIds(notifications));
}
};

const AlarmList = ({
notifications,
totalCount,
alarmListRef,
handleDelete,
handleDeleteAll,
emptyAlarmText,
}: AlarmListProps) => {
return (
<>
<div className={cx('alarm-list')} ref={alarmListRef}>
<div className={cx('alarm-list-top')}>
<div className={cx('alarm-list-count')}>
<span className={cx('alarm')}>알림</span>
<span className={cx('total-count')}>{totalCount}</span>
</div>
<button className={cx('delete-all')} onClick={handleDeleteAllNotifications}>
전체 삭제
</button>
</div>
<div className={cx('alarm-list-bottom')}>
{notifications[0] ? (
<ul className={cx('alarm-list-contents')}>
{notifications.map(({ id, content, createdAt }) => (
<li key={id}>
<AlarmCard id={id} content={content} createdAt={createdAt} />
</li>
))}
</ul>
) : (
<EmptyAlarm />
)}
<div className={cx('alarm-list')} ref={alarmListRef}>
<div className={cx('alarm-list-top')}>
<div className={cx('alarm-list-count')}>
<span className={cx('alarm')}>알림</span>
<span className={cx('total-count')}>{totalCount}</span>
</div>
<button className={cx('delete-all')} onClick={handleDeleteAll}>
전체 삭제
</button>
</div>
<div className={cx('alarm-list-bottom')}>
{notifications[0] ? (
<ul className={cx('alarm-list-contents')}>
{notifications.map(({ id, content, createdAt }) => (
<li key={id}>
<AlarmCard id={id} content={content} createdAt={createdAt} onClick={handleDelete} />
</li>
))}
</ul>
) : (
<EmptyAlarm emptyAlarmText={emptyAlarmText} />
)}
</div>
<ConfirmModal
warning
openModal={is404Open}
onClose={handle404Click}
state='ERROR'
title={'알림 삭제에 실패하였습니다'}
desc={API_ERROR_MESSAGE.notification[404]}
renderButton={<ModalButton onClick={handle404Click}>닫기</ModalButton>}
></ConfirmModal>
</>
</div>
);
};

Expand Down
21 changes: 8 additions & 13 deletions src/components/header/DrawerMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
import Image from 'next/image';
import Link from 'next/link';

import { MouseEventHandler } from 'react';

import classNames from 'classnames/bind';

import { GAME_NAME_LIST_EN, SVGS } from '@/constants';
import { formatGameToLink } from '@/utils';
import { SVGS } from '@/constants';

import styles from './DrawerMenu.module.scss';

const cx = classNames.bind(styles);
const { url, alt } = SVGS.close.active;

type DrawerMenuProps = {
onClick: MouseEventHandler<HTMLElement>;
menuList: string[];
onClick: () => void;
};

const DrawerMenu = ({ onClick }: DrawerMenuProps) => {
const DrawerMenu = ({ menuList, onClick }: DrawerMenuProps) => {
return (
<aside className={cx('drawer-menu')}>
<button className={cx('drawer-menu-close-button')} onClick={onClick}>
<Image src={url} alt={alt} width={24} height={24} />
<img src={url} alt={alt} width={24} height={24} />
</button>
<nav>
<ul className={cx('drawer-menu-game')}>
{GAME_NAME_LIST_EN.map((game, index) => (
{menuList.map((game, index) => (
<li className={cx('drawer-menu-game-item')} key={`game-${index}`}>
<Link className={cx('drawer-menu-game-link')} onClick={onClick} href={`/${formatGameToLink(game)}`}>
<a className={cx('drawer-menu-game-link')} onClick={onClick} href={`/${game}`}>
{game}
</Link>
</a>
</li>
))}
</ul>
Expand Down
12 changes: 7 additions & 5 deletions src/components/header/EmptyAlarm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import Image from 'next/image';

import classNames from 'classnames/bind';

import { SVGS } from '@/constants';
Expand All @@ -10,11 +8,15 @@ const cx = classNames.bind(styles);

const { url, alt } = SVGS.alarm.off;

const EmptyAlarm = () => {
type EmptyAlarmProps = {
emptyAlarmText: string;
};

const EmptyAlarm = ({ emptyAlarmText }: EmptyAlarmProps) => {
return (
<div className={cx('empty-alarm')}>
<Image src={url} alt={alt} width={24} height={24} />
<span className={cx('text')}>알림이 없습니다</span>
<img src={url} alt={alt} width={24} height={24} />
<span className={cx('text')}>{emptyAlarmText}</span>
</div>
);
};
Expand Down
29 changes: 0 additions & 29 deletions src/components/header/Header/Header.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,34 +73,5 @@
@include flexbox($gap: 0.8rem);
}
}

&-alarm-list,
&-user-menu {
@include responsive(T) {
right: 1.5rem;
}

position: absolute;
top: 100%;
right: 0;
margin-top: 0.8rem;
}
}
}

.drawer-menu {
position: absolute;
top: 0;
left: 0;
transform: translateX(-100%);

&.open {
transform: translateX(0);
animation: slide-in-left ease-in-out 0.3s;
}

&.close {
transform: translateX(-100%);
animation: slide-out-left ease-in-out 0.3s;
}
}
Loading