105 lines
3.2 KiB
TypeScript
105 lines
3.2 KiB
TypeScript
import { NavLink } from "react-router-dom";
|
|
import {
|
|
Activity,
|
|
Anvil,
|
|
Eye,
|
|
Gauge,
|
|
Hammer,
|
|
LogOut,
|
|
Terminal,
|
|
Wifi,
|
|
WifiOff,
|
|
} from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
import { clearStoredAuth } from "@/lib/auth";
|
|
import { useNavigate } from "react-router-dom";
|
|
import type { WsConnectionState } from "@/hooks/useFleetWebSocket";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
const navItems = [
|
|
{ to: "/dashboard", label: "Dashboard", icon: Gauge },
|
|
{ to: "/forge", label: "Forge", icon: Hammer },
|
|
{ to: "/calibrate", label: "Calibrate", icon: Activity },
|
|
{ to: "/crucible", label: "Crucible", icon: Terminal },
|
|
{ to: "/seer", label: "Seer", icon: Eye },
|
|
];
|
|
|
|
interface SidebarProps {
|
|
wsState?: WsConnectionState;
|
|
}
|
|
|
|
export function Sidebar({ wsState = "disconnected" }: SidebarProps) {
|
|
const navigate = useNavigate();
|
|
|
|
const handleLogout = () => {
|
|
clearStoredAuth();
|
|
navigate("/login");
|
|
};
|
|
|
|
const wsBadge =
|
|
wsState === "connected"
|
|
? { variant: "online" as const, label: "WS LIVE", icon: Wifi }
|
|
: wsState === "connecting"
|
|
? { variant: "probing" as const, label: "WS CONNECTING", icon: Wifi }
|
|
: { variant: "offline" as const, label: "WS OFFLINE", icon: WifiOff };
|
|
|
|
const WsIcon = wsBadge.icon;
|
|
|
|
return (
|
|
<aside className="flex h-full w-56 flex-col border-r border-deck-border bg-deck-surface/80 backdrop-blur-sm">
|
|
<div className="border-b border-deck-border p-4">
|
|
<div className="flex items-center gap-2">
|
|
<div className="flex h-8 w-8 items-center justify-center rounded-md border border-deck-accent/40 bg-deck-accent/10">
|
|
<Anvil className="h-4 w-4 text-deck-accent" />
|
|
</div>
|
|
<div>
|
|
<p className="text-sm font-semibold tracking-wide text-deck-text">
|
|
AetherForge
|
|
</p>
|
|
<p className="font-mono text-[10px] uppercase text-deck-muted">
|
|
Command Deck
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<nav className="flex-1 space-y-1 p-3">
|
|
{navItems.map(({ to, label, icon: Icon }) => (
|
|
<NavLink
|
|
key={to}
|
|
to={to}
|
|
className={({ isActive }) =>
|
|
cn(
|
|
"flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
|
|
isActive
|
|
? "bg-deck-accent/10 text-deck-accent border border-deck-accent/30"
|
|
: "text-deck-muted hover:bg-deck-panel hover:text-deck-text",
|
|
)
|
|
}
|
|
>
|
|
<Icon className="h-4 w-4" />
|
|
{label}
|
|
</NavLink>
|
|
))}
|
|
</nav>
|
|
|
|
<div className="space-y-3 border-t border-deck-border p-4">
|
|
<Badge variant={wsBadge.variant} className="w-full justify-center gap-1.5">
|
|
<WsIcon className="h-3 w-3" />
|
|
{wsBadge.label}
|
|
</Badge>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="w-full justify-start text-deck-muted"
|
|
onClick={handleLogout}
|
|
>
|
|
<LogOut className="h-4 w-4" />
|
|
Sign out
|
|
</Button>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|