import React, { forwardRef, useState, useEffect, useCallback } from 'react'; import { clsx } from 'clsx'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Button, ThemeToggle } from '../../ui'; import { CompactLanguageSelector } from '../../ui/LanguageSelector'; import { getRegisterUrl, getLoginUrl } from '../../../utils/navigation'; import { X } from 'lucide-react'; export interface PublicHeaderProps { className?: string; /** * Custom logo component */ logo?: React.ReactNode; /** * Show theme toggle */ showThemeToggle?: boolean; /** * Show authentication buttons (login/register) */ showAuthButtons?: boolean; /** * Show language selector */ showLanguageSelector?: boolean; /** * Custom navigation items */ navigationItems?: Array<{ id: string; label: string; href: string; external?: boolean; }>; /** * Header variant */ variant?: 'default' | 'transparent' | 'minimal'; } export interface PublicHeaderRef { scrollIntoView: () => void; } /** * PublicHeader - Header component for public pages (landing, login, register) * * Features: * - Clean, minimal design suitable for public pages * - Integrated theme toggle for consistent theming * - Authentication buttons (login/register) * - Optional custom navigation items * - Multiple visual variants * - Responsive design with mobile optimization * - Accessible navigation structure */ export const PublicHeader = forwardRef(({ className, logo, showThemeToggle = true, showAuthButtons = true, showLanguageSelector = true, navigationItems = [], variant = 'default', }, ref) => { const { t } = useTranslation(); const headerRef = React.useRef(null); // State for mobile menu const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); // State for sticky header const [isScrolled, setIsScrolled] = useState(false); // State for active section const [activeSection, setActiveSection] = useState(''); // Default navigation items const defaultNavItems: Array<{id: string; label: string; href: string; external?: boolean}> = [ { id: 'home', label: t('common:nav.home', 'Inicio'), href: '/' }, { id: 'features', label: t('common:nav.features', 'Funcionalidades'), href: '/features' }, { id: 'about', label: t('common:nav.about', 'Nosotros'), href: '/about' }, { id: 'contact', label: t('common:nav.contact', 'Contacto'), href: '/help/support' } ]; const navItems = navigationItems.length > 0 ? navigationItems : defaultNavItems; // Smooth scroll to section const scrollToSection = useCallback((href: string) => { if (href.startsWith('#')) { const element = document.querySelector(href); if (element) { const headerHeight = headerRef.current?.offsetHeight || 0; const elementPosition = element.getBoundingClientRect().top + window.pageYOffset; const offsetPosition = elementPosition - headerHeight - 20; // 20px additional offset window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); // Update URL hash window.history.pushState(null, '', href); // Close mobile menu setIsMobileMenuOpen(false); } } }, []); // Handle scroll for sticky header useEffect(() => { const handleScroll = () => { setIsScrolled(window.scrollY > 20); }; window.addEventListener('scroll', handleScroll, { passive: true }); return () => window.removeEventListener('scroll', handleScroll); }, []); // Active section detection with Intersection Observer useEffect(() => { const observerOptions = { rootMargin: '-100px 0px -66%', threshold: 0 }; const observerCallback = (entries: IntersectionObserverEntry[]) => { entries.forEach((entry) => { if (entry.isIntersecting) { const id = entry.target.getAttribute('id'); if (id) { setActiveSection(id); } } }); }; const observer = new IntersectionObserver(observerCallback, observerOptions); // Observe all sections that are navigation targets navItems.forEach(item => { if (item.href.startsWith('#')) { const element = document.querySelector(item.href); if (element) { observer.observe(element); } } }); return () => observer.disconnect(); }, [navItems]); // Close mobile menu on ESC key useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape' && isMobileMenuOpen) { setIsMobileMenuOpen(false); } }; document.addEventListener('keydown', handleEscape); return () => document.removeEventListener('keydown', handleEscape); }, [isMobileMenuOpen]); // Prevent body scroll when mobile menu is open useEffect(() => { if (isMobileMenuOpen) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } return () => { document.body.style.overflow = ''; }; }, [isMobileMenuOpen]); // Scroll into view const scrollIntoView = useCallback(() => { headerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, []); // Expose ref methods React.useImperativeHandle(ref, () => ({ scrollIntoView, }), [scrollIntoView]); // Render navigation link with improved styles and active state const renderNavLink = (item: typeof navItems[0], isMobile = false) => { const isActive = activeSection === item.id || (item.href.startsWith('#') && item.href === `#${activeSection}`); const linkContent = ( {item.label} ); if (item.href.startsWith('#')) { return ( { e.preventDefault(); scrollToSection(item.href); }} className={clsx( "focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:ring-offset-2 rounded-sm", isMobile && "block w-full py-3 px-4 hover:bg-[var(--bg-secondary)] transition-colors" )} aria-current={isActive ? 'page' : undefined} > {linkContent} ); } if (item.external || item.href.startsWith('http')) { return ( {linkContent} ); } return ( {linkContent} ); }; return ( <> {/* Skip to main content link for accessibility */} {t('common:header.skip_to_content', 'Saltar al contenido principal')}
{/* Logo and brand */}
{logo || ( <>
PI

Panadería IA

)}
{/* Desktop navigation */} {/* Right side actions */}
{/* Language selector - More compact */} {showLanguageSelector && (
)} {/* Theme toggle */} {showThemeToggle && ( )} {/* Authentication buttons - Enhanced */} {showAuthButtons && (
)} {/* Mobile theme toggle */} {showThemeToggle && ( )} {/* Mobile menu button */}
{/* Spacer to prevent content from hiding under fixed header */}