"use client"; import React, { useState, useEffect } from "react"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; import { useQuery } from "@tanstack/react-query"; import { cn } from "@/lib/utils"; import { authClient } from "@/lib/auth/client"; import { LayoutDashboard, Terminal, Activity, Clock, Users, Map, Puzzle, FolderOpen, Archive, Settings, RefreshCw, UserPlus, ScrollText, LogOut, ChevronRight, } from "lucide-react"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- interface NavItem { label: string; href: string; icon: React.ElementType; } interface NavSection { title: string; items: NavItem[]; } interface ServerStatus { online: boolean; status: "online" | "offline" | "starting" | "stopping"; } // --------------------------------------------------------------------------- // Navigation structure // --------------------------------------------------------------------------- const NAV_SECTIONS: NavSection[] = [ { title: "Overview", items: [ { label: "Dashboard", href: "/dashboard", icon: LayoutDashboard }, ], }, { title: "Server", items: [ { label: "Console", href: "/console", icon: Terminal }, { label: "Monitoring", href: "/monitoring", icon: Activity }, { label: "Scheduler", href: "/scheduler", icon: Clock }, ], }, { title: "World", items: [ { label: "Players", href: "/players", icon: Users }, { label: "Map", href: "/map", icon: Map }, ], }, { title: "Manage", items: [ { label: "Plugins", href: "/plugins", icon: Puzzle }, { label: "Files", href: "/files", icon: FolderOpen }, { label: "Backups", href: "/backups", icon: Archive }, { label: "Settings", href: "/settings", icon: Settings }, { label: "Updates", href: "/updates", icon: RefreshCw }, ], }, { title: "Admin", items: [ { label: "Team", href: "/team", icon: UserPlus }, { label: "Audit Log", href: "/audit", icon: ScrollText }, ], }, ]; // --------------------------------------------------------------------------- // CubeAdmin Logo SVG // --------------------------------------------------------------------------- function CubeIcon({ className }: { className?: string }) { return ( ); } // --------------------------------------------------------------------------- // Server status indicator // --------------------------------------------------------------------------- function ServerStatusBadge({ collapsed }: { collapsed: boolean }) { const { data: status } = useQuery({ queryKey: ["server-status"], queryFn: async () => { const res = await fetch("/api/server/status"); if (!res.ok) return { online: false, status: "offline" as const }; return res.json(); }, refetchInterval: 10_000, staleTime: 8_000, }); const isOnline = status?.online ?? false; const label = status?.status ? status.status.charAt(0).toUpperCase() + status.status.slice(1) : "Unknown"; const dotColor = { online: "bg-emerald-500", offline: "bg-red-500", starting: "bg-yellow-500 animate-pulse", stopping: "bg-orange-500 animate-pulse", }[status?.status ?? "offline"] ?? "bg-zinc-500"; if (collapsed) { return (
Server: {label}
); } return (
{label}
); } // --------------------------------------------------------------------------- // Individual nav item // --------------------------------------------------------------------------- function NavLink({ item, isActive, collapsed, }: { item: NavItem; isActive: boolean; collapsed: boolean; }) { const Icon = item.icon; const linkContent = ( {!collapsed && ( {item.label} )} {!collapsed && isActive && ( )} ); if (collapsed) { return ( {linkContent} {item.label} ); } return linkContent; } // --------------------------------------------------------------------------- // Sidebar // --------------------------------------------------------------------------- export function Sidebar() { const pathname = usePathname(); const router = useRouter(); const [collapsed, setCollapsed] = useState(false); const [mounted, setMounted] = useState(false); // Responsive: auto-collapse on small screens useEffect(() => { setMounted(true); const mq = window.matchMedia("(max-width: 1023px)"); const handler = (e: MediaQueryListEvent) => setCollapsed(e.matches); setCollapsed(mq.matches); mq.addEventListener("change", handler); return () => mq.removeEventListener("change", handler); }, []); const { data: session } = useQuery({ queryKey: ["session"], queryFn: async () => { const { data } = await authClient.getSession(); return data; }, staleTime: 5 * 60_000, }); async function handleLogout() { await authClient.signOut(); router.push("/login"); } if (!mounted) return null; const sidebarWidth = collapsed ? "w-[60px]" : "w-[240px]"; return ( ); }