mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 09:29:38 +02:00
169 lines
5.3 KiB
TypeScript
169 lines
5.3 KiB
TypeScript
|
|
import { NavLink } from "react-router";
|
||
|
|
import {
|
||
|
|
MessageSquareText,
|
||
|
|
LibraryBig,
|
||
|
|
Rotate3d,
|
||
|
|
Workflow,
|
||
|
|
Settings,
|
||
|
|
TestTube2,
|
||
|
|
Wifi,
|
||
|
|
WifiOff,
|
||
|
|
Database,
|
||
|
|
ChevronDown,
|
||
|
|
} from "lucide-react";
|
||
|
|
import { cn } from "@/lib/utils";
|
||
|
|
import { useConnectionState } from "@/providers/socket-provider";
|
||
|
|
import { useSessionStore } from "@/hooks/use-session-store";
|
||
|
|
import { useFlows } from "@/hooks/use-flows";
|
||
|
|
import { useSettings } from "@/providers/settings-provider";
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Nav item
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
interface NavItemProps {
|
||
|
|
to: string;
|
||
|
|
icon: React.ElementType;
|
||
|
|
label: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
function NavItem({ to, icon: Icon, label }: NavItemProps) {
|
||
|
|
return (
|
||
|
|
<NavLink to={to} className="w-full">
|
||
|
|
{({ isActive }) => (
|
||
|
|
<div
|
||
|
|
className={cn(
|
||
|
|
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||
|
|
isActive
|
||
|
|
? "bg-brand-600/20 text-brand-400"
|
||
|
|
: "text-fg-muted hover:bg-surface-200 hover:text-fg",
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
<Icon className="h-4 w-4 shrink-0" />
|
||
|
|
<span className="truncate">{label}</span>
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</NavLink>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Connection status badge
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
function ConnectionBadge() {
|
||
|
|
const state = useConnectionState();
|
||
|
|
|
||
|
|
const isConnected =
|
||
|
|
state.status === "connected" ||
|
||
|
|
state.status === "authenticated" ||
|
||
|
|
state.status === "unauthenticated";
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className={cn(
|
||
|
|
"flex items-center gap-2 rounded-lg px-3 py-2 text-xs font-medium",
|
||
|
|
isConnected ? "text-success" : "text-fg-subtle",
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
<span
|
||
|
|
className={cn(
|
||
|
|
"h-2 w-2 shrink-0 rounded-full",
|
||
|
|
isConnected ? "bg-success animate-pulse" : "bg-fg-subtle",
|
||
|
|
)}
|
||
|
|
/>
|
||
|
|
{isConnected ? (
|
||
|
|
<Wifi className="h-3.5 w-3.5" />
|
||
|
|
) : (
|
||
|
|
<WifiOff className="h-3.5 w-3.5" />
|
||
|
|
)}
|
||
|
|
<span className="truncate capitalize">{state.status}</span>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Flow selector dropdown
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
function FlowSelectorDropdown() {
|
||
|
|
const { flows } = useFlows();
|
||
|
|
const flowId = useSessionStore((s) => s.flowId);
|
||
|
|
const setFlowId = useSessionStore((s) => s.setFlowId);
|
||
|
|
const collection = useSettings((s) => s.settings.collection);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-2 px-3">
|
||
|
|
{/* Flow selector */}
|
||
|
|
<div className="space-y-1">
|
||
|
|
<label className="flex items-center gap-1.5 text-[10px] font-medium uppercase tracking-wider text-fg-subtle">
|
||
|
|
<Workflow className="h-3 w-3" />
|
||
|
|
Flow
|
||
|
|
</label>
|
||
|
|
<div className="relative">
|
||
|
|
<select
|
||
|
|
value={flowId}
|
||
|
|
onChange={(e) => setFlowId(e.target.value)}
|
||
|
|
className="w-full appearance-none rounded-md border border-border bg-surface-100 py-1.5 pl-2.5 pr-7 text-xs text-fg focus:border-brand-500 focus:outline-none focus:ring-1 focus:ring-brand-500"
|
||
|
|
>
|
||
|
|
<option value="default">default</option>
|
||
|
|
{flows.map((f) => (
|
||
|
|
<option key={f.id} value={f.id}>
|
||
|
|
{f.id}
|
||
|
|
</option>
|
||
|
|
))}
|
||
|
|
</select>
|
||
|
|
<ChevronDown className="pointer-events-none absolute right-2 top-1/2 h-3 w-3 -translate-y-1/2 text-fg-subtle" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Collection badge */}
|
||
|
|
<div className="flex items-center gap-1.5 rounded-md bg-surface-100 px-2.5 py-1.5 text-xs text-fg-muted">
|
||
|
|
<Database className="h-3 w-3 shrink-0 text-fg-subtle" />
|
||
|
|
<span className="truncate">{collection}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Sidebar
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export function Sidebar() {
|
||
|
|
return (
|
||
|
|
<aside className="flex h-screen w-sidebar shrink-0 flex-col border-r border-border bg-surface-50">
|
||
|
|
{/* Logo area */}
|
||
|
|
<div className="flex h-14 items-center gap-2 px-4">
|
||
|
|
<TestTube2 className="h-5 w-5 text-brand-500" />
|
||
|
|
<span className="text-lg font-bold text-fg">TrustGraph</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Divider */}
|
||
|
|
<div className="mx-3 border-t border-border" />
|
||
|
|
|
||
|
|
{/* Flow & collection selectors */}
|
||
|
|
<div className="py-3">
|
||
|
|
<FlowSelectorDropdown />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Divider */}
|
||
|
|
<div className="mx-3 border-t border-border" />
|
||
|
|
|
||
|
|
{/* Navigation links */}
|
||
|
|
<nav className="flex flex-1 flex-col gap-0.5 overflow-y-auto px-2 py-3">
|
||
|
|
<NavItem to="/chat" icon={MessageSquareText} label="Chat" />
|
||
|
|
<NavItem to="/library" icon={LibraryBig} label="Library" />
|
||
|
|
<NavItem to="/graph" icon={Rotate3d} label="Graph" />
|
||
|
|
<NavItem to="/flows" icon={Workflow} label="Flows" />
|
||
|
|
<NavItem to="/settings" icon={Settings} label="Settings" />
|
||
|
|
</nav>
|
||
|
|
|
||
|
|
{/* Footer: connection badge */}
|
||
|
|
<div className="border-t border-border px-2 py-2">
|
||
|
|
<ConnectionBadge />
|
||
|
|
</div>
|
||
|
|
</aside>
|
||
|
|
);
|
||
|
|
}
|