Files
2025-12-06 16:52:10 +05:30

106 lines
3.5 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import {
LayoutDashboard,
Server,
Database,
Users,
BarChart3,
Bell,
Settings,
LogOut,
PanelTop,
FileSearch,
AlertTriangle,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { useAuthStore } from '@/stores/auth';
import { Button } from '@/components/ui/button';
import { Logo } from '@/components/logo';
const navigation = [
{ name: 'Overview', href: '/overview', icon: LayoutDashboard },
{ name: 'Clusters', href: '/clusters', icon: Server },
{ name: 'Streams', href: '/streams', icon: Database },
{ name: 'Consumers', href: '/consumers', icon: Users },
{ name: 'Dead Letter Queues', href: '/dlq', icon: AlertTriangle },
{ name: 'Analytics', href: '/analytics', icon: BarChart3 },
{ name: 'Dashboards', href: '/dashboards', icon: PanelTop },
{ name: 'Saved Queries', href: '/saved-queries', icon: FileSearch },
{ name: 'Alerts', href: '/alerts', icon: Bell },
{ name: 'Settings', href: '/settings', icon: Settings },
];
export function Sidebar() {
const pathname = usePathname();
const { logout, user } = useAuthStore();
// Optimistic navigation state for instant feedback
const [optimisticPath, setOptimisticPath] = useState<string | null>(null);
// Sync optimistic state with actual pathname when navigation completes
useEffect(() => {
setOptimisticPath(null);
}, [pathname]);
// Use optimistic path if set, otherwise use actual pathname
const activePath = optimisticPath || pathname;
return (
<div className="flex h-full w-52 flex-col border-r bg-sidebar">
{/* Logo */}
<div className="flex h-14 items-center border-b px-4">
<Logo href="/clusters" size="sm" />
</div>
{/* Navigation */}
<nav className="flex-1 space-y-1 p-3">
{navigation.map((item) => {
const isActive = activePath === item.href || (item.href !== '/overview' && activePath.startsWith(item.href));
return (
<Link
key={item.name}
href={item.href}
onClick={() => setOptimisticPath(item.href)}
className={cn(
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
isActive
? 'bg-primary text-primary-foreground'
: 'text-foreground/70 hover:bg-muted hover:text-foreground'
)}
>
<item.icon className="h-4 w-4" />
{item.name}
</Link>
);
})}
</nav>
{/* User section */}
<div className="border-t p-3">
<div className="flex items-center gap-2 mb-2">
<div className="h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center">
<span className="text-xs font-medium">
{user?.firstName?.[0]}
{user?.lastName?.[0]}
</span>
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium truncate">
{user?.firstName} {user?.lastName}
</p>
<p className="text-xs text-muted-foreground truncate">{user?.email}</p>
</div>
</div>
<Button variant="ghost" size="sm" className="w-full justify-start gap-2 h-8" onClick={() => logout()}>
<LogOut className="h-3.5 w-3.5" />
Sign out
</Button>
</div>
</div>
);
}