Replace legacy text-kodo-cyan/border-kodo-cyan/bg-kodo-cyan with semantic text-primary/border-primary/bg-primary across 51 components. The brand primary color now uses the design system token, enabling proper theme adaptation. Covers UI primitives, search, dashboard, chat, playlists, settings, social, marketplace, and auth components. Co-authored-by: Cursor <cursoragent@cursor.com>
261 lines
9.2 KiB
TypeScript
261 lines
9.2 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-background/80 backdrop-blur-md border-b border-border/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-primary flex items-center justify-center shadow-lg shadow-border/20">
|
|
<span className="font-display font-bold text-primary-foreground 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-foreground leading-none">
|
|
VEZA
|
|
</span>
|
|
<span className="text-xs text-muted-foreground 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-muted rounded text-xs text-muted-foreground 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-border/50 pr-6">
|
|
<div className="text-right">
|
|
<div className="text-xs text-foreground font-medium">
|
|
Pro Plan
|
|
</div>
|
|
<div className="text-xs text-muted-foreground">
|
|
Valid until Dec 31
|
|
</div>
|
|
</div>
|
|
<div className="w-8 h-8 rounded-full bg-muted flex items-center justify-center">
|
|
<Zap className="w-4 h-4 text-warning 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-primary 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-border p-px hover:ring-2 hover:ring-border 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-primary transition-transform duration-[var(--duration-fast)] ${showUserMenu ? 'rotate-180' : ''}`}
|
|
/>
|
|
</div>
|
|
|
|
{showUserMenu && (
|
|
<div className="absolute top-full right-0 mt-4 w-56 bg-card border border-border 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-border/30 mb-1 bg-muted/50">
|
|
<p className="text-sm font-bold text-white">Cyber_Producer</p>
|
|
<p className="text-xs text-muted-foreground">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-foreground hover:bg-muted hover:text-foreground 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-foreground hover:bg-muted hover:text-foreground gap-4"
|
|
>
|
|
<Zap className="w-4 h-4 text-destructive" /> Go Live
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => {
|
|
onNavigate('purchases');
|
|
setShowUserMenu(false);
|
|
}}
|
|
className="w-full justify-start px-4 py-2.5 text-sm text-foreground hover:bg-muted hover:text-foreground 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-foreground hover:bg-muted hover:text-foreground gap-4"
|
|
>
|
|
<Settings className="w-4 h-4" /> Settings
|
|
</Button>
|
|
<div className="h-px bg-border/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-destructive hover:bg-destructive/10 rounded-lg gap-4"
|
|
>
|
|
<LogOut className="w-4 h-4" /> Sign Out
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</>
|
|
);
|
|
};
|