"use client"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { ArrowLeft } from "lucide-react"; import type { ApiUser } from "@ttsa/shared"; /** * Admin sidebar shell: fixed left nav + scrollable content. Uses the app's * design tokens (card/ink/accent/line). Active link is derived from the path. */ const NAV = [ { href: "/admin", label: "Overview", exact: true }, { href: "/admin/models", label: "Models" }, { href: "/admin/users", label: "Users" }, { href: "/admin/votes", label: "Votes" }, { href: "/admin/security", label: "Security" }, { href: "/admin/errors", label: "Errors" }, { href: "/admin/generations", label: "Generations" }, { href: "/admin/analytics", label: "Analytics" }, ]; export function AdminShell({ user, children, }: { user: ApiUser; children: React.ReactNode; }) { const pathname = usePathname(); const isActive = (href: string, exact?: boolean) => exact ? pathname === href : pathname === href || pathname.startsWith(href + "/"); return (
{/* Sidebar */} {/* Content */}
{/* Mobile top nav */}
{NAV.map((item) => { const on = isActive(item.href, item.exact); return ( {item.label} ); })}
{children}
); } /* ── Small shared layout primitives reused across admin pages ─────────── */ export function PageHeader({ title, subtitle, actions, }: { title: string; subtitle?: string; actions?: React.ReactNode; }) { return (

{title}

{subtitle &&

{subtitle}

}
{actions}
); } export function StatCard({ label, value, hint, }: { label: string; value: React.ReactNode; hint?: string; }) { return (

{label}

{value}

{hint &&

{hint}

}
); }