- Consolidated multi-line arguments into single lines where appropriate in `useAuth`. - Improved spacing and readability in data processing across components (`ProfileSettingsForm`, `PasswordChangeForm`, `SessionCard`). - Applied consistent table and markdown formatting in design system docs (e.g., `README.md`, `08-ai-guidelines.md`, `00-quick-start.md`). - Updated code snippets to ensure adherence to Prettier rules and streamlined JSX structures.
130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
/**
|
|
* Admin Sidebar Navigation
|
|
* Displays navigation links for admin section
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { cn } from '@/lib/utils';
|
|
import {
|
|
LayoutDashboard,
|
|
Users,
|
|
Building2,
|
|
Settings,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
} from 'lucide-react';
|
|
import { useState } from 'react';
|
|
import { useAuth } from '@/lib/auth/AuthContext';
|
|
|
|
interface NavItem {
|
|
name: string;
|
|
href: string;
|
|
icon: React.ComponentType<{ className?: string }>;
|
|
}
|
|
|
|
const navItems: NavItem[] = [
|
|
{
|
|
name: 'Dashboard',
|
|
href: '/admin',
|
|
icon: LayoutDashboard,
|
|
},
|
|
{
|
|
name: 'Users',
|
|
href: '/admin/users',
|
|
icon: Users,
|
|
},
|
|
{
|
|
name: 'Organizations',
|
|
href: '/admin/organizations',
|
|
icon: Building2,
|
|
},
|
|
{
|
|
name: 'Settings',
|
|
href: '/admin/settings',
|
|
icon: Settings,
|
|
},
|
|
];
|
|
|
|
export function AdminSidebar() {
|
|
const pathname = usePathname();
|
|
const { user } = useAuth();
|
|
const [collapsed, setCollapsed] = useState(false);
|
|
|
|
return (
|
|
<aside
|
|
className={cn(
|
|
'border-r bg-muted/40 transition-all duration-300',
|
|
collapsed ? 'w-16' : 'w-64'
|
|
)}
|
|
data-testid="admin-sidebar"
|
|
>
|
|
<div className="flex h-full flex-col">
|
|
{/* Sidebar Header */}
|
|
<div className="flex h-16 items-center justify-between border-b px-4">
|
|
{!collapsed && <h2 className="text-lg font-semibold">Admin Panel</h2>}
|
|
<button
|
|
onClick={() => setCollapsed(!collapsed)}
|
|
className="rounded-md p-2 hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
|
aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
data-testid="sidebar-toggle"
|
|
>
|
|
{collapsed ? (
|
|
<ChevronRight className="h-4 w-4" aria-hidden="true" />
|
|
) : (
|
|
<ChevronLeft className="h-4 w-4" aria-hidden="true" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Navigation Links */}
|
|
<nav className="flex-1 space-y-1 p-2">
|
|
{navItems.map((item) => {
|
|
const isActive =
|
|
pathname === item.href || (item.href !== '/admin' && pathname.startsWith(item.href));
|
|
const Icon = item.icon;
|
|
|
|
return (
|
|
<Link
|
|
key={item.href}
|
|
href={item.href}
|
|
className={cn(
|
|
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
|
|
'hover:bg-accent hover:text-accent-foreground',
|
|
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
isActive ? 'bg-accent text-accent-foreground' : 'text-muted-foreground',
|
|
collapsed && 'justify-center'
|
|
)}
|
|
title={collapsed ? item.name : undefined}
|
|
data-testid={`nav-${item.name.toLowerCase()}`}
|
|
>
|
|
<Icon className="h-5 w-5 flex-shrink-0" aria-hidden="true" />
|
|
{!collapsed && <span>{item.name}</span>}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* User Info */}
|
|
{!collapsed && user && (
|
|
<div className="border-t p-4">
|
|
<div className="flex items-center gap-3">
|
|
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary text-primary-foreground text-sm font-medium">
|
|
{user.first_name?.[0] || user.email[0].toUpperCase()}
|
|
</div>
|
|
<div className="flex-1 overflow-hidden">
|
|
<p className="text-sm font-medium truncate">
|
|
{user.first_name} {user.last_name}
|
|
</p>
|
|
<p className="text-xs text-muted-foreground truncate">{user.email}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|