veza/apps/web/src/components/layout/Navbar.tsx

261 lines
9.3 KiB
TypeScript

import React, { useState } from 'react';
import {
Menu,
Palette,
Zap,
ChevronDown,
LogOut,
Settings,
User,
ShoppingCart,
Search,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useCartStore } from '../../stores/cartStore';
import { useTheme } from '../theme/ThemeProvider';
import { Notification } from '../../types';
import { NotificationBell } from '../notifications/NotificationBell';
interface NavbarProps {
onNavigate: (viewId: string) => void;
onLogout: () => void;
}
const mockNotifications: Notification[] = [
{
id: '1',
type: 'info',
title: 'System Update',
message: 'System Update v2.0 Live',
timestamp: '2m',
read: false,
},
{
id: '2',
type: 'like',
title: 'New Like',
message: 'Neon_Dev liked your track',
timestamp: '15m',
read: false,
actionUrl: '/track/1',
},
{
id: '3',
type: 'follow',
title: 'New Follower',
message: 'Skrillex started following you',
timestamp: '1h',
read: true,
actionUrl: '/u/skrillex',
},
];
export const Navbar: React.FC<NavbarProps> = ({ onNavigate, onLogout }) => {
const { theme, setTheme } = useTheme();
const toggleTheme = () => {
setTheme(theme === 'dark' ? 'light' : 'dark');
};
// Selector ensures we re-render only when the calculated count changes
const itemCount = useCartStore((state) => state.getItemCount());
const [showUserMenu, setShowUserMenu] = useState(false);
const [notifications, setNotifications] =
useState<Notification[]>(mockNotifications);
const handleMarkAllRead = () => {
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
};
const handleRead = (id: string) => {
setNotifications((prev) =>
prev.map((n) => (n.id === id ? { ...n, read: true } : n)),
);
};
return (
<>
{/* Backdrop for closing menus - Lower z-index than Navbar (z-40) but higher than content */}
{showUserMenu && (
<div
className="fixed inset-0 z-[35] bg-transparent cursor-default"
onClick={() => setShowUserMenu(false)}
/>
)}
<nav
role="navigation"
aria-label="Main Navigation"
className="fixed top-0 left-0 right-0 h-16 bg-kodo-void/80 backdrop-blur-md border-b border-kodo-steel/40 z-40 flex items-center justify-between px-6 lg:px-8"
>
{/* Brand & Mobile Menu */}
<div className="flex items-center gap-4">
<Button variant="ghost" size="sm" className="lg:hidden p-1">
<Menu className="w-5 h-5" />
</Button>
<div
className="flex items-center gap-4 cursor-pointer"
onClick={() => onNavigate('dashboard')}
>
<div className="w-8 h-8 rounded-full bg-kodo-cyan flex items-center justify-center shadow-lg shadow-kodo-steel/20">
<span className="font-display font-bold text-kodo-void text-lg">
V
</span>
</div>
<div className="hidden sm:flex flex-col justify-center">
<span className="font-display font-bold text-base tracking-wide text-kodo-primary leading-none">
VEZA
</span>
<span className="text-[10px] text-kodo-secondary font-medium tracking-widest uppercase leading-none mt-1">
Spectre Astral
</span>
</div>
</div>
</div>
{/* Center Search (Hidden on Mobile) */}
<div className="hidden md:flex flex-1 max-w-md mx-8 relative">
<Input
placeholder="Search platform..."
icon={<Search className="w-4 h-4" />}
/>
<div className="absolute right-0 top-1/2 -translate-y-1/2 pr-3 flex gap-2">
<span className="px-1.5 py-0.5 bg-kodo-steel/50 rounded text-[10px] text-kodo-secondary font-mono border border-white/5">
CMD+K
</span>
</div>
</div>
{/* Right Actions */}
<div className="flex items-center gap-4 md:gap-6">
{/* Pro Badge */}
<div className="hidden xl:flex items-center gap-4 border-r border-kodo-steel/50 pr-6">
<div className="text-right">
<div className="text-xs text-kodo-primary font-medium">
Pro Plan
</div>
<div className="text-[10px] text-kodo-secondary">
Valid until Dec 31
</div>
</div>
<div className="w-8 h-8 rounded-full bg-kodo-slate flex items-center justify-center">
<Zap className="w-4 h-4 text-kodo-gold fill-current" />
</div>
</div>
<Button
variant="ghost"
size="sm"
onClick={toggleTheme}
title={`Theme: ${theme}`}
className="hidden sm:flex"
>
<Palette className="w-5 h-5" />
</Button>
{/* Cart Trigger */}
<Button
variant="ghost"
size="sm"
className="relative hidden sm:flex"
onClick={() => onNavigate('cart')}
>
<ShoppingCart className="w-5 h-5" />
{itemCount > 0 && (
<span className="absolute top-1.5 right-1.5 w-2 h-2 bg-kodo-cyan rounded-full border border-kodo-void"></span>
)}
</Button>
{/* Notification Center */}
<NotificationBell
notifications={notifications}
onMarkAllRead={handleMarkAllRead}
onRead={handleRead}
onViewAll={() => onNavigate('notifications')}
/>
{/* User Menu */}
<div className="relative z-50">
<div
className="flex items-center gap-2 cursor-pointer group select-none"
onClick={() => setShowUserMenu(!showUserMenu)}
>
<div className="w-9 h-9 rounded-full bg-kodo-steel p-px hover:ring-2 hover:ring-kodo-steel transition-all">
<div className="w-full h-full rounded-full overflow-hidden">
<img
src="https://picsum.photos/100/100"
alt="Avatar"
className="w-full h-full object-cover"
/>
</div>
</div>
<ChevronDown
className={`w-4 h-4 text-white group-hover:text-kodo-primary transition-transform duration-200 ${showUserMenu ? 'rotate-180' : ''}`}
/>
</div>
{showUserMenu && (
<div className="absolute top-full right-0 mt-4 w-56 bg-kodo-graphite border border-kodo-steel rounded-xl shadow-2xl overflow-hidden animate-fadeIn origin-top-right ring-1 ring-white/5 flex flex-col">
<div className="px-4 py-4 border-b border-kodo-steel/30 mb-1 bg-kodo-ink/50">
<p className="text-sm font-bold text-white">Cyber_Producer</p>
<p className="text-xs text-kodo-content-dim">Pro Plan</p>
</div>
<Button
variant="ghost"
onClick={() => {
onNavigate('profile');
setShowUserMenu(false);
}}
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white gap-4"
>
<User className="w-4 h-4" /> My Profile
</Button>
<Button
variant="ghost"
onClick={() => {
onNavigate('studio/go-live');
setShowUserMenu(false);
}}
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white gap-4"
>
<Zap className="w-4 h-4 text-kodo-red" /> Go Live
</Button>
<Button
variant="ghost"
onClick={() => {
onNavigate('purchases');
setShowUserMenu(false);
}}
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white gap-4"
>
<ShoppingCart className="w-4 h-4" /> Purchases
</Button>
<Button
variant="ghost"
onClick={() => {
onNavigate('settings');
setShowUserMenu(false);
}}
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-text-main hover:bg-white/5 hover:text-white gap-4"
>
<Settings className="w-4 h-4" /> Settings
</Button>
<div className="h-px bg-kodo-steel/30 my-1 mx-2"></div>
<Button
variant="ghost"
onClick={() => {
onLogout();
setShowUserMenu(false);
}}
className="w-full justify-start px-4 py-2.5 text-sm text-kodo-red hover:bg-kodo-red/10 rounded-lg gap-4"
>
<LogOut className="w-4 h-4" /> Sign Out
</Button>
</div>
)}
</div>
</div>
</nav>
</>
);
};