Files
streamrip-ui/frontend/app/components/Header.tsx
T

150 lines
4.6 KiB
TypeScript

"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import { loadBasket } from "../lib/basket";
const NAV = [
{ href: "/", label: "Suche" },
{ href: "/discover", label: "Entdecken" },
{ href: "/queue", label: "Queue" },
{ href: "/history", label: "Verlauf" },
{ href: "/basket", label: "Korb" },
{ href: "/files", label: "Dateien" },
{ href: "/settings", label: "Einstellungen" },
];
export default function Header() {
const pathname = usePathname();
const [backendOk, setBackendOk] = useState<boolean | null>(null);
const [theme, setTheme] = useState<"dark" | "light">("dark");
const [basketCount, setBasketCount] = useState(0);
useEffect(() => {
const update = () => {
try {
setBasketCount(loadBasket().length);
} catch {}
};
update();
window.addEventListener("basket-change", update);
return () => window.removeEventListener("basket-change", update);
}, [pathname]);
useEffect(() => {
try {
const stored = localStorage.getItem("srip-theme");
if (stored === "light" || stored === "dark") {
setTheme(stored);
document.documentElement.dataset.theme = stored === "light" ? "light" : "";
} else if (window.matchMedia("(prefers-color-scheme: light)").matches) {
setTheme("light");
document.documentElement.dataset.theme = "light";
}
} catch {}
}, []);
function toggleTheme() {
const next = theme === "dark" ? "light" : "dark";
setTheme(next);
try {
localStorage.setItem("srip-theme", next);
} catch {}
document.documentElement.dataset.theme = next === "light" ? "light" : "";
}
useEffect(() => {
let alive = true;
fetch("/api/health")
.then((r) => {
if (alive) setBackendOk(r.ok);
})
.catch(() => {
if (alive) setBackendOk(false);
});
return () => {
alive = false;
};
}, [pathname]);
return (
<header
style={{
position: "sticky",
top: 0,
zIndex: 50,
background: "color-mix(in srgb, var(--bg) 92%, transparent)",
backdropFilter: "blur(8px)",
borderBottom: "1px solid var(--border)",
}}
>
<div
style={{
maxWidth: 1200,
margin: "0 auto",
padding: "12px 20px",
display: "flex",
alignItems: "center",
gap: 24,
}}
>
<Link href="/" style={{ textDecoration: "none", color: "var(--text)", display: "flex", alignItems: "center", gap: 10 }}>
<span
aria-hidden
style={{
width: 28,
height: 28,
borderRadius: "50%",
background: "radial-gradient(circle at center, var(--accent) 0 3px, #2a2a31 3px 5px, #0c0c0e 5px)",
border: "2px solid var(--accent)",
flexShrink: 0,
}}
/>
<span style={{ fontWeight: 700, fontSize: 18, letterSpacing: "-0.01em" }}>streamrip-ui</span>
</Link>
<nav style={{ display: "flex", gap: 4, flex: 1 }}>
{NAV.map((item) => {
const active = item.href === "/" ? pathname === "/" : pathname.startsWith(item.href);
const label = item.href === "/basket" && basketCount > 0 ? `Korb (${basketCount})` : item.label;
return (
<Link
key={item.href}
href={item.href}
style={{
textDecoration: "none",
color: active ? "var(--text)" : "var(--muted)",
padding: "6px 12px",
borderRadius: 6,
borderBottom: active ? "2px solid var(--accent)" : "2px solid transparent",
fontWeight: active ? 600 : 400,
}}
>
{label}
</Link>
);
})}
</nav>
<button
onClick={toggleTheme}
className="btn"
title={theme === "dark" ? "Hellmodus einschalten" : "Dunkelmodus einschalten"}
style={{ padding: "6px 10px" }}
>
{theme === "dark" ? "☀" : "☾"}
</button>
<span
title={backendOk === null ? "Backend-Status wird geprüft" : backendOk ? "Backend erreichbar" : "Backend nicht erreichbar — Server prüfen"}
style={{
width: 10,
height: 10,
borderRadius: "50%",
background: backendOk === null ? "var(--muted)" : backendOk ? "var(--ok)" : "var(--danger)",
flexShrink: 0,
}}
/>
</div>
</header>
);
}