112 lines
4.2 KiB
TypeScript
112 lines
4.2 KiB
TypeScript
import React, { useState } from 'react'
|
|
import { BrowserRouter, Routes, Route, Navigate, NavLink } from 'react-router-dom'
|
|
import {
|
|
LayoutDashboard, Map, Monitor, QrCode, Package,
|
|
AlertTriangle, ShieldCheck, Wrench, Boxes, Brain, LogOut
|
|
} from 'lucide-react'
|
|
import Login from './pages/Login'
|
|
import Dashboard from './pages/Dashboard'
|
|
import FloorMap from './pages/FloorMap'
|
|
import EpaperManagement from './pages/EpaperManagement'
|
|
import QrTracking from './pages/QrTracking'
|
|
import ProductionOrders from './pages/ProductionOrders'
|
|
import AndonBoard from './pages/AndonBoard'
|
|
import QualityControl from './pages/QualityControl'
|
|
import EquipmentStatus from './pages/EquipmentStatus'
|
|
import AiFactory from './pages/AiFactory'
|
|
|
|
const NAV_ITEMS = [
|
|
{ to: '/dashboard', icon: LayoutDashboard, label: '대시보드' },
|
|
{ to: '/floor-map', icon: Map, label: '공장 배치도' },
|
|
{ to: '/epaper', icon: Monitor, label: 'e-Paper 관리' },
|
|
{ to: '/qr', icon: QrCode, label: 'QR WIP 추적' },
|
|
{ to: '/orders', icon: Package, label: '생산 오더' },
|
|
{ to: '/andon', icon: AlertTriangle, label: '안돈 현황판' },
|
|
{ to: '/quality', icon: ShieldCheck, label: '품질 관리' },
|
|
{ to: '/equipment', icon: Wrench, label: '설비 현황' },
|
|
{ to: '/inventory', icon: Boxes, label: '재고 관리' },
|
|
{ to: '/ai', icon: Brain, label: 'AI 공장 분석' },
|
|
]
|
|
|
|
function Sidebar() {
|
|
const user = JSON.parse(localStorage.getItem('fa_user') || '{}')
|
|
return (
|
|
<aside className="w-60 bg-slate-900 border-r border-slate-700 flex flex-col h-screen fixed">
|
|
<div className="p-4 border-b border-slate-700">
|
|
<div className="text-blue-400 font-bold text-lg">GUARDiA FA</div>
|
|
<div className="text-slate-400 text-xs mt-1">Factory Automation Platform</div>
|
|
</div>
|
|
<nav className="flex-1 overflow-y-auto py-2">
|
|
{NAV_ITEMS.map(({ to, icon: Icon, label }) => (
|
|
<NavLink
|
|
key={to}
|
|
to={to}
|
|
className={({ isActive }) =>
|
|
`flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${
|
|
isActive
|
|
? 'bg-blue-600 text-white'
|
|
: 'text-slate-300 hover:bg-slate-800 hover:text-white'
|
|
}`
|
|
}
|
|
>
|
|
<Icon size={16} />
|
|
{label}
|
|
</NavLink>
|
|
))}
|
|
</nav>
|
|
<div className="p-4 border-t border-slate-700">
|
|
<div className="text-xs text-slate-400 mb-2">{user.username} ({user.role})</div>
|
|
<button
|
|
onClick={() => { localStorage.clear(); window.location.href = '/login' }}
|
|
className="flex items-center gap-2 text-xs text-slate-400 hover:text-red-400 transition-colors"
|
|
>
|
|
<LogOut size={14} /> 로그아웃
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
)
|
|
}
|
|
|
|
function RequireAuth({ children }: { children: React.ReactNode }) {
|
|
const token = localStorage.getItem('fa_token')
|
|
return token ? <>{children}</> : <Navigate to="/login" replace />
|
|
}
|
|
|
|
function Layout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<div className="flex">
|
|
<Sidebar />
|
|
<main className="ml-60 flex-1 min-h-screen p-6">{children}</main>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<BrowserRouter>
|
|
<Routes>
|
|
<Route path="/login" element={<Login />} />
|
|
<Route path="/*" element={
|
|
<RequireAuth>
|
|
<Layout>
|
|
<Routes>
|
|
<Route path="/" element={<Navigate to="/dashboard" replace />} />
|
|
<Route path="/dashboard" element={<Dashboard />} />
|
|
<Route path="/floor-map" element={<FloorMap />} />
|
|
<Route path="/epaper" element={<EpaperManagement />} />
|
|
<Route path="/qr" element={<QrTracking />} />
|
|
<Route path="/orders" element={<ProductionOrders />} />
|
|
<Route path="/andon" element={<AndonBoard />} />
|
|
<Route path="/quality" element={<QualityControl />} />
|
|
<Route path="/equipment" element={<EquipmentStatus />} />
|
|
<Route path="/inventory" element={<AiFactory />} />
|
|
<Route path="/ai" element={<AiFactory />} />
|
|
</Routes>
|
|
</Layout>
|
|
</RequireAuth>
|
|
} />
|
|
</Routes>
|
|
</BrowserRouter>
|
|
)
|
|
}
|