diff --git a/src/app/(root)/layout.tsx b/src/app/(root)/layout.tsx
index 36a3238..e32fcef 100644
--- a/src/app/(root)/layout.tsx
+++ b/src/app/(root)/layout.tsx
@@ -1,28 +1,16 @@
'use client';
-import { useState } from 'react';
import Image from 'next/image';
import { usePathname, useRouter } from 'next/navigation';
import { useCurrentUser } from '@/hooks/useCurrentUser';
-import {
- Sidebar,
- SidebarButton,
- SidebarTeamSelect,
- SidebarAddButton,
- MobileHeader,
- MobileDrawer,
-} from '@/components/sidebar';
-import boardSmall from '@/assets/icons/board/boardSmall.svg';
-import boardLarge from '@/assets/icons/board/boardLarge.svg';
-import chessSmall from '@/assets/icons/chess/chessSmall.svg';
-import chessBig from '@/assets/icons/chess/chessBig.svg';
+import { Sidebar, MobileHeader } from '@/components/sidebar';
+import TeamSidebarDropdown from './[teamid]/_domain/components/Team/TeamSidebarDropdown';
import humanBig from '@/assets/buttons/human/humanBig.svg';
import styles from './layout.module.css';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const router = useRouter();
- const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const { data: user, isPending } = useCurrentUser();
// isPending: 최초 로딩 중 (undefined)
@@ -30,11 +18,12 @@ export default function RootLayout({ children }: { children: React.ReactNode })
// user !== null: 로그인
const isLoggedIn = !isPending && user !== null && user !== undefined;
const isLanding = pathname === '/';
- const firstGroup = user?.memberships?.[0]?.group;
- // [teamid] 페이지는 자체 모바일 헤더(TeamNavClient)를 사용하므로 root layout의 MobileHeader를 숨김
- const knownPaths = ['/', '/addteam', '/boards', '/mypage', '/history', '/list'];
- const isTeamIdPage = !knownPaths.some((p) => pathname === p || pathname.startsWith(p + '/'));
+ // 자체 사이드바가 없는 페이지에서만 root layout 사이드바 표시
+ const rootSidebarPaths = ['/', '/boards', '/mypage'];
+ const showRootSidebar = rootSidebarPaths.some(
+ (p) => pathname === p || pathname.startsWith(p + '/'),
+ );
const handleProfileClick = () => {
if (isLoggedIn) {
@@ -51,126 +40,53 @@ export default function RootLayout({ children }: { children: React.ReactNode })
return (
- router.push('/addteam')}
- profileImage={
- user?.image ? (
-
- ) : (
-
- )
- }
- profileName={user?.nickname ?? '사용자'}
- profileTeam={firstGroup?.name ?? ''}
- teamSelect={(isCollapsed: boolean) =>
- firstGroup ? (
- !isCollapsed ? (
-
- ) : (
-
- )
- }
- label={firstGroup.name}
- isSelected
- onClick={() => router.push(`/${firstGroup.id}`)}
+ {showRootSidebar && (
+ router.push('/addteam')}
+ profileImage={
+ user?.image ? (
+
) : (
-
- ) : (
-
- )
- }
- label={firstGroup.name}
- isActive
- iconOnly
- onClick={() => router.push(`/${firstGroup.id}`)}
- />
+
)
- ) : null
- }
- addButton={
- isLoggedIn
- ? (isCollapsed: boolean) => (
- <>
- {!isCollapsed && (
- router.push('/addteam')} />
- )}
-
-
- }
- label="자유게시판"
- isActive
- iconOnly={isCollapsed}
- href="/boards"
- />
- >
- )
- : undefined
- }
- />
- {!isTeamIdPage && (
- <>
-
- ) : undefined
- }
- onMenuClick={() => setIsDrawerOpen(true)}
- onProfileClick={handleProfileClick}
- />
- setIsDrawerOpen(false)}>
- }
- label="자유게시판"
- isActive
- href="/boards"
- onClick={() => setIsDrawerOpen(false)}
- />
-
- >
+ }
+ profileName={user?.nickname ?? '사용자'}
+ profileTeam={user?.memberships?.[0]?.group?.name ?? ''}
+ teamSelect={
+ isLoggedIn
+ ? (isCollapsed: boolean) =>
+ : undefined
+ }
+ />
+ )}
+ {showRootSidebar && (
+
+ ) : undefined
+ }
+ onProfileClick={handleProfileClick}
+ onLogout={handleLogout}
+ drawerContent={}
+ />
)}
{children}
diff --git a/src/app/(root)/mypage/page.tsx b/src/app/(root)/mypage/page.tsx
index badc6c1..6f497c4 100644
--- a/src/app/(root)/mypage/page.tsx
+++ b/src/app/(root)/mypage/page.tsx
@@ -33,6 +33,7 @@ export default function ProfilePage() {
} = useUser();
const [showToast, setShowToast] = useState(false);
+ const [successToast, setSuccessToast] = useState(null);
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false);
const [isWithdrawModalOpen, setIsWithdrawModalOpen] = useState(false);
const newPasswordRef = useRef(null);
@@ -47,6 +48,7 @@ export default function ProfilePage() {
const result = await updateProfile();
if (result.success) {
setShowToast(false);
+ setSuccessToast('이름이 변경되었습니다.');
}
};
@@ -63,6 +65,7 @@ export default function ProfilePage() {
setIsPasswordModalOpen(false);
if (newPasswordRef.current) newPasswordRef.current.value = '';
if (confirmPasswordRef.current) confirmPasswordRef.current.value = '';
+ setSuccessToast('비밀번호가 변경되었습니다.');
}
};
@@ -171,8 +174,8 @@ export default function ProfilePage() {
- {hasChanges && (
-
+
+ {hasChanges && (
setShowToast(false)}
className={styles.toast}
/>
-
- )}
+ )}
+ {successToast && (
+
setSuccessToast(null)}
+ className={styles.toast}
+ />
+ )}
+
void;
+ /** 드로어 내부 콘텐츠 (전달 시 햄버거 메뉴 클릭으로 드로어 표시) */
+ drawerContent?: ReactNode;
/** 프로필 버튼 클릭 시 호출되는 콜백 */
onProfileClick?: () => void;
/** 로그아웃 클릭 시 호출되는 콜백 */
@@ -37,16 +41,27 @@ type MobileHeaderProps = {
export default function MobileHeader({
isLoggedIn,
profileImage,
- onMenuClick,
+ drawerContent,
onProfileClick,
onLogout,
onLogoClick,
logoWidth = 102,
logoHeight = 20,
}: MobileHeaderProps) {
+ const router = useRouter();
const [showProfileMenu, setShowProfileMenu] = useState(false);
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const profileMenuRef = useRef(null);
+ const defaultLogout = useCallback(async () => {
+ await fetch('/api/auth/logout', { method: 'POST' });
+ router.push('/login');
+ }, [router]);
+
+ const handleLogout = onLogout ?? defaultLogout;
+ const handleProfileClick = onProfileClick ?? (() => router.push('/mypage'));
+ const handleLogoClick = onLogoClick ?? (() => router.push('/addteam'));
+
useEffect(() => {
if (!showProfileMenu) return;
const handleClickOutside = (e: MouseEvent) => {
@@ -61,12 +76,7 @@ export default function MobileHeader({
if (!isLoggedIn) {
return (
-
+
@@ -79,17 +89,12 @@ export default function MobileHeader({
-
@@ -109,7 +114,7 @@ export default function MobileHeader({
className={styles.profileMenuItem}
onClick={() => {
setShowProfileMenu(false);
- onProfileClick?.();
+ handleProfileClick();
}}
>
마이페이지
@@ -119,7 +124,7 @@ export default function MobileHeader({
className={`${styles.profileMenuItem} ${styles.profileMenuDanger}`}
onClick={() => {
setShowProfileMenu(false);
- onLogout?.();
+ handleLogout();
}}
>
로그아웃
@@ -127,6 +132,11 @@ export default function MobileHeader({
)}
+ {drawerContent && (
+ setIsDrawerOpen(false)}>
+ {drawerContent}
+
+ )}
);
}
diff --git a/src/components/sidebar/Sidebar.tsx b/src/components/sidebar/Sidebar.tsx
index dc814ed..8967b00 100644
--- a/src/components/sidebar/Sidebar.tsx
+++ b/src/components/sidebar/Sidebar.tsx
@@ -1,7 +1,8 @@
'use client';
import type { ReactNode } from 'react';
-import { useState, useRef, useEffect } from 'react';
+import { useState, useRef, useEffect, useCallback } from 'react';
+import { useRouter } from 'next/navigation';
import Image from 'next/image';
import clsx from 'clsx';
import { motion, AnimatePresence } from 'framer-motion';
@@ -49,10 +50,20 @@ export default function Sidebar({
onLogout,
onLogoClick,
}: SidebarProps) {
+ const router = useRouter();
const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed ?? false);
const [showProfileMenu, setShowProfileMenu] = useState(false);
const profileMenuRef = useRef(null);
+ const defaultLogout = useCallback(async () => {
+ await fetch('/api/auth/logout', { method: 'POST' });
+ router.push('/login');
+ }, [router]);
+
+ const handleLogout = onLogout ?? defaultLogout;
+ const handleProfileClick = onProfileClick ?? (() => router.push('/mypage'));
+ const handleLogoClick = onLogoClick ?? (() => router.push('/addteam'));
+
useEffect(() => {
if (!showProfileMenu) return;
const handleClickOutside = (e: MouseEvent) => {
@@ -72,7 +83,7 @@ export default function Sidebar({
const renderFooter = () => {
if (footer) {
return (
-
+
{renderSlot(footer)}
);
@@ -80,7 +91,7 @@ export default function Sidebar({
if (!isLoggedIn) {
return (
-
+
{!isCollapsed && (
{
setShowProfileMenu(false);
- onProfileClick?.();
+ handleProfileClick();
}}
>
마이페이지
@@ -120,7 +131,7 @@ export default function Sidebar({
className={`${styles.profileMenuItem} ${styles.profileMenuDanger}`}
onClick={() => {
setShowProfileMenu(false);
- onLogout?.();
+ handleLogout();
}}
>
로그아웃
@@ -155,12 +166,7 @@ export default function Sidebar({
transition={{ duration: 0.3, ease: 'easeInOut' }}
>