95 lines
3.8 KiB
TypeScript
95 lines
3.8 KiB
TypeScript
import { useState } from "react";
|
|
import { Outlet, useLocation } from "react-router";
|
|
import AdminSidebar from "@/components/AdminSidebar";
|
|
import ServerActivity from "@/components/ServerActivity";
|
|
import { RealtimeEventsProvider } from "@/components/RealtimeEventsProvider";
|
|
import { useEventChannel } from "@/components/realtimeEventsContext";
|
|
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
|
import { useDocumentTitle } from "@/hooks/useDocumentTitle";
|
|
import { resolveAdminDocumentTitle } from "@/lib/documentTitle";
|
|
import { Menu } from "lucide-react";
|
|
import { useWatchPlaybackController } from "@/playback/watchPlaybackContext";
|
|
|
|
export default function AdminLayout() {
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
const location = useLocation();
|
|
const { isBackgroundBarVisible } = useWatchPlaybackController();
|
|
|
|
useDocumentTitle(resolveAdminDocumentTitle(location.pathname));
|
|
|
|
return (
|
|
<RealtimeEventsProvider>
|
|
<AdminLayoutEventChannels />
|
|
<div className="bg-background relative min-h-[100dvh] overflow-x-hidden">
|
|
<a
|
|
href="#main-content"
|
|
className="focus:bg-background focus:text-foreground focus:ring-ring sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50 focus:rounded-lg focus:px-4 focus:py-2 focus:text-sm focus:font-medium focus:ring-2 focus:outline-none"
|
|
>
|
|
Skip to content
|
|
</a>
|
|
<div className="from-primary/6 pointer-events-none fixed inset-x-0 top-0 z-0 h-40 bg-gradient-to-b to-transparent blur-3xl" />
|
|
{/* Desktop sidebar */}
|
|
<div className="hidden lg:block">
|
|
<AdminSidebar />
|
|
</div>
|
|
|
|
{/* Mobile header */}
|
|
<div className="glass-dark border-border/70 sticky top-0 z-30 mx-3 mt-3 flex items-center justify-between rounded-2xl border px-4 py-3 lg:hidden">
|
|
<div className="flex items-center gap-3">
|
|
<button
|
|
onClick={() => setMobileOpen(true)}
|
|
className="text-muted-foreground hover:text-foreground hover:bg-accent/60 flex h-10 w-10 items-center justify-center rounded-xl transition-all"
|
|
aria-label="Open admin menu"
|
|
>
|
|
<Menu className="h-5 w-5" />
|
|
</button>
|
|
<div className="flex items-center gap-2">
|
|
<div className="text-primary border-border/70 bg-surface flex h-9 w-9 items-center justify-center rounded-xl border text-sm font-bold">
|
|
▶
|
|
</div>
|
|
<span className="text-[15px] font-extrabold tracking-tight">Admin</span>
|
|
</div>
|
|
</div>
|
|
<ServerActivity />
|
|
</div>
|
|
|
|
{/* Desktop activity indicator */}
|
|
<div className="fixed top-5 right-5 z-40 hidden lg:block">
|
|
<ServerActivity />
|
|
</div>
|
|
|
|
{/* Mobile sidebar drawer */}
|
|
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
|
|
<SheetContent side="left" className="w-[260px] p-0 sm:max-w-[260px]">
|
|
<SheetHeader className="sr-only">
|
|
<SheetTitle>Admin Navigation</SheetTitle>
|
|
</SheetHeader>
|
|
<AdminSidebar onNavigate={() => setMobileOpen(false)} />
|
|
</SheetContent>
|
|
</Sheet>
|
|
|
|
<main
|
|
id="main-content"
|
|
className={`relative z-10 min-h-screen px-4 py-4 sm:px-6 lg:ml-[240px] lg:px-8 lg:py-8 xl:px-10 ${
|
|
isBackgroundBarVisible ? "pb-32 sm:pb-36" : ""
|
|
}`}
|
|
>
|
|
<div className="admin-shell">
|
|
<Outlet />
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</RealtimeEventsProvider>
|
|
);
|
|
}
|
|
|
|
function AdminLayoutEventChannels() {
|
|
useEventChannel("catalog");
|
|
useEventChannel("user_state");
|
|
useEventChannel("jobs");
|
|
useEventChannel("sessions");
|
|
useEventChannel("tasks");
|
|
useEventChannel("scans");
|
|
return null;
|
|
}
|