diff --git a/src/App.tsx b/src/App.tsx index cb66b19..0bcfadb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,7 +36,6 @@ const ManagedApplicationDetail = lazy(() => import('./pages/Manager/ManagedAppli const ManagedApplicationList = lazy(() => import('./pages/Manager/ManagedApplicationList')); const ManagedClubDetail = lazy(() => import('./pages/Manager/ManagedClubDetail')); const ManagedClubInfo = lazy(() => import('./pages/Manager/ManagedClubProfile')); -const ManagedClubList = lazy(() => import('./pages/Manager/ManagedClubList')); const ManagedMemberApplicationDetail = lazy(() => import('./pages/Manager/ManagedMemberApplicationDetail')); const ManagedMemberList = lazy(() => import('./pages/Manager/ManagedMemberList')); const ManagedRecruitment = lazy(() => import('./pages/Manager/ManagedRecruitment')); @@ -94,7 +93,6 @@ function App() { } /> - } /> } /> } /> } /> diff --git a/src/components/common/Card.tsx b/src/components/common/Card.tsx index 6003bc5..6c9e96e 100644 --- a/src/components/common/Card.tsx +++ b/src/components/common/Card.tsx @@ -1,16 +1,16 @@ import type { HTMLAttributes, ReactNode } from 'react'; -import clsx from 'clsx'; -import { twMerge } from 'tailwind-merge'; +import { cn } from '@/utils/ts/cn'; interface CardProps extends HTMLAttributes { children: ReactNode; } function Card({ children, className, ...props }: CardProps) { - const base = 'border-indigo-5 flex w-full flex-col gap-3 rounded-lg border bg-white p-3'; - return ( -
+
{children}
); diff --git a/src/components/layout/Header/components/ProfileHeader.tsx b/src/components/layout/Header/components/ProfileHeader.tsx deleted file mode 100644 index 10cdbcf..0000000 --- a/src/components/layout/Header/components/ProfileHeader.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { Ref } from 'react'; -import NotificationBell from './NotificationBell'; - -function ProfileHeader({ headerRef }: { headerRef?: Ref }) { - return ( -
- -
- ); -} - -export default ProfileHeader; diff --git a/src/components/layout/Header/headerConfig.ts b/src/components/layout/Header/headerConfig.ts index ad2b0f1..60acfd1 100644 --- a/src/components/layout/Header/headerConfig.ts +++ b/src/components/layout/Header/headerConfig.ts @@ -17,13 +17,10 @@ export const HEADER_CONFIGS: HeaderConfig[] = [ type: 'default', match: (pathname) => /^\/clubs\/\d+$/.test(pathname), }, - { - type: 'profile', - match: (pathname) => pathname === '/mypage', - }, { type: 'info', - match: (pathname) => pathname === '/home' || pathname === '/timer' || pathname === '/council', + match: (pathname) => + pathname === '/home' || pathname === '/timer' || pathname === '/council' || pathname === '/mypage', }, { type: 'chatList', diff --git a/src/components/layout/Header/index.tsx b/src/components/layout/Header/index.tsx index 2e12ba5..4af2690 100644 --- a/src/components/layout/Header/index.tsx +++ b/src/components/layout/Header/index.tsx @@ -6,7 +6,6 @@ import DefaultHeader from './components/DefaultHeader'; import InfoHeader from './components/InfoHeader'; import ManagerHeader from './components/ManagerHeader'; import PlainSubpageHeader from './components/PlainSubpageHeader'; -import ProfileHeader from './components/ProfileHeader'; import ScheduleHeader from './components/ScheduleHeader'; import SubpageHeader from './components/SubpageHeader'; import { getHeaderPresentation } from './presentation'; @@ -22,7 +21,6 @@ function Header({ headerRef }: HeaderProps) { const { title, type: headerType } = getHeaderPresentation(pathname); const HEADER_RENDERERS: Record = { - profile: ({ headerRef }) => , info: ({ headerRef }) => , chatList: ({ title, headerRef }) => , chat: ({ headerRef }) => , diff --git a/src/components/layout/Header/types.ts b/src/components/layout/Header/types.ts index 9a4a4fd..9d219d9 100644 --- a/src/components/layout/Header/types.ts +++ b/src/components/layout/Header/types.ts @@ -2,7 +2,6 @@ import type { ReactNode, Ref } from 'react'; export type HeaderType = | 'info' - | 'profile' | 'chat' | 'none' | 'notification' diff --git a/src/pages/Manager/ManagedClubList/index.tsx b/src/pages/Manager/ManagedClubList/index.tsx deleted file mode 100644 index 8f2fe5e..0000000 --- a/src/pages/Manager/ManagedClubList/index.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; -import { Link } from 'react-router-dom'; -import { managedClubQueries } from '@/apis/club/managedQueries'; -import RightArrowIcon from '@/assets/svg/Chevron-left-dark.svg'; -import ManagerInfoCard from '@/pages/User/MyPage/components/ManagerInfoCard'; - -function ManagedClubList() { - const { data: managedClubList } = useSuspenseQuery(managedClubQueries.clubs()); - - return ( -
- -
-

동아리 목록

-
- {managedClubList.joinedClubs.map((club) => ( - -
- Club Avatar -
- {club.name} - {club.categoryName} -
-
- - - ))} -
-
-
- ); -} - -export default ManagedClubList; diff --git a/src/pages/User/MyPage/components/MyPageRows.tsx b/src/pages/User/MyPage/components/MyPageRows.tsx new file mode 100644 index 0000000..f5686f2 --- /dev/null +++ b/src/pages/User/MyPage/components/MyPageRows.tsx @@ -0,0 +1,56 @@ +import type { ReactNode } from 'react'; +import ChatIcon from '@/assets/svg/chat.svg'; +import RightArrowIcon from '@/assets/svg/Chevron-left-dark.svg'; + +type MyPageRowIcon = typeof ChatIcon; + +interface MyPageRowBaseProps { + icon: MyPageRowIcon; + label: string; +} + +interface MyPageRowLayoutProps extends MyPageRowBaseProps { + rightSlot: ReactNode; + labelClassName: string; +} + +type MyPageLinkRowProps = MyPageRowBaseProps; + +interface MyPageInfoRowProps extends MyPageRowBaseProps { + value: string; +} + +type MyPageActionRowProps = MyPageRowBaseProps; + +function MyPageRowLayout({ icon: Icon, label, rightSlot, labelClassName }: MyPageRowLayoutProps) { + return ( +
+
+ +
{label}
+
+ {rightSlot} +
+ ); +} + +export function MyPageLinkRow({ icon, label }: MyPageLinkRowProps) { + return } labelClassName="text-sub2" />; +} + +export function MyPageInfoRow({ icon, label, value }: MyPageInfoRowProps) { + return ( + {value}
} + labelClassName="text-sm leading-4 font-semibold" + /> + ); +} + +export function MyPageActionRow({ icon, label }: MyPageActionRowProps) { + return ( + + ); +} diff --git a/src/pages/User/MyPage/components/UserInfoCard.tsx b/src/pages/User/MyPage/components/UserInfoCard.tsx index 514b77e..77dadf3 100644 --- a/src/pages/User/MyPage/components/UserInfoCard.tsx +++ b/src/pages/User/MyPage/components/UserInfoCard.tsx @@ -33,7 +33,7 @@ function UserInfoCard() { }; return ( - +
diff --git a/src/pages/User/MyPage/index.tsx b/src/pages/User/MyPage/index.tsx index 2a847c0..3867b5d 100644 --- a/src/pages/User/MyPage/index.tsx +++ b/src/pages/User/MyPage/index.tsx @@ -1,86 +1,109 @@ import { useSuspenseQuery } from '@tanstack/react-query'; import { Link } from 'react-router-dom'; -import { authQueries } from '@/apis/auth/queries'; +import { managedClubQueries } from '@/apis/club/managedQueries'; import ChatIcon from '@/assets/svg/chat.svg'; -import RightArrowIcon from '@/assets/svg/chevron-right.svg'; +import RightArrowIcon from '@/assets/svg/Chevron-left-dark.svg'; import FileSearchIcon from '@/assets/svg/file-search.svg'; import FileIcon from '@/assets/svg/file.svg'; import LayersIcon from '@/assets/svg/layers.svg'; import LogoutIcon from '@/assets/svg/logout.svg'; -import UserIdCardIcon from '@/assets/svg/user-id-card.svg'; import UserSquareIcon from '@/assets/svg/user-square.svg'; import BottomModal from '@/components/common/BottomModal'; +import { MyPageActionRow, MyPageInfoRow, MyPageLinkRow } from '@/pages/User/MyPage/components/MyPageRows'; import useBooleanState from '@/utils/hooks/useBooleanState'; import { useAdminChatMutation } from '../hooks/useAdminChatMutation'; import UserInfoCard from './components/UserInfoCard'; import { useLogoutMutation } from './hooks/useLogout'; -const menuItems = [ - { to: 'manager', icon: UserIdCardIcon, label: '동아리 관리' }, - { to: '/legal/oss', icon: FileSearchIcon, label: '오픈소스 라이선스' }, - { to: '/legal/terms', icon: FileIcon, label: '코넥트 약관 확인' }, - { to: '/legal/privacy', icon: UserSquareIcon, label: '개인정보 처리 방침' }, +interface LegalMenuState { + backPath: string; +} + +interface MenuItem { + to: string; + icon: typeof ChatIcon; + label: string; + state?: LegalMenuState; +} + +interface ManagedClubSummary { + id: number; + name: string; + categoryName: string; + imageUrl: string; +} + +interface ManagedClubLinkProps { + club: ManagedClubSummary; +} + +const menuItems: MenuItem[] = [ + { to: '/legal/oss', icon: FileSearchIcon, label: '오픈소스 라이선스', state: { backPath: '/mypage' } }, + { to: '/legal/terms', icon: FileIcon, label: '코넥트 약관 확인', state: { backPath: '/mypage' } }, + { to: '/legal/privacy', icon: UserSquareIcon, label: '개인정보 처리 방침', state: { backPath: '/mypage' } }, ]; +function ManagedClubLink({ club }: ManagedClubLinkProps) { + return ( + +
+ Club Avatar +
+ {club.name} + {club.categoryName} +
+
+ + + ); +} + function MyPage() { - const { data: myInfo } = useSuspenseQuery(authQueries.myInfo()); + const { data: managedClubList } = useSuspenseQuery(managedClubQueries.clubs()); const { mutate: logout, isPending: isLoggingOut } = useLogoutMutation(); const { value: isOpen, setTrue: openModal, setFalse: closeModal } = useBooleanState(false); const { mutate: goToAdminChat, isPending: isCreatingAdminChat } = useAdminChatMutation(); return ( -
+
-
- {menuItems - .filter(({ to }) => to !== 'manager' || myInfo.isClubManager || myInfo.role === 'ADMIN') - .map(({ to, icon: Icon, label }) => ( - -
-
- -
{label}
-
- -
- + +
+ 관리중인 동아리 +
+ {managedClubList.joinedClubs.map((club) => ( + ))} +
+
+
+ {menuItems.map(({ to, icon, label, state }) => ( + + + + ))}
-
-
- -
버전관리
-
-
- {window.APP_VERSION ? `v${window.APP_VERSION}` : '-'} -
-
+
-
diff --git a/src/utils/hooks/useSmartBack.ts b/src/utils/hooks/useSmartBack.ts index 315e8fd..5f1ae1e 100644 --- a/src/utils/hooks/useSmartBack.ts +++ b/src/utils/hooks/useSmartBack.ts @@ -61,7 +61,9 @@ export function useSmartBack() { const parts = pathname.split('/'); const clubId = parts[3]; - if (parts[4] === 'info') { + if (parts.length === 4) { + targetPath = '/mypage'; + } else if (parts[4] === 'info') { targetPath = `/mypage/manager/${clubId}`; } else if (parts[4] === 'recruitment' && parts[5]) { targetPath = `/mypage/manager/${clubId}/recruitment`; @@ -76,7 +78,7 @@ export function useSmartBack() { } else if (parts[4] === 'members') { targetPath = `/mypage/manager/${clubId}`; } else { - targetPath = `/mypage/manager`; + targetPath = '/mypage'; } } else if (pathname === '/mypage/manager') { targetPath = '/mypage';