feat: RBAC controlling

This commit is contained in:
2026-03-10 17:59:44 +08:00
parent f803a447b5
commit b9c0f3025c
11 changed files with 716 additions and 380 deletions

View File

@@ -5,16 +5,17 @@ import { usePathname } from 'next/navigation'
import { useState } from 'react'
import { CreditCard, ListCheck, Person, PlugConnection, Thunderbolt, Xmark, Bars } from '@gravity-ui/icons'
import SidebarFooter from '@/components/sidebar-footer'
import { useSession } from '@/lib/auth-client'
const navItems = [
{ href: '/dashboard', label: '概览', icon: Thunderbolt, exact: true },
{ href: '/dashboard/charge-points', label: '充电桩', icon: PlugConnection },
{ href: '/dashboard/transactions', label: '充电记录', icon: ListCheck },
{ href: '/dashboard/id-tags', label: '储值卡', icon: CreditCard },
{ href: '/dashboard/users', label: '用户管理', icon: Person },
{ href: '/dashboard', label: '概览', icon: Thunderbolt, exact: true, adminOnly: false },
{ href: '/dashboard/charge-points', label: '充电桩', icon: PlugConnection, adminOnly: false },
{ href: '/dashboard/transactions', label: '充电记录', icon: ListCheck, adminOnly: false },
{ href: '/dashboard/id-tags', label: '储值卡', icon: CreditCard, adminOnly: false },
{ href: '/dashboard/users', label: '用户管理', icon: Person, adminOnly: true },
]
function NavContent({ pathname, onNavigate }: { pathname: string; onNavigate?: () => void }) {
function NavContent({ pathname, isAdmin, onNavigate }: { pathname: string; isAdmin: boolean; onNavigate?: () => void }) {
return (
<>
{/* Logo */}
@@ -32,7 +33,7 @@ function NavContent({ pathname, onNavigate }: { pathname: string; onNavigate?: (
<p className="mb-1 px-2 text-[11px] font-semibold uppercase tracking-widest text-muted">
</p>
{navItems.map((item) => {
{navItems.filter((item) => !item.adminOnly || isAdmin).map((item) => {
const isActive = item.exact
? pathname === item.href
: pathname === item.href || pathname.startsWith(item.href + '/')
@@ -68,6 +69,8 @@ function NavContent({ pathname, onNavigate }: { pathname: string; onNavigate?: (
export default function Sidebar() {
const pathname = usePathname()
const [open, setOpen] = useState(false)
const { data: sessionData } = useSession()
const isAdmin = sessionData?.user?.role === "admin"
return (
<>
@@ -112,12 +115,12 @@ export default function Sidebar() {
>
<Xmark className="size-4" />
</button>
<NavContent pathname={pathname} onNavigate={() => setOpen(false)} />
<NavContent pathname={pathname} isAdmin={isAdmin} onNavigate={() => setOpen(false)} />
</aside>
{/* Desktop sidebar */}
<aside className="hidden w-60 shrink-0 flex-col border-r border-border bg-surface-secondary lg:flex">
<NavContent pathname={pathname} />
<NavContent pathname={pathname} isAdmin={isAdmin} />
</aside>
</>
)