"use client"; import React, { useState, useEffect, useRef } from "react"; import Link from "next/link"; import AppImage from "@/components/ui/AppImage"; import { LinkedInIcon, InstagramIcon, TwitterXIcon, FacebookIcon, RssIcon, MenuIcon, CloseIcon, SearchIcon, } from "@/components/ui/Icons"; import { createClient } from "@/lib/supabase/client"; import NotificationCenter from "@/components/NotificationCenter"; import LanguageSwitcher from "@/components/LanguageSwitcher"; import AdImage from "@/components/AdImage"; import { ADS_728X90, shuffle } from "@/lib/ads"; interface NavItem { label: string; href: string; dropdown?: { label: string; href: string }[]; } const navLinks: NavItem[] = [ { label: "NEWS", href: "/news", dropdown: [ { label: "Live Production", href: "/news?category=live-production" }, { label: "IP & Cloud", href: "/news?category=ip-cloud" }, { label: "AI & Automation", href: "/news?category=ai" }, { label: "People", href: "/news?category=people" }, ], }, { label: "GEAR & REVIEWS", href: "/gear", dropdown: [ { label: "Audio", href: "/gear?category=audio" }, { label: "Cameras", href: "/gear?category=cameras" }, { label: "Reviews", href: "/gear" }, ], }, { label: "SHOW COVERAGE", href: "/show-coverage", dropdown: [ { label: "NAB Show", href: "/show-coverage" }, { label: "IBC", href: "/show-coverage?event=ibc" }, ], }, { label: "TECHNOLOGY", href: "/technology", dropdown: [ { label: "Storage & MAM", href: "/technology?category=storage" }, { label: "Streaming", href: "/technology?category=streaming" }, { label: "AI & Automation", href: "/technology?category=ai" }, ], }, { label: "NEWSLETTER", href: "/newsletter/archive" }, { label: "FORUM", href: "/forum" }, { label: "ADVERTISE", href: "/advertise" }, { label: "ABOUT", href: "/about" }, ]; export default function Header() { const [scrolled, setScrolled] = useState(false); const [mobileOpen, setMobileOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [searchFocused, setSearchFocused] = useState(false); const [currentUserId, setCurrentUserId] = useState(null); const [currentUserEmail, setCurrentUserEmail] = useState(null); const [isAdmin, setIsAdmin] = useState(false); const [subEmail, setSubEmail] = useState(""); const [subStatus, setSubStatus] = useState<"idle" | "loading" | "success" | "error">("idle"); const [subMessage, setSubMessage] = useState(""); const searchRef = useRef(null); const mobileMenuRef = useRef(null); const hamburgerRef = useRef(null); const navLinksRef = useRef<(HTMLAnchorElement | null)[]>([]); useEffect(() => { const handler = () => setScrolled(window.scrollY > 60); window.addEventListener("scroll", handler, { passive: true }); return () => window.removeEventListener("scroll", handler); }, []); useEffect(() => { const supabase = createClient(); const ADMIN_ROLES = new Set([ "administrator", "admin", "super_admin", "editor", "support", ]); const refreshAdmin = async (userId: string | null) => { if (!userId) { setIsAdmin(false); return; } const { data } = await supabase .from("user_profiles") .select("role") .eq("id", userId) .maybeSingle(); setIsAdmin(!!data?.role && ADMIN_ROLES.has(data.role)); }; supabase.auth.getUser().then(({ data: { user } }) => { setCurrentUserId(user?.id || null); setCurrentUserEmail(user?.email || null); refreshAdmin(user?.id || null); }); const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { setCurrentUserId(session?.user?.id || null); setCurrentUserEmail(session?.user?.email || null); refreshAdmin(session?.user?.id || null); }); return () => subscription.unsubscribe(); }, []); // Global keyboard shortcuts useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { const tag = document.activeElement?.tagName; const isInput = tag === "INPUT" || tag === "TEXTAREA"; // "/" to focus search if (e.key === "/" && !isInput) { e.preventDefault(); searchRef.current?.focus(); } // Cmd/Ctrl+Shift+A — open AI Console if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === "A") { e.preventDefault(); window.location.href = "/dashboard/ai-console"; } // Escape: close mobile menu or clear search if (e.key === "Escape") { if (mobileOpen) { setMobileOpen(false); hamburgerRef.current?.focus(); } else if (searchFocused) { setSearchQuery(""); searchRef.current?.blur(); } } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [searchFocused, mobileOpen]); // Arrow key navigation for desktop nav links const handleNavKeyDown = (e: React.KeyboardEvent, index: number, refs: React.MutableRefObject<(HTMLAnchorElement | null)[]>) => { const items = refs.current.filter(Boolean); if (e.key === "ArrowRight" || e.key === "ArrowDown") { e.preventDefault(); const next = items[(index + 1) % items.length]; next?.focus(); } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") { e.preventDefault(); const prev = items[(index - 1 + items.length) % items.length]; prev?.focus(); } else if (e.key === "Home") { e.preventDefault(); items[0]?.focus(); } else if (e.key === "End") { e.preventDefault(); items[items.length - 1]?.focus(); } }; // Trap focus inside mobile menu when open useEffect(() => { if (!mobileOpen) return; const menu = mobileMenuRef.current; if (!menu) return; const focusable = menu.querySelectorAll( 'a[href], button, input, [tabindex]:not([tabIndex="-1"])' ); const first = focusable[0]; const last = focusable[focusable.length - 1]; first?.focus(); const trap = (e: KeyboardEvent) => { if (e.key !== "Tab") return; if (e.shiftKey) { if (document.activeElement === first) { e.preventDefault(); last?.focus(); } } else { if (document.activeElement === last) { e.preventDefault(); first?.focus(); } } }; menu.addEventListener("keydown", trap); return () => menu.removeEventListener("keydown", trap); }, [mobileOpen]); const handleSubscribe = async (e: React.FormEvent) => { e.preventDefault(); if (!subEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(subEmail)) { setSubStatus("error"); setSubMessage("Enter a valid email"); return; } setSubStatus("loading"); try { const res = await fetch("/api/newsletter/subscribe", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: subEmail, topics: [] }), }); if (res.ok) { setSubStatus("success"); setSubMessage("You're subscribed!"); setSubEmail(""); setTimeout(() => setSubStatus("idle"), 4000); } else { const data = await res.json(); setSubStatus("error"); setSubMessage(data?.error || "Failed. Try again."); setTimeout(() => setSubStatus("idle"), 3000); } } catch { setSubStatus("error"); setSubMessage("Network error. Try again."); setTimeout(() => setSubStatus("idle"), 3000); } }; return ( <> {/* TOP BAR */}

News & Intelligence for Broadcast, Post-Production & Streaming Technology

{/* Language Switcher — Section 18B */}
{!currentUserId && ( <> Sign In Register )} {currentUserId && ( My Profile )} {isAdmin && ( <> WP Import Articles Editorial Newsletter {/* TOOLS dropdown — admin tools consolidated */}
Users Analytics Audit Log Notifications Forum Seed Company Tracker Show Calendar AI Console Banned Terms
Dashboard )} {/* Inline Email Subscription Form */} {subStatus === "success" ? ( {subMessage} ) : (
setSubEmail(e.target.value)} placeholder="your@email.com" aria-label="Email address for newsletter" disabled={subStatus === "loading"} className="h-5 px-2 text-[11px] bg-[#0d1520] border border-[#2a3a50] text-[#ccc] placeholder-[#555] rounded focus:outline-none focus:border-[#3b82f6] focus-visible:ring-1 focus-visible:ring-[#3b82f6] w-32 disabled:opacity-50 transition-colors" /> {subStatus === "error" && ( {subMessage} )}
)}
{/* HEADER LEADERBOARD — site-wide 728x90 between utility bar and main nav */} {ADS_728X90.length > 0 && (
)} {/* MAIN NAV */}
); }