spb/wp-spboucher-ai
Public
1import Link from "next/link";2import { requireUserPage } from "@/lib/auth/guard";3import { csrfToken, maybeRotate } from "@/lib/auth/session";4import { accountAction } from "@/lib/admin/actions";5export const dynamic = "force-dynamic";6const NAV: [string, string][] = [["/admin", "Dashboard"], ["/admin/papers", "Papers"], ["/admin/ingest", "Ingestion"], ["/admin/taxonomy", "Taxonomy"], ["/admin/analytics", "Analytics"], ["/admin/audit", "Audit log"], ["/admin/system", "System"], ["/admin/settings", "Settings"], ["/admin/account", "Account"]];7export default async function AdminLayout({ children }: { children: React.ReactNode }) {8 const u = await requireUserPage(); await maybeRotate(u);9 return (10 <div className="min-h-dvh grid lg:grid-cols-[14rem_1fr]">11 <aside className="border-b lg:border-b-0 lg:border-r border-line bg-bg-3/50 p-4 lg:sticky lg:top-0 lg:h-dvh overflow-auto">12 <Link href="/" className="serif text-lg text-fg no-underline">Research Working Papers</Link><p className="text-xs text-fg-3 mb-4">Administration · {u.email}</p>13 <nav className="flex lg:flex-col flex-wrap gap-1 text-sm">{NAV.map(([h, l]) => <Link key={h} href={h} prefetch={false} className="px-2.5 py-1.5 rounded-md text-fg-2 no-underline hover:bg-bg-2 hover:text-fg">{l}</Link>)}14 <form action={accountAction} className="mt-2"><input type="hidden" name="_csrf" value={csrfToken(u)} /><input type="hidden" name="op" value="logout" /><button className="px-2.5 py-1.5 rounded-md text-fg-3 hover:bg-bg-2 hover:text-fg w-full text-left">Sign out</button></form>15 </nav>16 {u.mustChangePassword && <p className="mt-4 text-xs text-bad">Change your initial password in Account.</p>}17 {!u.totpEnabled && <p className="mt-2 text-xs text-warn">Two-factor authentication is off. Enable it in Account.</p>}18 </aside>19 <main className="p-4 sm:p-8 min-w-0">{children}</main>20 </div>21 );22}23