[CLAUDE] FE: TopBar + NotificationBell + UserMenu — ERP shell foundation
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
Kiến trúc Layout giờ tách thành [sidebar] [topbar + content], foundation để scale thêm module trong tương lai (HR, Accounting, Inventory...). TopBar: title placeholder + NotificationBell + UserMenu (initials avatar + role badges + logout). UserMenu thay cho bottom-of-sidebar (cleaner). NotificationBell: - fe-admin: cảnh báo SLA (HĐ quá/sắp quá hạn, 24h window) - fe-user: hộp thư chờ xử lý (items trong /contracts/inbox) - refetchInterval: 60s - Placeholder cho SignalR/email notifications thật sẽ thay bằng /api/notifications endpoint ở Tier 3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -1,15 +1,8 @@
|
||||
import { Link, NavLink, Outlet } from 'react-router-dom'
|
||||
import { LogOut, Circle, Inbox, FileText, Plus, type LucideIcon } from 'lucide-react'
|
||||
import * as Icons from 'lucide-react'
|
||||
import { useAuth } from '@/contexts/AuthContext'
|
||||
import { Inbox, FileText, Plus } from 'lucide-react'
|
||||
import { TopBar } from '@/components/TopBar'
|
||||
import { cn } from '@/lib/cn'
|
||||
|
||||
function getIcon(name: string | null): LucideIcon {
|
||||
if (!name) return Circle
|
||||
const cand = (Icons as unknown as Record<string, LucideIcon>)[name]
|
||||
return cand ?? Circle
|
||||
}
|
||||
|
||||
// Menu fixed cho fe-user (không show tree động vì user-flow đơn giản)
|
||||
const USER_MENU = [
|
||||
{ to: '/inbox', label: 'HĐ chờ xử lý', icon: Inbox },
|
||||
@ -18,8 +11,6 @@ const USER_MENU = [
|
||||
]
|
||||
|
||||
export function Layout() {
|
||||
const { user, logout } = useAuth()
|
||||
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
<aside className="flex w-64 flex-col border-r border-slate-200 bg-white">
|
||||
@ -30,7 +21,7 @@ export function Layout() {
|
||||
</div>
|
||||
<nav className="flex-1 space-y-1 p-3">
|
||||
{USER_MENU.map(item => {
|
||||
const Icon = item.icon ?? getIcon(null)
|
||||
const Icon = item.icon
|
||||
return (
|
||||
<NavLink
|
||||
key={item.to}
|
||||
@ -48,26 +39,13 @@ export function Layout() {
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
<div className="border-t border-slate-200 p-3">
|
||||
<div className="mb-2 px-3 text-xs text-slate-500">
|
||||
<div className="truncate font-medium text-slate-700">{user?.fullName}</div>
|
||||
<div className="truncate">{user?.email}</div>
|
||||
{user && user.roles.length > 0 && (
|
||||
<div className="mt-1 font-mono text-[10px]">{user.roles.join(', ')}</div>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={logout}
|
||||
className="flex w-full items-center gap-2 rounded-md px-3 py-2 text-sm text-slate-600 transition hover:bg-slate-100"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
Đăng xuất
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
<main className="flex-1 overflow-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
<div className="flex flex-1 flex-col overflow-hidden">
|
||||
<TopBar />
|
||||
<main className="flex-1 overflow-auto">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user