Skip to content

Commit 0a9eb28

Browse files
authored
Merge pull request Next-Room#81 from Next-Room/feat/refresh
�feat: 리프레시 토큰 갱신 및 로그아웃 처리/ useClickOutside 개선
2 parents 8ca7453 + aadca4d commit 0a9eb28

File tree

18 files changed

+600
-207
lines changed

18 files changed

+600
-207
lines changed

app/admin-new/(components)/ThemeDrawer/Container.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,4 @@ const ThemeDrawer = ({
116116
</form>
117117
);
118118
};
119-
export default ThemeDrawer;
119+
export default ThemeDrawer;

app/admin-new/(components)/ThemeInfo/Container.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { useEffect, useState } from "react";
2-
import "../../(style)/themeInfo.modules.sass";
1+
import React, { useEffect, useState } from "react";
32
import classNames from "classnames";
43

4+
import "../../(style)/themeInfo.modules.sass";
5+
56
import useModal from "@/hooks/useModal";
67
import Dialog from "@/components/common/Dialog-new/Dialog";
78
import { useSelectedHintReset } from "@/components/atoms/selectedHint.atom";

app/admin-new/(components)/ThemeInfo/ThemeInfoHint.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import {
88
useSelectedHint,
99
useSelectedHintReset,
1010
} from "@/components/atoms/selectedHint.atom";
11-
import { useCreateHintReset } from "@/components/atoms/createHint.atom";
11+
import {
12+
useCreateHint,
13+
useCreateHintReset,
14+
} from "@/components/atoms/createHint.atom";
1215

