Files
shadow-gunbuilder-ai-proto/app/admin/layout.tsx
T
2025-12-30 17:22:02 -05:00

146 lines
4.2 KiB
TypeScript

"use client";
import type React from "react";
import { useEffect, useMemo, useState } from "react";
import { usePathname, useRouter } from "next/navigation";
import AdminLeftNavigation, {
type AdminNavGroup,
} from "@/components/AdminLeftNavigation";
import { useAuth } from "@/context/AuthContext";
import {
LayoutDashboard,
Download,
Layers,
Boxes,
Store,
Users,
Settings,
Mail,
Wand2,
FolderKanban,
Shield,
} from "lucide-react";
const navGroups: AdminNavGroup[] = [
{
label: "Overview",
icon: <LayoutDashboard className="h-4 w-4" />,
items: [{ label: "Dashboard", href: "/admin", icon: <LayoutDashboard className="h-4 w-4" /> }],
},
{
label: "Catalog",
icon: <FolderKanban className="h-4 w-4" />,
items: [
{ label: "Products", href: "/admin/products", icon: <Boxes className="h-4 w-4" /> },
{ label: "Platforms", href: "/admin/platforms", icon: <Layers className="h-4 w-4" /> },
],
},
{
label: "Data Ops",
icon: <Download className="h-4 w-4" />,
items: [
{ label: "Imports", href: "/admin/import-status", icon: <Download className="h-4 w-4" /> },
{ label: "Mappings", href: "/admin/mapping", icon: <Layers className="h-4 w-4" /> },
{ label: "Enrichment", href: "/admin/enrichment", icon: <Wand2 className="h-4 w-4" /> },
],
},
{
label: "Growth & Comms",
icon: <Mail className="h-4 w-4" />,
items: [
{ label: "Beta Invites", href: "/admin/beta-invites", icon: <Mail className="h-4 w-4" /> },
{ label: "List Emails", href: "/admin/email", icon: <Mail className="h-4 w-4" /> },
{ label: "Send an Email", href: "/admin/email/send", icon: <Mail className="h-4 w-4" /> },
],
},
{
label: "Access & Config",
icon: <Shield className="h-4 w-4" />,
items: [
{ label: "Users", href: "/admin/users", icon: <Users className="h-4 w-4" /> },
{ label: "Merchants", href: "/admin/merchants", icon: <Store className="h-4 w-4" /> },
{ label: "Settings", href: "/admin/settings", icon: <Settings className="h-4 w-4" /> },
],
},
];
export default function AdminLayout({
children,
}: {
children: React.ReactNode;
}) {
const [collapsed, setCollapsed] = useState(false);
const { user, loading } = useAuth();
const router = useRouter();
const pathname = usePathname();
// Where to send people back after login
const next = useMemo(
() => encodeURIComponent(pathname || "/admin"),
[pathname]
);
/**
* ✅ AUTH GUARD
* Redirects happen in useEffect (NOT during render)
*/
useEffect(() => {
if (loading) return;
// Not logged in
if (!user) {
router.replace(`/login?next=${next}`);
return;
}
// Logged in but not admin
if (user.role !== "ADMIN") {
router.replace("/");
}
}, [loading, user, router, next]);
// While loading OR redirecting, render nothing
if (loading) return null;
if (!user) return null;
if (user.role !== "ADMIN") return null;
return (
<div className="flex min-h-screen bg-black text-zinc-50">
<AdminLeftNavigation
collapsed={collapsed}
onToggleCollapsed={() => setCollapsed((v) => !v)}
groups={navGroups}
/>
{/* Main column */}
<div className="flex min-h-screen flex-1 flex-col">
{/* Top bar */}
<header className="flex items-center justify-between border-b border-zinc-900 bg-zinc-950/70 px-4 py-3">
<div>
<p className="text-xs uppercase tracking-[0.18em] text-zinc-500">
Admin
</p>
<p className="text-sm text-zinc-200">
Battl Builders Control Panel
</p>
</div>
<div className="flex items-center gap-3">
<span className="rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] font-medium text-amber-300">
Internal v0.1
</span>
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-zinc-900 text-[11px] text-zinc-300">
ADMIN
</div>
</div>
</header>
{/* Content */}
<main className="mx-auto flex w-full max-w-6xl flex-1 flex-col px-4 py-6">
{children}
</main>
</div>
</div>
);
}