veza/apps/web/src/components/layout/Header.tsx
senke 0eca0729b5 feat: Visual masterpiece - true light mode & premium UI
🎨 **True Light/Dark Mode**
- Implemented proper light mode with inverted color scheme
- Smooth theme transitions (0.3s ease)
- Light mode colors: white backgrounds, dark text, vibrant accents
- System theme detection with proper class application

🌈 **Enhanced Theme System**
- 4 color themes work in both light and dark modes
- Cyber (cyan/magenta), Ocean (blue/teal), Forest (green/lime), Sunset (orange/purple)
- Theme-specific glassmorphism effects
- Proper contrast in light mode

 **Premium Animations**
- Float, glow-pulse, slide-in, scale-in, rotate-in animations
- Smooth page transitions
- Hover effects with depth (lift, glow, scale)
- Micro-interactions on all interactive elements

🎯 **Visual Polish**
- Enhanced glassmorphism for light/dark modes
- Custom scrollbar with theme colors
- Beautiful text selection
- Focus indicators for accessibility
- Premium utility classes

🔧 **Technical Improvements**
- Updated UIStore to properly apply light/dark classes
- Added data-theme attribute for CSS targeting
- Smooth scroll behavior
- Optimized transitions

The app is now a visual masterpiece with perfect light/dark mode support!
2026-01-11 02:32:21 +01:00

182 lines
8.3 KiB
TypeScript

import { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useAuthStore } from '@/features/auth/store/authStore';
import { useUIStore } from '@/stores/ui';
import { useTranslation } from '@/hooks/useTranslation';
import { EmailVerificationBadge } from '@/features/auth/components/EmailVerificationBadge';
import { NotificationMenu } from '@/components/notifications/NotificationMenu';
import { GlobalSearchBar } from '@/components/search/GlobalSearchBar';
import { Button } from '@/components/ui/button';
import { FocusTrap } from '@/components/ui/focus-trap';
import { Tooltip } from '@/components/ui/tooltip';
import { cn } from '@/lib/utils';
import {
User,
Settings,
LogOut,
Moon,
Sun,
Monitor,
Search,
Cpu,
} from 'lucide-react';
import type { BaseComponentProps } from '../types';
/**
* Props for Header component
* FE-TYPE-013: Fully typed component props
*/
export interface HeaderProps extends BaseComponentProps {
// No additional props needed - uses global stores
}
export function Header(_props: HeaderProps) {
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
const { user, logout } = useAuthStore();
const { theme, setTheme } = useUIStore();
const { t } = useTranslation();
const navigate = useNavigate();
const handleLogout = async () => {
await logout();
navigate('/login');
};
const toggleTheme = () => {
const newTheme =
theme === 'light' ? 'dark' : theme === 'dark' ? 'system' : 'light';
setTheme(newTheme);
};
const getThemeIcon = () => {
switch (theme) {
case 'light':
return <Sun className="h-4 w-4" />;
case 'dark':
return <Moon className="h-4 w-4" />;
default:
return <Monitor className="h-4 w-4" />;
}
};
return (
<header className="fixed top-0 left-0 right-0 h-16 px-6 mt-4 pointer-events-none" style={{ zIndex: 'var(--z-fixed)' }}>
<div className="max-w-[1700px] mx-auto w-full h-full glass-hud rounded-2xl border-white/10 flex items-center justify-between px-6 pointer-events-auto hud-corner shadow-neon-cyan/5 group">
<div className="flex items-center gap-6">
<Link to="/dashboard" className="flex items-center gap-3 active:scale-95 transition-transform">
<div className="w-10 h-10 bg-kodo-cyan/20 rounded-xl flex items-center justify-center border border-kodo-cyan/30 shadow-neon-cyan/20 animate-scan">
<Cpu className="w-6 h-6 text-kodo-cyan animate-pulse-glow" />
</div>
<div className="hidden sm:block">
<h1 className="text-lg font-display font-bold text-white tracking-widest uppercase leading-tight">
Veza<span className="text-kodo-cyan">OS</span>
</h1>
<div className="text-[9px] font-mono text-kodo-cyan/50 tracking-[0.2em] uppercase -mt-0.5">
Core Network v2.4
</div>
</div>
</Link>
{/* HUD System Status */}
<div className="hidden xl:flex items-center gap-6 border-l border-white/10 pl-8 h-8">
<div className="flex flex-col">
<span className="text-hud">Uplink Status</span>
<div className="flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-kodo-lime animate-pulse" />
<span className="text-[11px] font-mono text-white opacity-90 uppercase tracking-tighter">Connected</span>
</div>
</div>
<div className="flex flex-col">
<span className="text-hud">Node ID</span>
<span className="text-[11px] font-mono text-kodo-cyan opacity-90 uppercase truncate max-w-[80px]">VZ-{user?.id?.slice(0, 4)}</span>
</div>
</div>
</div>
{/* Global Search integrated */}
<div className="flex-1 max-w-lg mx-8 relative hidden md:block">
<GlobalSearchBar className="w-full bg-transparent border-none" />
<div className="absolute left-3 top-1/2 -translate-y-1/2 flex items-center gap-2 pointer-events-none opacity-50 group-hover:opacity-100 transition-opacity">
<Search className="w-4 h-4 text-kodo-cyan" />
</div>
</div>
<div className="flex items-center gap-3">
{/* Quick Stats Overlay - Styled specifically */}
<div className="hidden lg:flex items-center gap-1 mr-4 bg-kodo-cyan/5 border border-kodo-cyan/10 rounded-full px-3 py-1.5">
<div className="w-1.5 h-1.5 rounded-full bg-kodo-lime" />
<span className="text-[10px] font-mono text-kodo-cyan uppercase font-bold tracking-tight">Active_Stream_OK</span>
</div>
<NotificationMenu />
<Tooltip content={t('common.changeTheme')}>
<Button
variant="ghost"
size="icon"
onClick={toggleTheme}
className="hover:bg-white/5 hover:text-kodo-cyan transition-all rounded-xl border border-transparent hover:border-white/5"
>
{getThemeIcon()}
</Button>
</Tooltip>
<div className="w-px h-6 bg-white/10 mx-1" />
{/* User Profile HUD */}
<div className="relative">
<Button
variant="ghost"
size="sm"
onClick={() => setIsUserMenuOpen(!isUserMenuOpen)}
className={cn(
"p-1 pr-3 rounded-xl gap-2 transition-all border border-transparent",
isUserMenuOpen ? "bg-kodo-cyan/10 border-kodo-cyan/30 text-kodo-cyan" : "hover:bg-white/5"
)}
>
<div className="w-8 h-8 rounded-lg bg-kodo-graphite border border-white/10 flex items-center justify-center overflow-hidden">
<User className="w-5 h-5 text-kodo-secondary" />
</div>
<div className="flex flex-col items-start hidden sm:flex">
<span className="text-xs font-bold text-white leading-none">{user?.username}</span>
<span className="text-[9px] font-mono text-kodo-secondary uppercase tracking-tighter">Lvl 1 Operative</span>
</div>
</Button>
{isUserMenuOpen && (
<FocusTrap active={isUserMenuOpen} onEscape={() => setIsUserMenuOpen(false)}>
<div className="absolute right-0 mt-3 w-56 glass-hud rounded-2xl border-white/10 shadow-2xl z-50 p-2 animate-scaleIn hud-corner">
<div className="px-4 py-3 border-b border-white/5 mb-2">
<div className="text-xs font-mono text-kodo-cyan opacity-50 uppercase tracking-widest mb-1">User_Registry</div>
<div className="text-sm font-bold text-white truncate">{user?.email}</div>
{user && !user.is_verified && (
<div className="mt-2">
<EmailVerificationBadge verified={false} />
</div>
)}
</div>
<div className="space-y-1">
<Link to="/profile" onClick={() => setIsUserMenuOpen(false)} className="flex items-center gap-3 px-3 py-2 text-sm text-kodo-secondary hover:text-kodo-cyan hover:bg-kodo-cyan/5 rounded-xl transition-all group">
<User className="w-4 h-4 group-hover:drop-shadow-[0_0_5px_rgba(102,252,241,0.5)]" />
<span>System.Profile</span>
</Link>
<Link to="/settings" onClick={() => setIsUserMenuOpen(false)} className="flex items-center gap-3 px-3 py-2 text-sm text-kodo-secondary hover:text-kodo-cyan hover:bg-kodo-cyan/5 rounded-xl transition-all group">
<Settings className="w-4 h-4 group-hover:rotate-45 transition-transform" />
<span>Global.Settings</span>
</Link>
<div className="h-px bg-white/5 my-2 mx-2" />
<button onClick={handleLogout} className="flex items-center gap-3 w-full px-3 py-2 text-sm text-red-400/80 hover:text-red-400 hover:bg-red-500/10 rounded-xl transition-all group">
<LogOut className="w-4 h-4" />
<span>Terminate.Session</span>
</button>
</div>
</div>
</FocusTrap>
)}
</div>
</div>
</div>
</header>
);
}