1316
interface ThemeDrawerProps {
1417
handleHintCreate: (type: string) => void;
@@ -19,6 +22,7 @@ const ThemeInfoHint: React.FC<ThemeDrawerProps> = ({ handleHintCreate }) => {
1922

2023
const { data: hints = [] } = useGetHintList({ themeId });
2124
const [selectedHint, setSelectedHint] = useSelectedHint();
25+
const [_, setCreateHint] = useCreateHint();
2226
const resetSelectedHint = useSelectedHintReset();
2327
const resetCreateHint = useCreateHintReset();
2428

@@ -31,6 +35,11 @@ const ThemeInfoHint: React.FC<ThemeDrawerProps> = ({ handleHintCreate }) => {
3135
e: React.MouseEvent<HTMLLIElement, globalThis.MouseEvent>,
3236
hintElement: SelectedHintType
3337
) => {
38+
if (hintElement.id === selectedHint.id) {
39+
return;
40+
}
41+
42+
setCreateHint(hintElement);
3443
setSelectedHint(hintElement);
3544
handleHintCreate("Edit");
3645
};

app/admin-new/(style)/admin.modules.sass

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
display: flex
77
overflow: hidden
88
height: 100vh
9-
9+
background-color: #131417
1010
.sidebar
1111
flex-shrink: 0
1212
display: flex
@@ -137,8 +137,8 @@
137137
&:hover
138138
overflow-x: overlay
139139
display: flex
140-
max-width: 2198px
141-
min-width: 918px
140+
max-width: 2278px
141+
min-width: 998px
142142
background-color: $color-main
143143

144144
.modal-0

app/admin-new/Admin.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@ import { useRouter } from "next/navigation";
55

66
import useCheckSignIn from "@/hooks/useCheckSignIn";
77
import Loader from "@/components/Loader/Loader";
8-
import {
9-
getAdminCode,
10-
getShopName,
11-
setSelectedThemeId,
12-
} from "@/utils/localStorage";
8+
import { getLoginInfo, setSelectedThemeId } from "@/utils/localStorage";
139
import { useSelectedTheme } from "@/components/atoms/selectedTheme.atom";
1410
import { useGetThemeList } from "@/queries/getThemeList";
1511
import { useToastInfo } from "@/components/atoms/toast.atom";
@@ -29,8 +25,8 @@ function Admin() {
2925
const isLoggedIn = useCheckSignIn();
3026

3127
const [selectedTheme, setSelectedTheme] = useSelectedTheme();
32-
const adminCode: string = getAdminCode() || "";
33-
const shopName: string = getShopName() || "";
28+
const { adminCode, shopName } = getLoginInfo();
29+
3430
const [toast, setToast] = useToastInfo();
3531
const router = useRouter();
3632

app/components/RequireAuth/RequireAuth.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
import React, { ReactNode, useEffect, useMemo, useState } from "react";
44
import { useRouter, usePathname } from "next/navigation";
55

6+
import { useTokenRefresh } from "@/mutations/useRefresh";
67
import { useGetThemeList } from "@/queries/getThemeList";
78
import {
89
useCurrentTheme,
910
useCurrentThemeReset,
1011
} from "@/components/atoms/currentTheme.atom";
1112
import { useSelectedThemeReset } from "@/components/atoms/selectedTheme.atom";
12-
import { useIsLoggedInValue } from "@/components/atoms/account.atom";
13+
import { useIsLoggedIn } from "@/components/atoms/account.atom";
1314
import { getSelectedThemeId } from "@/utils/localStorage";
1415
import * as S from "@/home/HomeView.styled";
1516
import Header from "@/components/common/Header/Header";
@@ -23,7 +24,7 @@ interface RequireAuthProps {
2324
function RequireAuth({
2425
children,
2526
}: RequireAuthProps): React.ReactElement | null {
26-
const isLoggedIn = useIsLoggedInValue();
27+
const [isLoggedIn, setIsLoggedIn] = useIsLoggedIn();
2728
const [currentTheme, setCurrentTheme] = useCurrentTheme();
2829
const resetCurrentTheme = useCurrentThemeReset();
2930
const resetSelectedTheme = useSelectedThemeReset();
@@ -33,6 +34,7 @@ function RequireAuth({
3334
const [isMobile, setIsMobile] = useState(false);
3435
const [isLoading, setIsLoading] = useState(true);
3536
const { data: categories = [] } = useGetThemeList();
37+
const { refreshToken, error } = useTokenRefresh();
3638
useEffect(() => {
3739
if (typeof window !== "undefined") {
3840
const { userAgent } = window.navigator;

app/components/common/Drawer/Drawer.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ import {
1818
useSelectedTheme,
1919
} from "@/components/atoms/selectedTheme.atom";
2020
import { Theme, Themes } from "@/queries/getThemeList";
21-
import { getAdminCode, getShopName } from "@/utils/localStorage";
21+
import { getLoginInfo } from "@/utils/localStorage";
2222
import Dialog from "@/components/common/Dialog/Dialog";
2323

24+
2425
import * as S from "./DrawerView.styled";
2526

2627
type Props = {
@@ -39,9 +40,9 @@ function MainDrawer(props: Props) {
3940
const router = useRouter();
4041

4142
const [selectedTheme, setSelectedTheme] = useSelectedTheme();
42-
const shopName = getShopName();
43-
const adminCode = getAdminCode();
43+
const { shopName, adminCode } = getLoginInfo();
4444

45+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
4546
const [modalState, setModalState] = useModalState();
4647
const [focusedTheme, setFocusedTheme] = useState<Theme | null>(null); // 현재 선택된 테마를 저장할 상태 추가
4748
const [open, setOpen] = useState<boolean>(false);

app/components/common/EmptyHome/EmptyHomeView.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { useRouter } from "next/navigation";
44

55
import { HOME_TITLE } from "@/consts/components/home";
66
import { useModalStateWrite } from "@/components/atoms/modalState.atom";
7-
import { getShopName } from "@/utils/localStorage";
7+
import { getLoginInfo } from "@/utils/localStorage";
8+
89

910
import * as S from "./EmptyHomeView.styled";
1011

1112
function EmptyHomeView() {
12-
const shopName = getShopName();
13+
const { shopName } = getLoginInfo();
1314
const router = useRouter();
1415
const setModalState = useModalStateWrite();
1516
const toggleOnModalState = () => {

app/hooks/useCheckSignIn.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { useEffect } from "react";
22

33
import { apiClient } from "@/lib/reactQueryProvider";
4-
import { getAccessToken } from "@/utils/localStorage";
4+
import { getLoginInfo } from "@/utils/localStorage";
55
import { useIsLoggedIn } from "@/components/atoms/account.atom";
66
import { getSubscriptionPlan } from "@/queries/getSubscriptionPlan";
77

88
const useCheckSignIn = () => {
9-
const accessToken = getAccessToken();
9+
const { accessToken } = getLoginInfo();
1010
const [isLoggedIn, setIsLoggedIn] = useIsLoggedIn();
1111

1212
useEffect(() => {

app/hooks/useClickOutside.ts

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
1-
import { useEffect, MutableRefObject } from "react";
1+
import { useEffect, RefObject } from "react";
22

3-
function useClickOutside<T extends HTMLElement>(
4-
ref: MutableRefObject<T | null>,
5-
onClickOutside: () => void
3+
/**
4+
* 클릭 시 이벤트를 닫아주는 공통 hook
5+
* @param {RefObject<HTMLElement | HTMLElement[]>} ref - Element ref
6+
* @param {Function} handler - 이벤트 핸들러
7+
* @param {boolean} isActive - 활성화 유무
8+
* @param {number} targetIndex - 닫아야 할 창이 여러 개일 때 닫을 창의 index
9+
* @param {boolean} ignoreSiblings - 형제 요소 클릭 시에도 닫힐지 여부
10+
*/
11+
function useClickOutside(
12+
ref: RefObject<HTMLElement | HTMLElement[]>,
13+
handler: (event: MouseEvent) => void,
14+
isActive = true,
15+
targetIndex = 0,
16+
ignoreSiblings = false
617
) {
718
useEffect(() => {
8-
/**
9-
* Invoke Function onClick outside of element
10-
*/
11-
function handleClickOutside(event: MouseEvent) {
12-
// 이벤트가 상위로 전파되지 않도록 수정
13-
if (ref.current && !ref.current.contains(event.target as Node)) {
14-
onClickOutside();
15-
event.stopPropagation(); // 상위 모달로의 이벤트 전파를 막음
19+
if (!isActive) return;
20+
21+
const listener = (event: MouseEvent) => {
22+
const target = event.target as HTMLElement;
23+
24+
// ref가 비어 있거나, ref 내부를 클릭한 경우 무시
25+
const elements = Array.isArray(ref.current) ? ref.current : [ref.current];
26+
const isClickInside = elements.some((element) => element?.contains(target));
27+
if (isClickInside) return;
28+
29+
// ignoreSiblings 옵션이 true인 경우 형제 요소 클릭 시 닫음
30+
if (ignoreSiblings) {
31+
const parentNode = elements[targetIndex]?.parentNode;
32+
if (parentNode && Array.from(parentNode.children).some((sibling) => sibling.contains(target))) {
33+
return;
34+
}
1635
}
17-
}
1836

19-
// Bind
20-
document.addEventListener("click", handleClickOutside);
21-
return () => {
22-
// dispose
23-
document.removeEventListener("click", handleClickOutside);
37+
// 핸들러 실행 (버튼이든 어떤 요소든 외부 클릭이면)
38+
handler(event);
2439
};
25-
}, [ref, onClickOutside]);
40+
41+
document.addEventListener("click", listener);
42+
return () => document.removeEventListener("click", listener);
43+
}, [ref, handler, isActive, targetIndex, ignoreSiblings]);
2644
}
2745

2846
export default useClickOutside;

0 commit comments

Comments
 (0)