121 lines
3.2 KiB
TypeScript
121 lines
3.2 KiB
TypeScript
|
|
import {
|
||
|
|
LayoutDashboard,
|
||
|
|
Container,
|
||
|
|
MemoryStick,
|
||
|
|
HardDrive,
|
||
|
|
Wifi,
|
||
|
|
Settings,
|
||
|
|
HelpCircle,
|
||
|
|
Server,
|
||
|
|
} from "lucide-react"
|
||
|
|
import { cn } from "@/lib/utils"
|
||
|
|
import { Separator } from "@/components/ui/separator"
|
||
|
|
|
||
|
|
interface NavItem {
|
||
|
|
id: string
|
||
|
|
label: string
|
||
|
|
icon: React.ElementType
|
||
|
|
}
|
||
|
|
|
||
|
|
const mainNav: NavItem[] = [
|
||
|
|
{ id: "dashboard", label: "Dashboard", icon: LayoutDashboard },
|
||
|
|
{ id: "containers", label: "Containers", icon: Container },
|
||
|
|
{ id: "memory", label: "Memory", icon: MemoryStick },
|
||
|
|
{ id: "storage", label: "Storage", icon: HardDrive },
|
||
|
|
{ id: "network", label: "Network", icon: Wifi },
|
||
|
|
]
|
||
|
|
|
||
|
|
const bottomNav: NavItem[] = [
|
||
|
|
{ id: "settings", label: "Settings", icon: Settings },
|
||
|
|
{ id: "help", label: "Help & Support", icon: HelpCircle },
|
||
|
|
]
|
||
|
|
|
||
|
|
interface SidebarProps {
|
||
|
|
activePage: string
|
||
|
|
onNavigate: (page: string) => void
|
||
|
|
}
|
||
|
|
|
||
|
|
function NavButton({
|
||
|
|
item,
|
||
|
|
active,
|
||
|
|
onClick,
|
||
|
|
}: {
|
||
|
|
item: NavItem
|
||
|
|
active: boolean
|
||
|
|
onClick: () => void
|
||
|
|
}) {
|
||
|
|
const Icon = item.icon
|
||
|
|
return (
|
||
|
|
<button
|
||
|
|
onClick={onClick}
|
||
|
|
className={cn(
|
||
|
|
"flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors",
|
||
|
|
active
|
||
|
|
? "bg-primary text-primary-foreground"
|
||
|
|
: "text-muted-foreground hover:bg-muted hover:text-foreground"
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
<Icon className="size-5 shrink-0" />
|
||
|
|
<span>{item.label}</span>
|
||
|
|
</button>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function Sidebar({ activePage, onNavigate }: SidebarProps) {
|
||
|
|
return (
|
||
|
|
<aside className="flex h-screen w-60 shrink-0 flex-col border-r bg-sidebar-background">
|
||
|
|
{/* Brand */}
|
||
|
|
<div className="flex items-center gap-3 px-5 py-6">
|
||
|
|
<div className="flex size-9 items-center justify-center rounded-lg bg-primary">
|
||
|
|
<Server className="size-5 text-primary-foreground" />
|
||
|
|
</div>
|
||
|
|
<span className="text-lg font-bold tracking-tight text-foreground">
|
||
|
|
Eventify
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Main nav */}
|
||
|
|
<nav className="flex flex-1 flex-col gap-1 px-3">
|
||
|
|
{mainNav.map((item) => (
|
||
|
|
<NavButton
|
||
|
|
key={item.id}
|
||
|
|
item={item}
|
||
|
|
active={activePage === item.id}
|
||
|
|
onClick={() => onNavigate(item.id)}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
|
||
|
|
<Separator className="my-4" />
|
||
|
|
|
||
|
|
{bottomNav.map((item) => (
|
||
|
|
<NavButton
|
||
|
|
key={item.id}
|
||
|
|
item={item}
|
||
|
|
active={activePage === item.id}
|
||
|
|
onClick={() => onNavigate(item.id)}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
|
||
|
|
{/* Spacer */}
|
||
|
|
<div className="flex-1" />
|
||
|
|
|
||
|
|
{/* Status card */}
|
||
|
|
<div className="mb-4 rounded-xl border bg-muted/40 p-4">
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
<span className="relative flex size-2.5">
|
||
|
|
<span className="absolute inline-flex size-full animate-ping rounded-full bg-green-400 opacity-75" />
|
||
|
|
<span className="relative inline-flex size-2.5 rounded-full bg-green-500" />
|
||
|
|
</span>
|
||
|
|
<span className="text-xs font-semibold text-foreground">
|
||
|
|
Server Status
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<p className="mt-2 text-xs text-muted-foreground">
|
||
|
|
All Systems Operational
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</nav>
|
||
|
|
</aside>
|
||
|
|
)
|
||
|
|
}
|