\x89PNG\r\n\x1a\n\x00\x00\x00\x0DIHDR\x00\x00\x00\x01\x00 \x00\x00\x01\x08\x06\x00\x00\x00\x1F\x15\xC4\x89\x00\x00\x00 \x0AIDATx\x9Ccb\x00\x00\x00\x06\x00\x03\x1A\x05\x9D\x00\x00 \x00\x00IEND\xAE\x42\x60\x82
| Path : /var/www/html/infraai.ai/frontend/src/components/ |
|
B-Con CMD Config cPanel C-Rdp D-Log Info Jump Mass Ransom Symlink vHost Zone-H |
| Current File : /var/www/html/infraai.ai/frontend/src/components/Header.jsx |
import React from "react";
import { useI18n } from "@/i18n";
import { SiteBrand } from "@/components/SiteBrand";
import { useTheme } from "@/theme";
import { Link } from "react-router-dom";
import { Sun, Moon, ChevronDown, Menu, X } from "lucide-react";
/** Geçici gizli nav öğeleri — tekrar açmak için listeden çıkarın */
const HIDDEN_NAV_KEYS = new Set(["support"]);
/** Geçici gizli dropdown menü linkleri */
const HIDDEN_MENU_HREFS = new Set(["#references"]);
const SHOW_ADMIN_LINK = false;
export default function Header() {
const { lang, setLang, t } = useI18n();
const { theme, toggle } = useTheme();
const [scrolled, setScrolled] = React.useState(false);
const [open, setOpen] = React.useState(false);
const [openDropdown, setOpenDropdown] = React.useState(null); // "solutions" | "services" | null
React.useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 20);
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
// close dropdown on outside click / esc
React.useEffect(() => {
const onKey = (e) => e.key === "Escape" && setOpenDropdown(null);
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, []);
const NAV = [
{ key: "solutions", href: "#capabilities", dropdown: "solutions" },
{ key: "services", href: "#services", dropdown: "services" },
{ key: "techSecurity", href: "#partners", dropdown: "tech" },
{ key: "about", href: "#about" },
{ key: "contact", href: "#contact" },
].filter((item) => !HIDDEN_NAV_KEYS.has(item.key));
return (
<header
className={`fixed top-0 inset-x-0 z-50 transition-all duration-300 ${
scrolled
? "bg-bg/85 backdrop-blur-xl border-b border-line"
: "bg-transparent border-b border-transparent"
}`}
data-testid="site-header"
>
<div className="container-x flex items-center justify-between h-16">
{/* Logo */}
<SiteBrand href="/" data-testid="nav-logo" />
{/* Desktop nav */}
<nav
className="hidden lg:flex items-center gap-1"
onMouseLeave={() => setOpenDropdown(null)}
>
{NAV.map((item) => (
<NavItem
key={item.key}
item={item}
label={t.nav[item.key]}
menu={item.dropdown ? t.nav[`${item.dropdown}Menu`] : null}
isOpen={openDropdown === item.dropdown}
onOpen={() => setOpenDropdown(item.dropdown || null)}
onClose={() => setOpenDropdown(null)}
/>
))}
</nav>
{/* Right cluster */}
<div className="flex items-center gap-2">
<button
onClick={toggle}
data-testid="theme-toggle"
aria-label="Toggle theme"
className="hidden sm:inline-flex items-center justify-center border border-line px-2.5 py-1.5 text-ink-mute/80 hover:text-white hover:border-amber transition-colors"
>
{theme === "dark" ? <Sun size={14} /> : <Moon size={14} />}
</button>
<div className="hidden sm:flex items-center text-[11px] font-mono border border-line rounded-none overflow-hidden">
{["en", "tr"].map((l) => (
<button
key={l}
onClick={() => setLang(l)}
data-testid={`lang-${l}`}
className={`px-2.5 py-1.5 transition-colors ${
lang === l ? "bg-cyan text-black" : "site-nav-lang-btn"
}`}
>
{l.toUpperCase()}
</button>
))}
</div>
{SHOW_ADMIN_LINK && (
<Link
to="/admin/login"
className="hidden md:inline-flex text-[12px] text-ink-mute hover:text-ink font-mono tracking-wide px-2"
data-testid="nav-admin"
>
{t.nav.login}
</Link>
)}
<a
href="#contact"
className="hidden sm:inline-flex items-center gap-1 bg-cyan text-black font-mono text-[11px] tracking-widest2 uppercase px-2.5 py-1.5 hover:bg-cyan-light transition-colors"
data-testid="nav-contact-cta"
>
{t.nav.demo}
<span className="text-[11px] leading-none">→</span>
</a>
<button
className="lg:hidden site-nav-mobile-link p-2"
onClick={() => setOpen(!open)}
data-testid="nav-mobile-toggle"
aria-label="Menu"
>
{open ? <X size={20} /> : <Menu size={20} />}
</button>
</div>
</div>
{/* Mobile menu */}
{open && (
<div className="lg:hidden border-t border-line bg-bg-paper">
<div className="container-x py-5 flex flex-col gap-1 max-h-[80vh] overflow-y-auto">
{NAV.map((item) => (
<MobileNavItem
key={item.key}
item={item}
label={t.nav[item.key]}
menu={item.dropdown ? t.nav[`${item.dropdown}Menu`] : null}
onPick={() => setOpen(false)}
/>
))}
<div className="border-t border-line mt-3 pt-4 flex items-center justify-between">
<div className="flex items-center gap-2">
{["en", "tr"].map((l) => (
<button
key={l}
onClick={() => setLang(l)}
className={`px-3 py-1.5 text-[11px] font-mono border transition-colors ${
lang === l ? "border-cyan text-cyan" : "border-line site-nav-lang-btn"
}`}
>
{l.toUpperCase()}
</button>
))}
</div>
<button
onClick={toggle}
aria-label="Toggle theme"
className="inline-flex items-center justify-center border border-line px-3 py-1.5 text-ink-mute"
>
{theme === "dark" ? <Sun size={14} /> : <Moon size={14} />}
</button>
</div>
<a
href="#contact"
onClick={() => setOpen(false)}
className="mt-3 bg-cyan text-black text-center font-mono text-[12px] tracking-widest2 uppercase py-3"
>
{t.nav.demo} →
</a>
{SHOW_ADMIN_LINK && (
<Link
to="/admin/login"
onClick={() => setOpen(false)}
className="text-center text-[12px] text-ink-mute font-mono tracking-wide py-2"
>
{t.nav.login}
</Link>
)}
</div>
</div>
)}
</header>
);
}
/* ---------- Desktop Nav Item ---------- */
function NavItem({ item, label, menu, isOpen, onOpen, onClose }) {
if (menu) {
return (
<div
className="relative"
onMouseEnter={onOpen}
onMouseLeave={onClose}
>
<a
href={item.href}
className={`site-nav-link group flex items-center gap-1 px-3 py-2 text-[13px] font-body tracking-tight ${
isOpen ? "is-open" : ""
}`}
data-testid={`nav-${item.key}`}
>
{label}
<ChevronDown
size={12}
className={`site-nav-chevron ${isOpen ? "rotate-180" : ""}`}
strokeWidth={2}
/>
</a>
{isOpen && (
<div
className="absolute left-0 top-full pt-3 min-w-[320px] z-50"
data-testid={`dropdown-${item.key}`}
>
<div className="site-nav-dropdown shadow-xl">
<div className="p-2">
{(menu || []).filter((m) => !HIDDEN_MENU_HREFS.has(m.href)).map((m, i) => (
<a
key={i}
href={m.href}
onClick={onClose}
className="site-nav-dropdown-item block px-4 py-3"
data-testid={`menu-${item.key}-${i}`}
>
<div className="site-nav-dropdown-item__title font-body text-[13.5px] mb-0.5">
{m.k}
</div>
<div className="nav-dropdown-desc font-mono text-[10.5px] tracking-wide">
{m.v}
</div>
</a>
))}
</div>
</div>
</div>
)}
</div>
);
}
return (
<a
href={item.href}
onMouseEnter={onClose}
className="site-nav-link px-3 py-2 text-[13px] font-body tracking-tight"
data-testid={`nav-${item.key}`}
>
{label}
</a>
);
}
/* ---------- Mobile Nav Item (collapsible if dropdown) ---------- */
function MobileNavItem({ item, label, menu, onPick }) {
const [exp, setExp] = React.useState(false);
if (menu) {
return (
<div className="border-b border-line/60">
<button
onClick={() => setExp(!exp)}
className={`site-nav-mobile-toggle w-full flex items-center justify-between py-3 text-[15px] ${
exp ? "is-open" : ""
}`}
>
<span>{label}</span>
<ChevronDown size={14} className={`site-nav-chevron transition-transform ${exp ? "rotate-180" : ""}`} />
</button>
{exp && (
<div className="pb-3 pl-3 space-y-2">
{(menu || []).filter((m) => !HIDDEN_MENU_HREFS.has(m.href)).map((m, i) => (
<a
key={i}
href={m.href}
onClick={onPick}
className="site-nav-mobile-sub block py-1.5"
>
<div className="text-[13.5px]">{m.k}</div>
<div className="nav-dropdown-desc font-mono text-[10.5px]">{m.v}</div>
</a>
))}
</div>
)}
</div>
);
}
return (
<a
href={item.href}
onClick={onPick}
className="site-nav-mobile-link block py-3 text-[15px] border-b border-line/60"
>
{label}
</a>
);
}