stremio/apps/admin-ui/src/components/Nav.tsx
Jos Vooges | STH fafe97e729 Rename Licenties to DRM and ingest event API ClearKeys into drm_keys.
Schedule feed kids/keys are persisted on fetch/play; admin backfill covers live lists and events.
2026-09-23 01:02:41 +02:00

143 lines
4.5 KiB
TypeScript

"use client";
import Link from "next/link";
import { useEffect, useState } from "react";
import { usePathname, useRouter } from "next/navigation";
/** Volledige desktop-navigatie — logische groepen: overzicht → infra → bibliotheek → live → mensen → systeem */
const desktopLinks = [
{ href: "/dashboard", label: "Dashboard" },
{ href: "/nodes", label: "Nodes" },
{ href: "/movies", label: "Movies" },
{ href: "/series", label: "Series" },
{ href: "/pending", label: "Te koppelen" },
{ href: "/downloads", label: "Downloads" },
{ href: "/streams", label: "Streams" },
{ href: "/live-lists", label: "Live-lijsten" },
{ href: "/drm", label: "DRM" },
{ href: "/scripts", label: "Scripts" },
{ href: "/agent", label: "Agent" },
{ href: "/viewers", label: "Gebruikers" },
{ href: "/settings", label: "Instellingen" },
];
/** Bottom-nav (max 6): Series via drawer/desktop */
const mobileLinks = [
{ href: "/dashboard", label: "Home", short: "Home" },
{ href: "/nodes", label: "Nodes", short: "Nodes" },
{ href: "/movies", label: "Movies", short: "Films" },
{ href: "/downloads", label: "Downloads", short: "DL" },
{ href: "/streams", label: "Streams", short: "Live" },
{ href: "/viewers", label: "Users", short: "Users" },
];
export function Nav() {
const pathname = usePathname();
const router = useRouter();
const [menuOpen, setMenuOpen] = useState(false);
useEffect(() => {
setMenuOpen(false);
}, [pathname]);
useEffect(() => {
if (!menuOpen) return;
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") setMenuOpen(false);
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [menuOpen]);
async function logout() {
await fetch("/api/v1/auth/logout", { method: "POST", credentials: "include" });
router.push("/");
}
const isActive = (href: string) => pathname === href || pathname.startsWith(`${href}/`);
return (
<>
<nav className="topnav">
<Link href="/dashboard" className="brand">
<span className="brand-mark">VMC</span>
<span className="brand-text">
Vonas Media Cluster
<small>ops console</small>
</span>
</Link>
<div className="topnav-links topnav-links-desktop">
{desktopLinks.map((l) => (
<Link key={l.href} href={l.href} className={isActive(l.href) ? "active" : ""}>
{l.label}
</Link>
))}
</div>
<button type="button" className="btn-logout topnav-logout-desktop" onClick={logout}>
Uitloggen
</button>
<button
type="button"
className={`nav-burger${menuOpen ? " open" : ""}`}
aria-label={menuOpen ? "Menu sluiten" : "Menu openen"}
aria-expanded={menuOpen}
onClick={() => setMenuOpen((v) => !v)}
>
<span />
<span />
<span />
</button>
</nav>
{menuOpen && (
<div className="nav-drawer-backdrop" onClick={() => setMenuOpen(false)} aria-hidden />
)}
<aside className={`nav-drawer${menuOpen ? " open" : ""}`} aria-hidden={!menuOpen}>
<div className="nav-drawer-head">
<span className="page-kicker">Navigatie</span>
<button type="button" className="btn-logout" onClick={() => setMenuOpen(false)}>
Sluiten
</button>
</div>
<div className="nav-drawer-links">
{desktopLinks.map((l) => (
<Link
key={l.href}
href={l.href}
className={isActive(l.href) ? "active" : ""}
onClick={() => setMenuOpen(false)}
>
{l.label}
</Link>
))}
</div>
<button type="button" className="btn-logout nav-drawer-logout" onClick={logout}>
Uitloggen
</button>
</aside>
<nav className="bottomnav" aria-label="Hoofdnavigatie">
{mobileLinks.map((l) => (
<Link key={l.href} href={l.href} className={isActive(l.href) ? "active" : ""}>
<span className="bottomnav-dot" aria-hidden />
<span>{l.short}</span>
</Link>
))}
</nav>
</>
);
}
export function useAuth() {
const router = useRouter();
useEffect(() => {
fetch("/api/v1/auth/me", { credentials: "include" })
.then((r) => {
if (!r.ok) router.push("/");
})
.catch(() => router.push("/"));
}, [router]);
}