7a1ab18657
Align the next-intl message schema across components, hooks, and locale files so the frontend uses one canonical structure instead of compile-first workarounds. Restore Spanish, French, German, Japanese, and Korean translations to the new schema while narrowing clipboard hook dependencies to translation contracts.
146 lines
4.7 KiB
TypeScript
146 lines
4.7 KiB
TypeScript
"use client";
|
|
import { useState } from "react";
|
|
import { useLocale, useTranslations } from "next-intl";
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
import { cn } from "@/lib/utils";
|
|
import { Button } from "@/components/ui/button";
|
|
import Image from "next/image";
|
|
import { Menu, X, Github } from "lucide-react";
|
|
import LanguageSwitcher from "@/components/LanguageSwitcher";
|
|
import ThemeToggle from "@/components/web/ThemeToggle";
|
|
|
|
/**
|
|
* Header component providing navigation, language switching, and GitHub link
|
|
* Features responsive design with mobile menu support
|
|
*/
|
|
const Header = () => {
|
|
const t = useTranslations("text.navigation");
|
|
const lang = useLocale();
|
|
const pathname = usePathname();
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
// Configuration for navigation items
|
|
const navItems = [
|
|
{ href: `/${lang}`, label: t("home") },
|
|
{ href: `/${lang}/features`, label: t("features") },
|
|
{ href: `/${lang}/blog`, label: t("blog") },
|
|
{ href: `/${lang}/about`, label: t("about") },
|
|
{ href: `/${lang}/help`, label: t("help") },
|
|
{ href: `/${lang}/faq`, label: t("faq") },
|
|
{ href: `/${lang}/terms`, label: t("terms") },
|
|
{ href: `/${lang}/privacy`, label: t("privacy") },
|
|
];
|
|
|
|
// GitHub repository URL
|
|
const githubUrl = "https://github.com/david-bai00/PrivyDrop";
|
|
|
|
return (
|
|
<header className="bg-background border-b sticky top-0 z-50">
|
|
<div className="container mx-auto px-4 py-4">
|
|
<div className="flex justify-between items-center">
|
|
{/* Logo and site name */}
|
|
<Link href={`/${lang}`} className="flex items-center space-x-2">
|
|
<Image
|
|
src="/logo.png"
|
|
alt="PrivyDrop Logo"
|
|
width={40}
|
|
height={40}
|
|
priority
|
|
/>
|
|
<span className="font-bold text-xl hidden sm:inline">
|
|
PrivyDrop
|
|
</span>
|
|
</Link>
|
|
|
|
{/* Desktop navigation and controls */}
|
|
<div className="hidden md:flex items-center space-x-4">
|
|
<nav>
|
|
<ul className="flex space-x-2">
|
|
{navItems.map((item) => (
|
|
<li key={item.href}>
|
|
<Button
|
|
asChild
|
|
variant="ghost"
|
|
size="sm"
|
|
className={cn(
|
|
"hover:bg-muted",
|
|
pathname === item.href && "bg-muted"
|
|
)}
|
|
>
|
|
<Link href={item.href}>{item.label}</Link>
|
|
</Button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</nav>
|
|
{/* Desktop GitHub link and language switcher */}
|
|
<div className="flex items-center space-x-2">
|
|
<Button asChild variant="ghost" size="icon">
|
|
<Link
|
|
href={githubUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
aria-label="GitHub Repository"
|
|
>
|
|
<Github className="h-5 w-5" />
|
|
</Link>
|
|
</Button>
|
|
<ThemeToggle />
|
|
<LanguageSwitcher />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile menu controls */}
|
|
<div className="md:hidden flex items-center space-x-2">
|
|
<LanguageSwitcher />
|
|
<ThemeToggle />
|
|
<Button asChild variant="ghost" size="icon">
|
|
<Link
|
|
href={githubUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
aria-label="GitHub Repository"
|
|
>
|
|
<Github className="h-5 w-5" />
|
|
</Link>
|
|
</Button>
|
|
<button
|
|
className="p-2"
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
aria-label="Toggle menu"
|
|
>
|
|
{isOpen ? <X size={24} /> : <Menu size={24} />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile navigation menu */}
|
|
{isOpen && (
|
|
<nav className="md:hidden mt-4">
|
|
<ul className="flex flex-col space-y-2">
|
|
{navItems.map((item) => (
|
|
<li key={item.href}>
|
|
<Button
|
|
asChild
|
|
variant="ghost"
|
|
className={cn(
|
|
"w-full justify-start",
|
|
pathname === item.href && "bg-muted"
|
|
)}
|
|
onClick={() => setIsOpen(false)}
|
|
>
|
|
<Link href={item.href}>{item.label}</Link>
|
|
</Button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</nav>
|
|
)}
|
|
</div>
|
|
</header>
|
|
);
|
|
};
|
|
|
|
export default Header;
|