Files
aars/frontend/src/components/Layout.jsx
T
IT狗 eafc1c33fc Add full AARS system - Attendance & Accident Record System
- FastAPI backend with SQLite database
- React + TailwindCSS frontend
- JWT authentication
- Excel upload/download for attendance and accident records
- Shift roster management with S7a/S7b split
- Lateness/OT calculation based on shift times
- Roster info API endpoints
- Export API with token query parameter support
- Docker compose deployment
2026-07-16 03:16:33 +08:00

128 lines
4.8 KiB
React

import { Outlet, NavLink, useNavigate } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
import { LogOut, Users, AlertTriangle, LayoutDashboard, Upload, Calendar } from 'lucide-react'
export default function Layout() {
const { user, logout } = useAuth()
const navigate = useNavigate()
const handleLogout = () => {
logout()
navigate('/login')
}
return (
<div className="min-h-screen bg-slate-50">
{/* Header */}
<header className="bg-white border-b border-slate-200 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<div className="flex items-center gap-8">
<div className="flex items-center gap-2">
<div className="w-8 h-8 bg-primary-600 rounded-lg flex items-center justify-center">
<span className="text-white font-bold text-sm">A</span>
</div>
<span className="text-xl font-bold text-slate-900">AARS</span>
</div>
{/* Navigation */}
<nav className="hidden md:flex items-center gap-1">
<NavLink
to="/"
end
className={({ isActive }) =>
`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
isActive
? 'bg-primary-50 text-primary-700'
: 'text-slate-600 hover:bg-slate-100'
}`
}
>
<LayoutDashboard size={18} />
Dashboard
</NavLink>
<NavLink
to="/attendance"
className={({ isActive }) =>
`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
isActive
? 'bg-primary-50 text-primary-700'
: 'text-slate-600 hover:bg-slate-100'
}`
}
>
<Users size={18} />
出勤 Attendance
</NavLink>
<NavLink
to="/accident"
className={({ isActive }) =>
`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
isActive
? 'bg-primary-50 text-primary-700'
: 'text-slate-600 hover:bg-slate-100'
}`
}
>
<AlertTriangle size={18} />
意外 Accident
</NavLink>
<NavLink
to="/roster"
className={({ isActive }) =>
`flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-colors ${
isActive
? 'bg-primary-50 text-primary-700'
: 'text-slate-600 hover:bg-slate-100'
}`
}
>
<Calendar size={18} />
班次 Roster
</NavLink>
</nav>
</div>
{/* Right side */}
<div className="flex items-center gap-4">
<NavLink
to="/upload"
className={({ isActive }) =>
`flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
isActive
? 'bg-primary-50 text-primary-700'
: 'text-slate-600 hover:bg-slate-100'
}`
}
>
<Upload size={16} />
<span className="hidden sm:inline">上傳</span>
</NavLink>
<div className="flex items-center gap-3 pl-4 border-l border-slate-200">
<div className="text-right hidden sm:block">
<div className="text-sm font-medium text-slate-900">{user?.name}</div>
<div className="text-xs text-slate-500">{user?.role}</div>
</div>
<button
onClick={handleLogout}
className="p-2 text-slate-400 hover:text-slate-600 hover:bg-slate-100 rounded-md transition-colors"
title="Logout"
>
<LogOut size={18} />
</button>
</div>
</div>
</div>
</div>
</header>
{/* Main content */}
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<Outlet />
</main>
</div>
)
}