import React, { useState, useCallback, forwardRef } from 'react'; import { clsx } from 'clsx'; import { useAuthUser, useIsAuthenticated, useAuthActions } from '../../../stores'; import { useTheme } from '../../../contexts/ThemeContext'; import { Button } from '../../ui'; import { Avatar } from '../../ui'; import { Badge } from '../../ui'; import { Modal } from '../../ui'; import { Menu, Search, Bell, Sun, Moon, Computer, Settings, User, LogOut, ChevronDown, X } from 'lucide-react'; export interface HeaderProps { className?: string; /** * Callback when menu button is clicked (for mobile sidebar toggle) */ onMenuClick?: () => void; /** * Whether the sidebar is currently collapsed (affects logo display) */ sidebarCollapsed?: boolean; /** * Show/hide search functionality */ showSearch?: boolean; /** * Show/hide notifications */ showNotifications?: boolean; /** * Show/hide theme toggle */ showThemeToggle?: boolean; /** * Show/hide user menu */ showUserMenu?: boolean; /** * Custom logo component */ logo?: React.ReactNode; /** * Custom search placeholder */ searchPlaceholder?: string; /** * Notification count */ notificationCount?: number; /** * Custom notification handler */ onNotificationClick?: () => void; } export interface HeaderRef { focusSearch: () => void; toggleUserMenu: () => void; closeUserMenu: () => void; } /** * Header - Top navigation header with logo, user menu, notifications, theme toggle * * Features: * - Logo/brand area with responsive sizing * - Global search functionality with keyboard shortcuts * - User avatar with dropdown menu * - Notifications bell with badge count * - Theme toggle button (light/dark/system) * - Mobile hamburger menu integration * - Keyboard navigation support */ export const Header = forwardRef(({ className, onMenuClick, sidebarCollapsed = false, showSearch = true, showNotifications = true, showThemeToggle = true, showUserMenu = true, logo, searchPlaceholder = 'Buscar...', notificationCount = 0, onNotificationClick, }, ref) => { const user = useAuthUser(); const isAuthenticated = useIsAuthenticated(); const { logout } = useAuthActions(); const { theme, resolvedTheme, setTheme } = useTheme(); const [isUserMenuOpen, setIsUserMenuOpen] = useState(false); const [isSearchFocused, setIsSearchFocused] = useState(false); const [searchValue, setSearchValue] = useState(''); const [isThemeMenuOpen, setIsThemeMenuOpen] = useState(false); const searchInputRef = React.useRef(null); // Focus search input const focusSearch = useCallback(() => { searchInputRef.current?.focus(); }, []); // Toggle user menu const toggleUserMenu = useCallback(() => { setIsUserMenuOpen(prev => !prev); }, []); // Close user menu const closeUserMenu = useCallback(() => { setIsUserMenuOpen(false); }, []); // Expose ref methods React.useImperativeHandle(ref, () => ({ focusSearch, toggleUserMenu, closeUserMenu, }), [focusSearch, toggleUserMenu, closeUserMenu]); // Handle search const handleSearchChange = useCallback((e: React.ChangeEvent) => { setSearchValue(e.target.value); }, []); const handleSearchSubmit = useCallback((e: React.FormEvent) => { e.preventDefault(); if (searchValue.trim()) { // TODO: Implement search functionality console.log('Search:', searchValue); } }, [searchValue]); const clearSearch = useCallback(() => { setSearchValue(''); searchInputRef.current?.focus(); }, []); // Handle logout const handleLogout = useCallback(async () => { await logout(); setIsUserMenuOpen(false); }, [logout]); // Keyboard shortcuts React.useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Cmd/Ctrl + K for search if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); focusSearch(); } // Escape to close menus if (e.key === 'Escape') { setIsUserMenuOpen(false); setIsThemeMenuOpen(false); if (isSearchFocused) { searchInputRef.current?.blur(); } } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, [focusSearch, isSearchFocused]); // Close menus when clicking outside React.useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const target = event.target as Element; if (!target.closest('[data-user-menu]')) { setIsUserMenuOpen(false); } if (!target.closest('[data-theme-menu]')) { setIsThemeMenuOpen(false); } }; document.addEventListener('click', handleClickOutside); return () => document.removeEventListener('click', handleClickOutside); }, []); const themeIcons = { light: Sun, dark: Moon, auto: Computer, }; const ThemeIcon = themeIcons[theme] || Sun; return (
{/* Left section */}
{/* Mobile menu button */} {/* Logo */}
{logo || ( <>
PI

Panadería IA

)}
{/* Search */} {showSearch && isAuthenticated && (
setIsSearchFocused(true)} onBlur={() => setIsSearchFocused(false)} placeholder={searchPlaceholder} className={clsx( 'w-full pl-10 pr-10 py-2 text-sm', 'bg-[var(--bg-secondary)] border border-[var(--border-primary)]', 'rounded-lg transition-colors duration-200', 'focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)]/20', 'focus:border-[var(--color-primary)]', 'placeholder:text-[var(--text-tertiary)]' )} aria-label="Buscar en la aplicación" /> {searchValue && ( )} ⌘K
)}
{/* Right section */} {isAuthenticated && (
{/* Mobile search */} {showSearch && ( )} {/* Theme toggle */} {showThemeToggle && (
{isThemeMenuOpen && (
{[ { key: 'light' as const, label: 'Claro', icon: Sun }, { key: 'dark' as const, label: 'Oscuro', icon: Moon }, { key: 'auto' as const, label: 'Sistema', icon: Computer }, ].map(({ key, label, icon: Icon }) => ( ))}
)}
)} {/* Notifications */} {showNotifications && (
)} {/* User menu */} {showUserMenu && user && (
{isUserMenuOpen && (
{/* User info */}
{user.full_name}
{user.email}
{/* Menu items */}
{/* Logout */}
)}
)}
)}
); }); Header.displayName = 'Header';