'use client'; import Link from 'next/link'; import { usePathname, useRouter } from 'next/navigation'; import { motion } from 'motion/react'; import { Workflow, Activity, CreditCard, LayoutDashboard, Package, Store, Bot, FileText, KeyRound, LogOut, ShieldAlert, UserRound, } from 'lucide-react'; import { BrandMark } from '@/components/brand/BrandMark'; import { Avatar } from '@/components/ui/Avatar'; import { Badge } from '@/components/ui/Badge'; import { canReachBilling } from '@/lib/billing-access'; import { cn } from '@/lib/cn'; import { MODULE_COLOR, type ModuleKey } from '@/lib/module-colors'; import { SPRING, useReducedMotion } from '@/lib/motion'; import { useAuthStore } from '@/stores/auth'; export const NAV: { href: string; label: string; icon: typeof Workflow; module: ModuleKey; /** Rendered as a non-clickable "Soon" row instead of a link. */ comingSoon?: boolean; }[] = [ { href: '/dashboard', label: 'Dashboard', icon: LayoutDashboard, module: 'dashboard', }, { href: '/marketplace', label: 'Marketplace', icon: Store, module: 'marketplace', }, // Shares the marketplace hue: a plugin catalog is a marketplace surface, and // a new ModuleKey costs a hand-written Tailwind literal set mirrored twice. { href: '/plugins', label: 'Plugins', icon: Package, module: 'marketplace' }, { href: '/architect', label: 'Architect', icon: Workflow, module: 'architect', }, { href: '/traces', label: 'Traces', icon: Activity, module: 'traces' }, { href: '/agents', label: 'Agents', icon: Bot, module: 'agents' }, { href: '/documents', label: 'Documents', icon: FileText, module: 'documents', }, { href: '/settings/api-keys', label: 'API Keys', icon: KeyRound, module: 'api-keys', }, { href: '/settings/billing', label: 'Billing', icon: CreditCard, module: 'billing', }, { href: '/settings/profile', label: 'Profile', icon: UserRound, module: 'profile', }, ]; // The admin surface is appended to the nav only for admins. Exported so TopBar // can resolve its section label without duplicating the definition. export const ADMIN_LINK: (typeof NAV)[number] = { href: '/admin', label: 'Admin', icon: ShieldAlert, module: 'admin', }; export function Sidebar() { const pathname = usePathname(); const router = useRouter(); const logout = useAuthStore((s) => s.logout); const user = useAuthStore((s) => s.user); const reduced = useReducedMotion(); // Paid billing is parked for ordinary accounts; admins keep it live so the // operator can test the real flow. Same predicate the backend enforces. const billingOpen = canReachBilling(user); const withBilling = NAV.map((item) => item.href === '/settings/billing' && !billingOpen ? { ...item, comingSoon: true } : item, ); const nav = user?.role === 'admin' ? [...withBilling, ADMIN_LINK] : withBilling; const onLogout = () => { // Not awaited: local state is cleared synchronously inside `logout`, and // the server-side revocation it fires is `keepalive`, so navigating away // immediately cannot cancel it. void logout(); router.replace('/login'); }; return ( ); }