225 lines
9.2 KiB
TypeScript
225 lines
9.2 KiB
TypeScript
import { useState } from 'react';
|
|
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Menu, X, Phone, Home, Shield, FileText, Truck, Users, Search, Accessibility } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import LanguageSwitcher from '@/components/LanguageSwitcher';
|
|
import AccessibilityBar from '@/components/AccessibilityBar';
|
|
|
|
const Header = () => {
|
|
const { t } = useTranslation('nav');
|
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
const [isAccessibilityOpen, setIsAccessibilityOpen] = useState(false);
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const baseUrl = import.meta.env.BASE_URL;
|
|
|
|
const navigation = [
|
|
{ name: t('home'), href: '/', icon: Home },
|
|
{ name: t('privateCustomers'), href: '/privatkunden', icon: Users },
|
|
{ name: t('familiesAndChildren', 'Familien & Kinder'), href: '/familien-kinder', icon: Users },
|
|
{ name: t('seniors'), href: '/senioren', icon: Shield },
|
|
{ name: t('businessCustomers'), href: '/geschaeftskunden', icon: Truck },
|
|
];
|
|
|
|
const rightMenuItems = [
|
|
{ name: t('agency'), href: '/agentur', icon: Shield },
|
|
{ name: t('finances'), href: '/finanzen', icon: FileText },
|
|
{ name: t('contact'), href: '/contact#contact', icon: Phone },
|
|
];
|
|
|
|
const searchItems = [
|
|
...navigation,
|
|
...rightMenuItems,
|
|
{ name: t('accidentInsurance', 'Unfall'), href: '/unfall', icon: Shield },
|
|
{ name: t('carInsurance', 'KFZ'), href: '/kfz', icon: Truck },
|
|
{ name: t('services', 'Leistungen'), href: '/leistungen', icon: FileText },
|
|
{ name: 'Risikolebensversicherung', href: '/risikolebensversicherung', icon: Shield },
|
|
{ name: 'Krankheits-Schutzbrief', href: '/krankheitsschutzbrief', icon: Shield },
|
|
{ name: 'Vermögenssicherung', href: '/vermoegenssicherung', icon: Shield },
|
|
];
|
|
|
|
const normalizedQuery = searchQuery.trim().toLowerCase();
|
|
const filteredItems = normalizedQuery
|
|
? searchItems.filter((item) => item.name.toLowerCase().includes(normalizedQuery))
|
|
: [];
|
|
|
|
return (
|
|
<header className="bg-white shadow-sm border-b">
|
|
<div className="container mx-auto px-2 lg:px-4">
|
|
|
|
<div className="flex items-center justify-between h-16 min-w-0">
|
|
{/* Logo */}
|
|
<div className="flex items-center gap-3 shrink-0 min-w-0">
|
|
<Link to="/" className="flex items-center space-x-2 shrink-0 min-w-0">
|
|
<img
|
|
src={`${baseUrl}${encodeURI('Agentur Mizera & Partner logo_2016.png')}`}
|
|
alt="Agentur Mizera & Partner"
|
|
className="h-14 w-auto"
|
|
loading="eager"
|
|
/>
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Right side items */}
|
|
<div className="flex items-center space-x-2 lg:space-x-4 shrink-0">
|
|
{/* Header Search (Desktop) */}
|
|
<div className="hidden md:block relative">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-500" />
|
|
<input
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
className="w-44 lg:w-56 2xl:w-72 h-10 rounded-full border border-gray-200 bg-white pl-9 pr-4 text-sm shadow-sm"
|
|
placeholder={t('searchPlaceholder', 'Suche...')}
|
|
/>
|
|
|
|
{filteredItems.length > 0 && (
|
|
<div className="absolute right-0 mt-2 w-72 rounded-md border border-gray-200 bg-white shadow-sm overflow-hidden z-50">
|
|
{filteredItems.slice(0, 8).map((item) => (
|
|
<button
|
|
key={`${item.href}-${item.name}`}
|
|
type="button"
|
|
className="w-full text-left px-4 py-3 hover:bg-gray-50 transition-colors"
|
|
onClick={() => {
|
|
navigate(item.href);
|
|
setSearchQuery('');
|
|
}}
|
|
>
|
|
<div className="text-sm font-semibold text-gray-900">{item.name}</div>
|
|
<div className="text-xs text-gray-600 mt-0.5">{item.href}</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Language Switcher (Desktop only) */}
|
|
<div className="hidden md:block">
|
|
<LanguageSwitcher />
|
|
</div>
|
|
|
|
{/* Mobile menu button */}
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="md:hidden"
|
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
|
>
|
|
{isMenuOpen ? (
|
|
<X className="h-6 w-6" />
|
|
) : (
|
|
<Menu className="h-6 w-6" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Desktop Navigation (zweite Zeile) */}
|
|
<div className="hidden md:flex items-center justify-between gap-4 border-t py-3 min-w-0">
|
|
<nav className="flex flex-1 items-center min-w-0">
|
|
<div className="flex flex-wrap items-center gap-2 min-w-0">
|
|
{navigation.map((item) => {
|
|
const Icon = item.icon;
|
|
const isActive = location.pathname === item.href;
|
|
|
|
return (
|
|
<Link
|
|
key={item.name}
|
|
to={item.href}
|
|
className={`flex items-center space-x-1 px-3 py-2 rounded-md text-sm font-semibold transition-colors ${
|
|
isActive
|
|
? 'text-blue-600 bg-blue-50'
|
|
: 'text-gray-700 hover:text-blue-600 hover:bg-gray-50'
|
|
}`}
|
|
>
|
|
<Icon className="h-5 w-5" strokeWidth={1.25} />
|
|
<span className="whitespace-nowrap">{item.name}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</nav>
|
|
|
|
<div className="flex flex-wrap items-center gap-2 shrink-0">
|
|
{rightMenuItems.map((item) => {
|
|
const Icon = item.icon;
|
|
const isActive = location.pathname === item.href;
|
|
|
|
return (
|
|
<Link
|
|
key={item.name}
|
|
to={item.href}
|
|
className={`flex items-center space-x-1 px-3 py-2 rounded-md text-sm font-semibold transition-colors ${
|
|
isActive
|
|
? 'text-blue-600 bg-blue-50'
|
|
: 'text-gray-700 hover:text-blue-600 hover:bg-gray-50'
|
|
}`}
|
|
>
|
|
<Icon className="h-5 w-5" strokeWidth={1.25} />
|
|
<span className="whitespace-nowrap">{item.name}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Navigation */}
|
|
{isMenuOpen && (
|
|
<div className="md:hidden border-t py-4">
|
|
<nav className="flex flex-col space-y-2">
|
|
{/* Language & Accessibility Section (Mobile) */}
|
|
<div className="px-3 py-2 border-b border-gray-200">
|
|
<div className="text-xs font-medium text-gray-500 mb-2">Sprache / Language</div>
|
|
<div className="flex items-center justify-between">
|
|
<LanguageSwitcher />
|
|
<button
|
|
onClick={() => setIsAccessibilityOpen(!isAccessibilityOpen)}
|
|
className={`px-3 py-1.5 rounded-lg text-sm font-medium transition-all duration-200 border ${
|
|
isAccessibilityOpen
|
|
? 'text-blue-600 bg-blue-50 border-blue-200 shadow-sm'
|
|
: 'text-gray-700 bg-white border-gray-200 hover:text-blue-600 hover:bg-gray-50 hover:border-blue-200'
|
|
}`}
|
|
aria-label="Barrierefreiheit"
|
|
>
|
|
<Accessibility className="h-4 w-4" strokeWidth={1.5} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{[...navigation, ...rightMenuItems].map((item) => {
|
|
const Icon = item.icon;
|
|
const isActive = location.pathname === item.href;
|
|
|
|
return (
|
|
<Link
|
|
key={item.name}
|
|
to={item.href}
|
|
onClick={() => setIsMenuOpen(false)}
|
|
className={`flex items-center space-x-3 px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
|
isActive
|
|
? 'text-blue-600 bg-blue-50'
|
|
: 'text-gray-700 hover:text-blue-600 hover:bg-gray-50'
|
|
}`}
|
|
>
|
|
<Icon className="h-5 w-5" strokeWidth={1.25} />
|
|
<span>{item.name}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* Mobile Accessibility Bar */}
|
|
{isAccessibilityOpen && (
|
|
<div className="mt-4 pt-4 border-t border-gray-200">
|
|
<AccessibilityBar />
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</header>
|
|
);
|
|
};
|
|
|
|
export default Header; |