diff --git a/src/App.tsx b/src/App.tsx index 97bcc2ed..d50577c4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,7 +6,15 @@ import { ModalViewer, Layout, Toast } from '@/components'; import { theme, globalStyles } from './styles'; -import { $me, $isAuthorized, $teams, $toast, $generations, $generationNumber } from './store'; +import { + $me, + $isAuthorized, + $teams, + $toast, + $generations, + $generationNumber, + $isMaster, +} from './store'; import * as api from './api'; import { ACCESS_TOKEN, PATH } from './constants'; @@ -36,6 +44,11 @@ interface RequiredAuthProps extends Partial { isAuth: boolean; } +interface MasterOnlyProps extends Partial { + children: ReactNode; + isMaster: boolean; +} + const RequiredAuth = ({ children, isAuth, to = PATH.LOGIN, ...restProps }: RequiredAuthProps) => { if (!isAuth) { return ; @@ -45,12 +58,27 @@ const RequiredAuth = ({ children, isAuth, to = PATH.LOGIN, ...restProps }: Requi return <>{children}; }; +const MasterOnly = ({ + children, + isMaster, + to = PATH.APPLICATION, + ...restProps +}: MasterOnlyProps) => { + if (!isMaster) { + return ; + } + + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{children}; +}; + const App = () => { const navigate = useNavigate(); const TOKEN = localStorage.getItem(ACCESS_TOKEN); const isAuthorized = useRecoilValue($isAuthorized) || !!TOKEN; const toast = useRecoilValue($toast); const generationNumber = useRecoilValue($generationNumber); + const isMaster = useRecoilValue($isMaster); const getTeams = useRecoilCallback(({ set }) => async () => { const { data: teams } = await api.getTeams(generationNumber); @@ -214,7 +242,9 @@ const App = () => { path={PATH.ADMIN_MEMBERS} element={ - + + + } /> diff --git a/src/components/common/LNB/LNB.component.tsx b/src/components/common/LNB/LNB.component.tsx index 0d4f6984..6c0828dc 100644 --- a/src/components/common/LNB/LNB.component.tsx +++ b/src/components/common/LNB/LNB.component.tsx @@ -17,7 +17,7 @@ import ScheduleIcon from '@/assets/svg/schedule.svg'; import RecruitIcon from '@/assets/svg/recruit.svg'; import FaqIcon from '@/assets/svg/faq.svg'; // import SignupCodeIcon from "@/assets/svg/signup-code.svg" -// import AdminMembersIcon from '@/assets/svg/admin-members.svg'; +import AdminMembersIcon from '@/assets/svg/admin-members.svg'; // import MyPageIcon from "@/assets/svg/my-page.svg" const navigationItems: NavigationItem[] = [ @@ -77,20 +77,21 @@ const navigationItems: NavigationItem[] = [ }, ], }, - // { - // title: '계정 관리', - // menus: [ - // { - // label: '계정 목록', - // to: PATH.ADMIN_MEMBERS, - // icon: , - // }, - // // { - // // label: '내 정보', - // // to: PATH.MY_PAGE, - // // }, - // ], - // }, + { + title: '계정 관리', + menus: [ + { + label: '계정 목록', + to: PATH.ADMIN_MEMBERS, + icon: , + isMasterOnly: true, + }, + // { + // label: '내 정보', + // to: PATH.MY_PAGE, + // }, + ], + }, ]; const LNB = () => { diff --git a/src/components/common/Navigation/Navigation.component.tsx b/src/components/common/Navigation/Navigation.component.tsx index 561f7861..a813bdde 100644 --- a/src/components/common/Navigation/Navigation.component.tsx +++ b/src/components/common/Navigation/Navigation.component.tsx @@ -1,18 +1,19 @@ import React, { ReactElement } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { useResetRecoilState } from 'recoil'; +import { useRecoilValue, useResetRecoilState } from 'recoil'; import { ValueOf } from '@/types'; import { colors } from '@/styles'; import * as Styled from './Navigation.styled'; import { ACCESS_TOKEN, PATH } from '@/constants'; import LogoutIcon from '@/assets/svg/logout-24.svg'; -import { $me } from '@/store'; +import { $me, $isMaster } from '@/store'; interface Menu { label: string; to: ValueOf; icon: ReactElement; + isMasterOnly?: boolean; } export interface NavigationItem { @@ -38,6 +39,7 @@ const Navigation = ({ size, inActiveColor, items, showBottomBorder = true }: Nav const { pathname } = useLocation(); const navigate = useNavigate(); const resetMe = useResetRecoilState($me); + const isMaster = useRecoilValue($isMaster); const handleLogout = () => { localStorage.removeItem(ACCESS_TOKEN); @@ -51,6 +53,10 @@ const Navigation = ({ size, inActiveColor, items, showBottomBorder = true }: Nav <> {item.title} {item.menus.map((menu, menuIdx) => { + if (menu.isMasterOnly && !isMaster) { + return null; + } + const isActive = pathname .split('/') .some((pathNameItem) => `/${pathNameItem}` === menu.to); diff --git a/src/store/login.ts b/src/store/login.ts index 18b11e69..3f9b1056 100644 --- a/src/store/login.ts +++ b/src/store/login.ts @@ -2,6 +2,7 @@ import { atom } from 'recoil'; import { LoginResponse, MemberPositionType } from '@/types/dto'; import { TeamType, RoleType } from '@/components/common/UserProfile/UserProfile.component'; import { selectorWithRefresher } from './recoil'; +import { ScoreType } from '@/components/ActivityScore'; export const $me = atom({ key: 'me', @@ -11,6 +12,7 @@ export const $me = atom({ adminMemberId: 0, phoneNumber: '', username: '', + position: undefined, }, }, }); @@ -35,3 +37,13 @@ export const $profile = selectorWithRefresher<[string, string, MemberPositionTyp return [...formattedPosition, position]; }, }); + +const MASTER_SCORE_TYPES = [ScoreType.MASHUP_LEADER, ScoreType.MASHUP_SUBLEADER] as string[]; + +export const $isMaster = selectorWithRefresher({ + key: 'isMaster', + get: ({ get }) => { + const { adminMember } = get($me); + return MASTER_SCORE_TYPES.includes(adminMember?.position ?? ''); + }, +});