Files
folx-tv/client/src/components/header.tsx
T

148 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Link, useLocation } from "wouter";
import { Menu, X, Search } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useState, useRef, useEffect } from "react";
import folxLogo from "@assets/folx_MT_poz_b_1772296729169.png";
import PushNotificationBell from "./push-notification-bell";
const navItems = [
{ label: "Start", href: "/" },
{ label: "News", href: "/category/News" },
{ label: "Video", href: "/videos" },
{ label: "Galerie", href: "/gallery" },
{ label: "Horoskop", href: "/horoskop" },
{ label: "Rezepte", href: "/rezepte" },
{ label: "Kontakt", href: "/kontakt" },
];
export default function Header() {
const [mobileOpen, setMobileOpen] = useState(false);
const [searchOpen, setSearchOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const [location, navigate] = useLocation();
const searchInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (searchOpen && searchInputRef.current) {
searchInputRef.current.focus();
}
}, [searchOpen]);
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
if (searchQuery.trim().length >= 2) {
const q = encodeURIComponent(searchQuery.trim());
setSearchOpen(false);
setSearchQuery("");
setMobileOpen(false);
navigate(`/search?q=${q}`);
}
};
return (
<header className="sticky top-0 z-50 bg-background/95 backdrop-blur-md border-b border-card-border" data-testid="header">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between gap-4 h-16">
<Link href="/">
<div className="flex items-center gap-2 cursor-pointer" data-testid="link-logo">
<img src={folxLogo} alt="FOLX TV – Volksmusik & Schlager Fernsehsender" className="h-32 w-auto object-contain py-1" />
</div>
</Link>
<nav className="hidden md:flex items-center gap-1" data-testid="nav-desktop">
{navItems.map((item) => (
<Link key={item.href} href={item.href}>
<Button
variant={location === item.href ? "default" : "ghost"}
size="sm"
data-testid={`link-nav-${item.label.toLowerCase().replace(/\s/g, "-")}`}
>
{item.label}
</Button>
</Link>
))}
</nav>
<div className="flex items-center gap-2">
<PushNotificationBell />
{searchOpen ? (
<form onSubmit={handleSearch} className="flex items-center gap-1" data-testid="form-header-search">
<input
ref={searchInputRef}
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Suchen..."
className="w-32 sm:w-48 px-3 py-1.5 bg-card border border-card-border rounded-md text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary"
data-testid="input-header-search"
/>
<Button
type="button"
variant="ghost"
size="icon"
className="h-8 w-8"
onClick={() => { setSearchOpen(false); setSearchQuery(""); }}
data-testid="button-close-search"
>
<X className="w-4 h-4" />
</Button>
</form>
) : (
<Button
variant="ghost"
size="icon"
className="h-8 w-8"
onClick={() => setSearchOpen(true)}
data-testid="button-open-search"
>
<Search className="w-4 h-4" />
</Button>
)}
<Button
variant="ghost"
size="icon"
className="md:hidden"
onClick={() => setMobileOpen(!mobileOpen)}
data-testid="button-mobile-menu"
>
{mobileOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</Button>
</div>
</div>
</div>
{mobileOpen && (
<div className="md:hidden border-t border-card-border bg-card" data-testid="nav-mobile">
<nav className="flex flex-col p-4 gap-1">
<form onSubmit={handleSearch} className="mb-2" data-testid="form-mobile-search">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Artikel, Videos suchen..."
className="w-full pl-9 pr-3 py-2 bg-background border border-card-border rounded-md text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary"
data-testid="input-mobile-search"
/>
</div>
</form>
{navItems.map((item) => (
<Link key={item.href} href={item.href}>
<Button
variant={location === item.href ? "default" : "ghost"}
className="w-full justify-start"
onClick={() => setMobileOpen(false)}
data-testid={`link-mobile-${item.label.toLowerCase().replace(/\s/g, "-")}`}
>
{item.label}
</Button>
</Link>
))}
</nav>
</div>
)}
</header>
);
}