UI/UX 개선: 사이드바 레이아웃 안정화 및 메뉴 hover 효과 개선

- 사이드바 고정 너비 설정으로 메뉴 클릭 시 너비 변화 방지
- 메뉴 항목 hover 효과 일관성 개선 (고정 높이, 부드러운 색상 전환)
- 디버깅 로그 제거로 성능 최적화
- 관리자 페이지 카드 디자인 개선 (그라데이션 배경, 아이콘 색상 조정)
This commit is contained in:
leeheejin
2025-09-25 09:29:56 +09:00
parent 1a60177fe4
commit e3cd6dc3a0
13 changed files with 307 additions and 269 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useState, Suspense } from "react";
import { useState, Suspense, useEffect } from "react";
import { useRouter, usePathname, useSearchParams } from "next/navigation";
import { Button } from "@/components/ui/button";
import {
@@ -197,8 +197,27 @@ function AppLayoutInner({ children }: AppLayoutProps) {
const searchParams = useSearchParams();
const { user, logout, refreshUserData } = useAuth();
const { userMenus, adminMenus, loading, refreshMenus } = useMenu();
const [sidebarOpen, setSidebarOpen] = useState(false);
const [sidebarOpen, setSidebarOpen] = useState(true);
const [expandedMenus, setExpandedMenus] = useState<Set<string>>(new Set());
const [isMobile, setIsMobile] = useState(false);
// 화면 크기 감지 및 사이드바 초기 상태 설정
useEffect(() => {
const checkIsMobile = () => {
const mobile = window.innerWidth < 1024; // lg 브레이크포인트
setIsMobile(mobile);
// 모바일에서만 사이드바를 닫음
if (mobile) {
setSidebarOpen(false);
} else {
setSidebarOpen(true);
}
};
checkIsMobile();
window.addEventListener('resize', checkIsMobile);
return () => window.removeEventListener('resize', checkIsMobile);
}, []);
// 프로필 관련 로직
const {
@@ -253,15 +272,10 @@ function AppLayoutInner({ children }: AppLayoutProps) {
? `/screens/${firstScreen.screenId}?mode=admin`
: `/screens/${firstScreen.screenId}`;
console.log("🎯 메뉴에서 화면으로 이동:", {
menuName: menu.name,
screenId: firstScreen.screenId,
isAdminMode,
targetPath: screenPath,
});
router.push(screenPath);
setSidebarOpen(false);
if (isMobile) {
setSidebarOpen(false);
}
return;
}
} catch (error) {
@@ -271,10 +285,11 @@ function AppLayoutInner({ children }: AppLayoutProps) {
// 할당된 화면이 없고 URL이 있으면 기존 URL로 이동
if (menu.url && menu.url !== "#") {
router.push(menu.url);
setSidebarOpen(false);
if (isMobile) {
setSidebarOpen(false);
}
} else {
// URL도 없고 할당된 화면도 없으면 경고 메시지
console.warn("메뉴에 URL이나 할당된 화면이 없습니다:", menu);
toast.warning("이 메뉴에는 연결된 페이지나 화면이 없습니다.");
}
}
@@ -295,7 +310,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
await logout();
router.push("/login");
} catch (error) {
console.error("로그아웃 실패:", error);
// 로그아웃 실패 시 처리
}
};
@@ -306,7 +321,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
return (
<div key={menu.id}>
<div
className={`group flex cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm font-medium transition-colors hover:cursor-pointer ${
className={`group flex cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm font-medium transition-colors duration-200 ease-in-out h-10 ${
pathname === menu.url
? "bg-gradient-to-br from-slate-100 to-blue-100/40 text-slate-900 border-l-4 border-blue-500"
: isExpanded
@@ -315,9 +330,9 @@ function AppLayoutInner({ children }: AppLayoutProps) {
} ${level > 0 ? "ml-6" : ""}`}
onClick={() => handleMenuClick(menu)}
>
<div className="flex items-center">
<div className="flex items-center min-w-0 flex-1">
{menu.icon}
<span className="ml-3">{menu.name}</span>
<span className="ml-3 truncate" title={menu.name}>{menu.name}</span>
</div>
{menu.hasChildren && (
<div className="ml-auto">
@@ -339,8 +354,10 @@ function AppLayoutInner({ children }: AppLayoutProps) {
}`}
onClick={() => handleMenuClick(child)}
>
{child.icon}
<span className="ml-3">{child.name}</span>
<div className="flex items-center min-w-0 flex-1">
{child.icon}
<span className="ml-3 truncate" title={child.name}>{child.name}</span>
</div>
</div>
))}
</div>
@@ -369,22 +386,29 @@ function AppLayoutInner({ children }: AppLayoutProps) {
{/* MainHeader 컴포넌트 사용 */}
<MainHeader
user={user}
onSidebarToggle={() => setSidebarOpen(!sidebarOpen)}
onSidebarToggle={() => {
// 모바일에서만 토글 동작
if (isMobile) {
setSidebarOpen(!sidebarOpen);
}
}}
onProfileClick={openProfileModal}
onLogout={handleLogout}
/>
<div className="flex flex-1">
{/* 모바일 사이드바 오버레이 */}
{sidebarOpen && (
{sidebarOpen && isMobile && (
<div className="fixed inset-0 z-30 bg-black/50 lg:hidden" onClick={() => setSidebarOpen(false)} />
)}
{/* 왼쪽 사이드바 */}
<aside
className={`${
sidebarOpen ? "translate-x-0" : "-translate-x-full"
} fixed top-14 left-0 z-40 flex h-full w-64 flex-col border-r border-slate-200 bg-white transition-transform duration-300 lg:relative lg:top-0 lg:z-auto lg:h-full lg:translate-x-0 lg:transform-none`}
isMobile
? (sidebarOpen ? "translate-x-0" : "-translate-x-full") + " fixed top-14 left-0 z-40"
: "translate-x-0 relative top-0 z-auto"
} flex h-full w-72 min-w-72 max-w-72 flex-col border-r border-slate-200 bg-white transition-transform duration-300`}
>
{/* 사이드바 상단 - Admin/User 모드 전환 버튼 (관리자만) */}
{(user as ExtendedUserInfo)?.userType === "admin" && (
@@ -428,7 +452,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
</aside>
{/* 가운데 컨텐츠 영역 */}
<main className="flex-1 bg-white">{children}</main>
<main className="flex-1 min-w-0 bg-white overflow-hidden">{children}</main>
</div>
{/* 프로필 수정 모달 */}