271 lines
9.3 KiB
TypeScript
271 lines
9.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent } from "@/components/ui/card";
|
|
import {
|
|
Home,
|
|
FileText,
|
|
Users,
|
|
Settings,
|
|
Package,
|
|
BarChart3,
|
|
LogOut,
|
|
Menu,
|
|
X,
|
|
ChevronDown,
|
|
ChevronRight,
|
|
} from "lucide-react";
|
|
import { useAuth } from "@/hooks/useAuth";
|
|
|
|
interface UserInfo {
|
|
userId: string;
|
|
userName: string;
|
|
deptName: string;
|
|
email: string;
|
|
}
|
|
|
|
interface MenuItem {
|
|
id: string;
|
|
title: string;
|
|
icon: any;
|
|
children?: MenuItem[];
|
|
}
|
|
|
|
const menuItems: MenuItem[] = [
|
|
{
|
|
id: "dashboard",
|
|
title: "대시보드",
|
|
icon: Home,
|
|
},
|
|
{
|
|
id: "project",
|
|
title: "프로젝트 관리",
|
|
icon: FileText,
|
|
children: [
|
|
{ id: "project-list", title: "프로젝트 목록", icon: FileText },
|
|
{ id: "project-concept", title: "프로젝트 컨셉", icon: FileText },
|
|
{ id: "project-planning", title: "프로젝트 기획", icon: FileText },
|
|
],
|
|
},
|
|
{
|
|
id: "part",
|
|
title: "부품 관리",
|
|
icon: Package,
|
|
children: [
|
|
{ id: "part-list", title: "부품 목록", icon: Package },
|
|
{ id: "part-bom", title: "BOM 관리", icon: Package },
|
|
{ id: "part-inventory", title: "재고 관리", icon: Package },
|
|
],
|
|
},
|
|
{
|
|
id: "user",
|
|
title: "사용자 관리",
|
|
icon: Users,
|
|
children: [
|
|
{ id: "user-list", title: "사용자 목록", icon: Users },
|
|
{ id: "user-auth", title: "권한 관리", icon: Users },
|
|
],
|
|
},
|
|
{
|
|
id: "report",
|
|
title: "보고서",
|
|
icon: BarChart3,
|
|
children: [
|
|
{ id: "report-project", title: "프로젝트 보고서", icon: BarChart3 },
|
|
{ id: "report-cost", title: "비용 보고서", icon: BarChart3 },
|
|
],
|
|
},
|
|
{
|
|
id: "settings",
|
|
title: "시스템 설정",
|
|
icon: Settings,
|
|
children: [
|
|
{ id: "settings-system", title: "시스템 설정", icon: Settings },
|
|
{ id: "settings-common", title: "공통 코드", icon: Settings },
|
|
],
|
|
},
|
|
];
|
|
|
|
export default function DashboardPage() {
|
|
const { user, logout } = useAuth();
|
|
const [sidebarOpen, setSidebarOpen] = useState(true);
|
|
const [expandedMenus, setExpandedMenus] = useState<Set<string>>(new Set(["dashboard"]));
|
|
const [selectedMenu, setSelectedMenu] = useState("dashboard");
|
|
const [currentContent, setCurrentContent] = useState<string>("dashboard");
|
|
|
|
const handleLogout = async () => {
|
|
await logout();
|
|
};
|
|
|
|
const toggleMenu = (menuId: string) => {
|
|
const newExpanded = new Set(expandedMenus);
|
|
if (newExpanded.has(menuId)) {
|
|
newExpanded.delete(menuId);
|
|
} else {
|
|
newExpanded.add(menuId);
|
|
}
|
|
setExpandedMenus(newExpanded);
|
|
};
|
|
|
|
const handleMenuClick = (menuId: string) => {
|
|
setSelectedMenu(menuId);
|
|
setCurrentContent(menuId);
|
|
};
|
|
|
|
const renderContent = () => {
|
|
switch (currentContent) {
|
|
case "dashboard":
|
|
return (
|
|
<div className="space-y-6">
|
|
<h1 className="text-3xl font-bold text-slate-900">대시보드</h1>
|
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4">
|
|
<Card>
|
|
<CardContent className="p-6">
|
|
<div className="flex items-center">
|
|
<FileText className="h-8 w-8 text-blue-600" />
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-slate-600">전체 프로젝트</p>
|
|
<p className="text-2xl font-bold text-slate-900">24</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="p-6">
|
|
<div className="flex items-center">
|
|
<Package className="h-8 w-8 text-green-600" />
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-slate-600">등록된 부품</p>
|
|
<p className="text-2xl font-bold text-slate-900">1,247</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="p-6">
|
|
<div className="flex items-center">
|
|
<Users className="h-8 w-8 text-purple-600" />
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-slate-600">활성 사용자</p>
|
|
<p className="text-2xl font-bold text-slate-900">89</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardContent className="p-6">
|
|
<div className="flex items-center">
|
|
<BarChart3 className="h-8 w-8 text-orange-600" />
|
|
<div className="ml-4">
|
|
<p className="text-sm font-medium text-slate-600">진행중인 작업</p>
|
|
<p className="text-2xl font-bold text-slate-900">12</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
<Card>
|
|
<CardContent className="p-6">
|
|
<h2 className="mb-4 text-xl font-semibold">최근 활동</h2>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center space-x-4">
|
|
<div className="h-2 w-2 rounded-full bg-blue-500"></div>
|
|
<span className="text-sm text-slate-600">새로운 프로젝트 '제품 A' 생성됨</span>
|
|
<span className="text-xs text-slate-400">2시간 전</span>
|
|
</div>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="h-2 w-2 rounded-full bg-green-500"></div>
|
|
<span className="text-sm text-slate-600">부품 'PCB-001' 승인 완료</span>
|
|
<span className="text-xs text-slate-400">4시간 전</span>
|
|
</div>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="h-2 w-2 rounded-full bg-orange-500"></div>
|
|
<span className="text-sm text-slate-600">사용자 '김개발' 권한 변경</span>
|
|
<span className="text-xs text-slate-400">1일 전</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
default:
|
|
return (
|
|
<div className="space-y-6">
|
|
<h1 className="text-3xl font-bold text-slate-900">
|
|
{menuItems.find((item) => item.id === currentContent)?.title ||
|
|
menuItems.flatMap((item) => item.children || []).find((child) => child.id === currentContent)?.title ||
|
|
"페이지를 찾을 수 없습니다"}
|
|
</h1>
|
|
<Card>
|
|
<CardContent className="p-6">
|
|
<p className="text-slate-600">{currentContent} 페이지 컨텐츠가 여기에 표시됩니다.</p>
|
|
<p className="mt-2 text-sm text-slate-400">각 메뉴에 맞는 컴포넌트를 개발하여 연결할 수 있습니다.</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
};
|
|
|
|
const renderMenuItem = (item: MenuItem, level: number = 0) => {
|
|
const isExpanded = expandedMenus.has(item.id);
|
|
const isSelected = selectedMenu === item.id;
|
|
const hasChildren = item.children && item.children.length > 0;
|
|
|
|
return (
|
|
<div key={item.id}>
|
|
<div
|
|
className={`flex cursor-pointer items-center rounded-md px-4 py-2 text-sm transition-colors ${
|
|
isSelected ? "bg-blue-600 text-white" : "text-slate-700 hover:bg-slate-100"
|
|
} ${level > 0 ? "ml-6" : ""}`}
|
|
onClick={() => {
|
|
if (hasChildren) {
|
|
toggleMenu(item.id);
|
|
} else {
|
|
handleMenuClick(item.id);
|
|
}
|
|
}}
|
|
>
|
|
<item.icon className="mr-3 h-4 w-4" />
|
|
<span className="flex-1">{item.title}</span>
|
|
{hasChildren && (isExpanded ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />)}
|
|
</div>
|
|
{hasChildren && isExpanded && (
|
|
<div className="mt-1">{item.children?.map((child) => renderMenuItem(child, level + 1))}</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-slate-50">
|
|
{/* 헤더 */}
|
|
<header className="border-b border-slate-200 bg-white shadow-sm">
|
|
<div className="px-6 py-4">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center space-x-4">
|
|
<Button variant="ghost" size="sm" onClick={() => setSidebarOpen(!sidebarOpen)}>
|
|
<Menu className="h-5 w-5" />
|
|
</Button>
|
|
<h1 className="text-xl font-semibold text-slate-900">PLM 시스템</h1>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="flex">
|
|
{/* 사이드바 */}
|
|
<aside
|
|
className={`${sidebarOpen ? "w-64" : "w-0"} overflow-hidden border-r border-slate-200 bg-white transition-all duration-300`}
|
|
>
|
|
<nav className="space-y-2 p-4">{menuItems.map((item) => renderMenuItem(item))}</nav>
|
|
</aside>
|
|
|
|
{/* 메인 컨텐츠 */}
|
|
<main className="flex-1 p-6">{renderContent()}</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|