Initial commit: AetherForge Linux (forge-mesh) v0.1.0-dev
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
This commit is contained in:
104
web/src/components/layout/Sidebar.tsx
Normal file
104
web/src/components/layout/Sidebar.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user