2025-04-18 00:07:21 +02:00
|
|
|
"use client"
|
|
|
|
|
|
|
|
|
|
import type * as React from "react"
|
2025-04-11 14:48:12 +02:00
|
|
|
import Image from "next/image"
|
2025-04-18 00:07:21 +02:00
|
|
|
import { usePathname } from "next/navigation"
|
|
|
|
|
import {
|
|
|
|
|
AppWindow,
|
|
|
|
|
Settings,
|
|
|
|
|
LayoutDashboardIcon,
|
|
|
|
|
Briefcase,
|
|
|
|
|
Server,
|
|
|
|
|
Network,
|
|
|
|
|
Activity,
|
|
|
|
|
LogOut,
|
|
|
|
|
ChevronDown,
|
|
|
|
|
} from "lucide-react"
|
2025-04-11 14:48:12 +02:00
|
|
|
import {
|
|
|
|
|
Sidebar,
|
|
|
|
|
SidebarContent,
|
|
|
|
|
SidebarGroup,
|
2025-04-18 00:07:21 +02:00
|
|
|
SidebarGroupContent,
|
|
|
|
|
SidebarGroupLabel,
|
2025-04-11 14:48:12 +02:00
|
|
|
SidebarHeader,
|
|
|
|
|
SidebarMenu,
|
|
|
|
|
SidebarMenuButton,
|
|
|
|
|
SidebarMenuItem,
|
|
|
|
|
SidebarMenuSub,
|
|
|
|
|
SidebarMenuSubButton,
|
|
|
|
|
SidebarMenuSubItem,
|
|
|
|
|
SidebarRail,
|
2025-04-18 00:07:21 +02:00
|
|
|
SidebarFooter,
|
2025-04-11 14:48:12 +02:00
|
|
|
} from "@/components/ui/sidebar"
|
|
|
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
|
import Link from "next/link"
|
2025-04-13 21:10:17 +02:00
|
|
|
import Cookies from "js-cookie"
|
|
|
|
|
import { useRouter } from "next/navigation"
|
2025-04-14 14:55:16 +02:00
|
|
|
import packageJson from "@/package.json"
|
2025-04-18 00:07:21 +02:00
|
|
|
import { cn } from "@/lib/utils"
|
|
|
|
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
|
2025-04-13 21:10:17 +02:00
|
|
|
|
|
|
|
|
interface NavItem {
|
|
|
|
|
title: string
|
|
|
|
|
icon?: React.ComponentType<any>
|
|
|
|
|
url: string
|
|
|
|
|
isActive?: boolean
|
|
|
|
|
items?: NavItem[]
|
|
|
|
|
}
|
2025-04-11 14:48:12 +02:00
|
|
|
|
2025-04-13 21:10:17 +02:00
|
|
|
const data: { navMain: NavItem[] } = {
|
2025-04-11 14:48:12 +02:00
|
|
|
navMain: [
|
|
|
|
|
{
|
2025-04-13 21:10:17 +02:00
|
|
|
title: "Dashboard",
|
|
|
|
|
icon: LayoutDashboardIcon,
|
2025-04-18 00:07:21 +02:00
|
|
|
url: "/dashboard",
|
2025-04-11 14:48:12 +02:00
|
|
|
},
|
|
|
|
|
{
|
2025-04-13 21:10:17 +02:00
|
|
|
title: "My Infrastructure",
|
|
|
|
|
url: "#",
|
|
|
|
|
icon: Briefcase,
|
|
|
|
|
items: [
|
|
|
|
|
{
|
|
|
|
|
title: "Servers",
|
|
|
|
|
icon: Server,
|
|
|
|
|
url: "/dashboard/servers",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Applications",
|
|
|
|
|
icon: AppWindow,
|
|
|
|
|
url: "/dashboard/applications",
|
|
|
|
|
},
|
2025-04-15 12:17:14 +02:00
|
|
|
{
|
2025-04-15 12:20:03 +02:00
|
|
|
title: "Uptime",
|
2025-04-15 12:17:14 +02:00
|
|
|
icon: Activity,
|
2025-04-15 12:20:03 +02:00
|
|
|
url: "/dashboard/uptime",
|
2025-04-15 12:17:14 +02:00
|
|
|
},
|
2025-04-13 21:10:17 +02:00
|
|
|
{
|
|
|
|
|
title: "Network",
|
|
|
|
|
icon: Network,
|
|
|
|
|
url: "/dashboard/network",
|
2025-04-18 00:07:21 +02:00
|
|
|
},
|
2025-04-11 14:48:12 +02:00
|
|
|
],
|
2025-04-12 13:21:03 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Settings",
|
|
|
|
|
icon: Settings,
|
2025-04-12 20:01:43 +02:00
|
|
|
url: "/dashboard/settings",
|
2025-04-11 14:48:12 +02:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
2025-04-13 21:10:17 +02:00
|
|
|
|
2025-04-11 14:48:12 +02:00
|
|
|
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
2025-04-13 21:10:17 +02:00
|
|
|
const router = useRouter()
|
2025-04-18 00:07:21 +02:00
|
|
|
const pathname = usePathname()
|
|
|
|
|
|
2025-04-13 21:10:17 +02:00
|
|
|
const logout = async () => {
|
2025-04-18 00:07:21 +02:00
|
|
|
Cookies.remove("token")
|
2025-04-13 21:10:17 +02:00
|
|
|
router.push("/")
|
|
|
|
|
}
|
2025-04-11 14:50:26 +02:00
|
|
|
|
2025-04-18 00:07:21 +02:00
|
|
|
// Check if a path is active (exact match or starts with path for parent items)
|
|
|
|
|
const isActive = (path: string) => {
|
|
|
|
|
if (path === "#") return false
|
|
|
|
|
return pathname === path || (path !== "/dashboard" && pathname?.startsWith(path))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if any child item is active
|
|
|
|
|
const hasActiveChild = (items?: NavItem[]) => {
|
|
|
|
|
if (!items) return false
|
|
|
|
|
return items.some((item) => isActive(item.url))
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-13 21:10:17 +02:00
|
|
|
return (
|
2025-04-11 14:48:12 +02:00
|
|
|
<Sidebar {...props}>
|
2025-04-18 00:07:21 +02:00
|
|
|
<SidebarHeader className="border-b border-sidebar-border/30 pb-2">
|
2025-04-11 14:48:12 +02:00
|
|
|
<SidebarMenu>
|
|
|
|
|
<SidebarMenuItem>
|
2025-04-18 00:07:21 +02:00
|
|
|
<SidebarMenuButton size="lg" asChild className="gap-3">
|
|
|
|
|
<a href="https://github.com/crocofied/corecontrol" className="transition-all hover:opacity-80">
|
|
|
|
|
<div className="flex items-center justify-center rounded-lg overflow-hidden bg-gradient-to-br from-teal-500 to-emerald-600 shadow-sm">
|
|
|
|
|
<Image src="/logo.png" width={48} height={48} alt="CoreControl Logo" className="object-cover" />
|
|
|
|
|
</div>
|
2025-04-11 14:48:12 +02:00
|
|
|
<div className="flex flex-col gap-0.5 leading-none">
|
2025-04-18 00:07:21 +02:00
|
|
|
<span className="font-semibold text-base">CoreControl</span>
|
|
|
|
|
<span className="text-xs text-sidebar-foreground/70">v{packageJson.version}</span>
|
2025-04-11 14:48:12 +02:00
|
|
|
</div>
|
|
|
|
|
</a>
|
|
|
|
|
</SidebarMenuButton>
|
|
|
|
|
</SidebarMenuItem>
|
|
|
|
|
</SidebarMenu>
|
|
|
|
|
</SidebarHeader>
|
2025-04-18 00:07:21 +02:00
|
|
|
|
|
|
|
|
<SidebarContent className="flex flex-col h-full py-4">
|
2025-04-11 14:48:12 +02:00
|
|
|
<SidebarGroup className="flex-grow">
|
2025-04-18 00:07:21 +02:00
|
|
|
<SidebarGroupLabel className="text-xs font-medium text-sidebar-foreground/60 uppercase tracking-wider px-4 mb-2">
|
|
|
|
|
Main Navigation
|
|
|
|
|
</SidebarGroupLabel>
|
|
|
|
|
<SidebarGroupContent>
|
|
|
|
|
<SidebarMenu>
|
|
|
|
|
{data.navMain.map((item) =>
|
|
|
|
|
item.items?.length ? (
|
|
|
|
|
<Collapsible key={item.title} defaultOpen={hasActiveChild(item.items)} className="group/collapsible">
|
|
|
|
|
<SidebarMenuItem>
|
|
|
|
|
<CollapsibleTrigger asChild>
|
|
|
|
|
<SidebarMenuButton
|
|
|
|
|
className={cn(
|
|
|
|
|
"font-medium transition-all",
|
|
|
|
|
(hasActiveChild(item.items) || isActive(item.url)) &&
|
|
|
|
|
"text-sidebar-accent-foreground bg-sidebar-accent/50",
|
|
|
|
|
)}
|
2025-04-13 21:10:17 +02:00
|
|
|
>
|
2025-04-18 00:07:21 +02:00
|
|
|
{item.icon && <item.icon className="h-4 w-4" />}
|
|
|
|
|
<span>{item.title}</span>
|
|
|
|
|
<ChevronDown className="ml-auto h-4 w-4 transition-transform group-data-[state=open]/collapsible:rotate-180" />
|
|
|
|
|
</SidebarMenuButton>
|
|
|
|
|
</CollapsibleTrigger>
|
|
|
|
|
<CollapsibleContent>
|
|
|
|
|
<SidebarMenuSub>
|
|
|
|
|
{item.items.map((subItem) => (
|
|
|
|
|
<SidebarMenuSubItem key={subItem.title}>
|
|
|
|
|
<SidebarMenuSubButton asChild isActive={isActive(subItem.url)} className="transition-all">
|
|
|
|
|
<Link href={subItem.url} className="flex items-center">
|
|
|
|
|
{subItem.icon && <subItem.icon className="h-3.5 w-3.5 mr-2" />}
|
|
|
|
|
<span>{subItem.title}</span>
|
|
|
|
|
</Link>
|
|
|
|
|
</SidebarMenuSubButton>
|
|
|
|
|
</SidebarMenuSubItem>
|
|
|
|
|
))}
|
|
|
|
|
</SidebarMenuSub>
|
|
|
|
|
</CollapsibleContent>
|
|
|
|
|
</SidebarMenuItem>
|
|
|
|
|
</Collapsible>
|
|
|
|
|
) : (
|
|
|
|
|
<SidebarMenuItem key={item.title}>
|
|
|
|
|
<SidebarMenuButton
|
|
|
|
|
asChild
|
|
|
|
|
className={cn(
|
|
|
|
|
"font-medium transition-all",
|
|
|
|
|
isActive(item.url) && "text-sidebar-accent-foreground bg-sidebar-accent/50",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<Link href={item.url}>
|
|
|
|
|
{item.icon && <item.icon className="h-4 w-4" />}
|
|
|
|
|
<span>{item.title}</span>
|
|
|
|
|
</Link>
|
|
|
|
|
</SidebarMenuButton>
|
|
|
|
|
</SidebarMenuItem>
|
|
|
|
|
),
|
|
|
|
|
)}
|
|
|
|
|
</SidebarMenu>
|
|
|
|
|
</SidebarGroupContent>
|
2025-04-11 14:48:12 +02:00
|
|
|
</SidebarGroup>
|
|
|
|
|
|
2025-04-18 00:07:21 +02:00
|
|
|
<SidebarFooter className="border-t border-sidebar-border/30 pt-4 mt-auto">
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
className="w-full justify-start text-destructive hover:text-destructive hover:bg-destructive/10 border-none shadow-none"
|
|
|
|
|
onClick={logout}
|
|
|
|
|
>
|
|
|
|
|
<LogOut className="h-4 w-4 mr-2" />
|
2025-04-11 14:48:12 +02:00
|
|
|
Logout
|
2025-04-13 21:10:17 +02:00
|
|
|
</Button>
|
2025-04-18 00:07:21 +02:00
|
|
|
</SidebarFooter>
|
2025-04-13 21:10:17 +02:00
|
|
|
</SidebarContent>
|
2025-04-11 14:48:12 +02:00
|
|
|
|
|
|
|
|
<SidebarRail />
|
|
|
|
|
</Sidebar>
|
|
|
|
|
)
|
2025-04-18 00:07:21 +02:00
|
|
|
}
|