Nanny7's picture
Phase 4: Infrastructure, Docker, Kubernetes, Chatbot with Qwen API
e566277
'use client';
import { useState } from 'react';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
import { cn } from '@/lib/utils';
import {
LayoutDashboard,
User,
LogOut,
LogIn,
Menu,
X,
} from 'lucide-react';
import { useAuth } from '@/hooks/use-auth';
import { Button } from '@/components/ui/button';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
interface NavItem {
href: string;
label: string;
icon: React.ComponentType<{ className?: string }>;
}
const navItems: NavItem[] = [
{ href: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ href: '/profile', label: 'Profile', icon: User },
];
export function Navbar() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const pathname = usePathname();
const router = useRouter();
const { user, logout, isAuthenticated } = useAuth();
const handleLogout = async () => {
await logout();
router.push('/login');
};
const handleLogin = () => {
router.push('/login');
};
// Get user initials for avatar
const getUserInitials = () => {
if (user?.name) {
return user.name
.split(' ')
.map((n: string) => n[0])
.join('')
.toUpperCase()
.slice(0, 2);
}
return user?.email?.[0].toUpperCase() || 'U';
};
return (
<nav className="sticky top-0 z-50 w-full border-b border-gray-200 dark:border-cyan-500/30 bg-white dark:bg-black/80 backdrop-blur-lg dark:shadow-[0_0_20px_rgba(6,182,212,0.2)]">
<div className="flex h-16 items-center px-4">
{/* Logo */}
<Link href="/dashboard" className="flex items-center space-x-2">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-primary dark:bg-gradient-to-br dark:from-cyan-500 dark:to-purple-500 text-primary-foreground dark:shadow-[0_0_15px_rgba(6,182,212,0.6)]">
<span className="text-lg font-bold"></span>
</div>
<span className="font-bold text-lg dark:text-white dark:drop-shadow-[0_0_8px_rgba(6,182,212,0.8)]">Todo App</span>
</Link>
{/* Desktop Navigation */}
<div className="hidden md:flex md:flex-1 md:items-center md:justify-end md:gap-1">
{navItems.map((item) => {
const Icon = item.icon;
const isActive = pathname === item.href;
return (
<Link
key={item.href}
href={item.href}
className={cn(
'flex items-center gap-2 px-4 py-2 rounded-lg transition-all',
isActive
? 'bg-primary text-primary-foreground font-medium dark:bg-gradient-to-r dark:from-cyan-500 dark:to-purple-500 dark:shadow-[0_0_15px_rgba(6,182,212,0.6)]'
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-cyan-500/10 dark:hover:shadow-[0_0_10px_rgba(6,182,212,0.3)]'
)}
>
<Icon className="h-4 w-4" />
<span>{item.label}</span>
</Link>
);
})}
</div>
{/* User Profile - Desktop */}
<div className="hidden md:flex md:items-center md:gap-3 md:ml-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-10 w-10 rounded-full">
<Avatar className="h-9 w-9">
<AvatarFallback className="bg-primary text-primary-foreground text-xs">
{getUserInitials()}
</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium leading-none">
{user?.name || 'User'}
</p>
<p className="text-xs leading-none text-muted-foreground">
{user?.email}
</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem
className="cursor-pointer text-red-600 focus:text-red-600"
onClick={handleLogout}
>
<LogOut className="mr-2 h-4 w-4" />
Logout
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
{/* Mobile Menu Button */}
<div className="flex flex-1 items-center justify-end md:hidden">
<Button
variant="ghost"
size="icon"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="h-10 w-10"
>
{isMobileMenuOpen ? (
<X className="h-5 w-5" />
) : (
<Menu className="h-5 w-5" />
)}
</Button>
</div>
</div>
{/* Mobile Menu */}
{isMobileMenuOpen && (
<div className="border-t border-gray-200 dark:border-gray-700 md:hidden">
<div className="space-y-1 px-4 py-3">
{navItems.map((item) => {
const Icon = item.icon;
const isActive = pathname === item.href;
return (
<Link
key={item.href}
href={item.href}
onClick={() => setIsMobileMenuOpen(false)}
className={cn(
'flex items-center gap-3 px-3 py-2 rounded-lg transition-colors',
isActive
? 'bg-primary text-primary-foreground font-medium'
: 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
)}
>
<Icon className="h-5 w-5" />
<span>{item.label}</span>
</Link>
);
})}
<div className="border-t border-gray-200 dark:border-gray-700 my-2 pt-2">
<div className="flex items-center gap-3 px-3 py-2 rounded-lg bg-gray-50 dark:bg-gray-700/50">
<Avatar className="h-9 w-9">
<AvatarFallback className="bg-primary text-primary-foreground text-xs">
{getUserInitials()}
</AvatarFallback>
</Avatar>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium truncate">
{user?.name || 'User'}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400 truncate">
{user?.email}
</p>
</div>
</div>
<button
onClick={handleLogout}
className="flex items-center gap-3 w-full px-3 py-2 mt-1 rounded-lg text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
>
<LogOut className="h-5 w-5" />
<span>Logout</span>
</button>
</div>
</div>
</div>
)}
</nav>
);
}