Files
sexy/components/layout/NavBar.tsx
T
valknarandClaude Sonnet 5 c46adf788f Add animated glass background, breadcrumbs, and brand mark polish
Cards now sit on an animated signature-gradient background and use a
translucent, blurred bp-glass surface so it shines through; extended
bp-glass to every stat surface on the Stats page and to its tab switcher.
Adds a Breadcrumbs component below the header on all app pages.

Also: drop the gradient text fill from the wordmark/heading, animate the
BrandMark's heart and curve on the dashboard hero instead of a plain pulse
ring, recenter the heart/curve artwork in icon.svg and BrandMark, close the
mobile nav menu on link click, remove the redundant "back to recordings"
button, and match the "Start session"/"Scan for devices" button sizes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 18:47:30 +02:00

102 lines
3.3 KiB
TypeScript

"use client";
import { useState } from "react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { useTheme } from "next-themes";
import { Menu, Moon, Sun, LogOut } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Sheet, SheetContent, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
import { ConnectionStatus } from "./ConnectionStatus";
import { BrandMark } from "./BrandMark";
import { cn } from "@/lib/utils";
const LINKS = [
{ href: "/", label: "Dashboard" },
{ href: "/control", label: "Control" },
{ href: "/recordings", label: "Recordings" },
{ href: "/sessions", label: "Sessions" },
{ href: "/stats", label: "Stats" },
{ href: "/devices", label: "Devices" },
];
function NavLinks({ onNavigate }: { onNavigate?: () => void }) {
const pathname = usePathname();
return (
<>
{LINKS.map((link) => (
<Link
key={link.href}
href={link.href}
onClick={onNavigate}
className={cn(
"border-b-2 px-2.5 py-1.5 text-xs font-medium tracking-wide uppercase transition-colors",
pathname === link.href
? "border-primary text-foreground"
: "border-transparent text-muted-foreground hover:text-foreground",
)}
>
{link.label}
</Link>
))}
</>
);
}
export function NavBar() {
const { theme, setTheme } = useTheme();
const router = useRouter();
const [menuOpen, setMenuOpen] = useState(false);
async function handleLogout() {
await fetch("/api/auth/logout", { method: "POST" });
router.push("/login");
router.refresh();
}
return (
<header className="sticky top-0 z-40 border-b border-border bg-background/80 backdrop-blur-lg">
<div className="mx-auto flex h-14 max-w-6xl items-center gap-3 px-4">
<Link href="/" className="mr-2 flex items-center gap-2">
<BrandMark size={26} />
<span className="font-heading text-lg font-semibold">Sexy</span>
</Link>
<nav className="hidden items-center gap-1 md:flex">
<NavLinks />
</nav>
<div className="ml-auto flex items-center gap-2">
<ConnectionStatus />
<Button
variant="ghost"
size="icon"
aria-label="Toggle theme"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
>
<Sun className="hidden size-4 dark:block" />
<Moon className="block size-4 dark:hidden" />
</Button>
<Button variant="ghost" size="icon" aria-label="Log out" onClick={handleLogout}>
<LogOut className="size-4" />
</Button>
<Sheet open={menuOpen} onOpenChange={setMenuOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="md:hidden" aria-label="Open menu">
<Menu className="size-4" />
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-64">
<SheetTitle className="px-4 pt-4">Menu</SheetTitle>
<nav className="flex flex-col gap-1 p-4">
<NavLinks onNavigate={() => setMenuOpen(false)} />
</nav>
</SheetContent>
</Sheet>
</div>
</div>
</header>
);
}