guardia-fa/frontend/src/App.tsx

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>
)
}