import React, { useState, useCallback, forwardRef } from 'react'; import { clsx } from 'clsx'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useAuthUser, useIsAuthenticated } from '../../../stores'; import { useTheme } from '../../../contexts/ThemeContext'; import { useNotifications } from '../../../hooks/useNotifications'; import { useHasAccess } from '../../../hooks/useAccessControl'; import { Button } from '../../ui'; import { CountBadge } from '../../ui'; import { TenantSwitcher } from '../../ui/TenantSwitcher'; import { ThemeToggle } from '../../ui/ThemeToggle'; import { NotificationPanel } from '../../ui/NotificationPanel/NotificationPanel'; import { CompactLanguageSelector } from '../../ui/LanguageSelector'; import { Menu, Bell, 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; /** * Custom logo component */ logo?: React.ReactNode; /** * Custom search placeholder */ searchPlaceholder?: string; /** * Notification count */ notificationCount?: number; /** * Custom notification handler */ onNotificationClick?: () => void; } export interface HeaderRef { // No search-related methods anymore } /** * Header - Top navigation header with logo, notifications, theme toggle * * Features: * - Logo/brand area with responsive sizing * - Global search functionality with keyboard shortcuts * - 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, logo, searchPlaceholder, notificationCount = 0, onNotificationClick, }, ref) => { const { t } = useTranslation(); const navigate = useNavigate(); const user = useAuthUser(); const hasAccess = useHasAccess(); // Check both authentication and demo mode const { theme, resolvedTheme, setTheme } = useTheme(); const { notifications, unreadCount, isConnected, markAsRead, markAllAsRead, removeNotification, clearAll } = useNotifications(); const [isNotificationPanelOpen, setIsNotificationPanelOpen] = useState(false); // Filter notifications to last 24 hours for the notification bell // This prevents showing old/stale alerts in the notification panel const recentNotifications = React.useMemo(() => { const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000); return notifications.filter(n => { const alertTime = new Date(n.timestamp).getTime(); return alertTime > oneDayAgo; }); }, [notifications]); const defaultSearchPlaceholder = searchPlaceholder || t('common:forms.search_placeholder', 'Search...'); // Expose ref methods React.useImperativeHandle(ref, () => ({ // No search functions available anymore }), []); // Keyboard shortcuts React.useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Escape to close menus if (e.key === 'Escape') { setIsNotificationPanelOpen(false); } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, []); // Close menus when clicking outside React.useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const target = event.target as Element; if (!target.closest('[data-notification-panel]')) { setIsNotificationPanelOpen(false); } }; document.addEventListener('click', handleClickOutside); return () => document.removeEventListener('click', handleClickOutside); }, []); return (
{/* Left section */}
{/* Mobile menu button */}
{/* Logo */}
{logo || ( <>
PI

{t('common:app.name', 'Panadería IA')}

)}
{/* Tenant Switcher - Desktop */} {hasAccess && (
)} {/* Tenant Switcher - Mobile (in title area) */} {hasAccess && (
)} {/* Space for potential future content */ } {hasAccess && (
  {/* Empty space to maintain layout consistency */}
)}
{/* Right section */} {hasAccess && (
{/* Placeholder for potential future items */ } {/* Language selector */} {/* Theme toggle */} {showThemeToggle && ( )} {/* Notifications */} {showNotifications && (
setIsNotificationPanelOpen(false)} onMarkAsRead={markAsRead} onMarkAllAsRead={markAllAsRead} onRemoveNotification={removeNotification} onClearAll={clearAll} />
)}
)}
); }); Header.displayName = 'Header';