flowstate / src /components /layout /Sidebar.tsx
muthuk1's picture
feat: recovery attribution, pre-churn warnings, recovery card, threshold editor, telegram alerts
f667d47
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { cn } from '@/lib/utils'
import { LayoutDashboard, Trophy, Megaphone, BarChart3, Wallet, Bot, Zap, Shield, ChevronLeft, ChevronRight, Settings } from 'lucide-react'
import { useState, useEffect } from 'react'
const nav = [
{ label: 'Dashboard', href: '/', icon: LayoutDashboard },
{ label: 'Leaderboard', href: '/leaderboard', icon: Trophy },
{ label: 'Campaigns', href: '/campaigns', icon: Megaphone },
{ label: 'Analytics', href: '/analytics', icon: BarChart3 },
{ label: 'Wallets', href: '/wallets', icon: Wallet },
{ label: 'AI Agent', href: '/agent', icon: Bot },
{ label: 'Settings', href: '/settings', icon: Settings },
]
export function Sidebar() {
const path = usePathname()
const [col, setCol] = useState(false)
const [eventCount, setEventCount] = useState<number | null>(null)
useEffect(() => {
const fetchCount = async () => {
try {
const res = await fetch('/api/torque/events/recent?limit=1')
if (res.ok) {
const data = await res.json()
setEventCount(data.total ?? 0)
}
} catch {}
}
fetchCount()
const i = setInterval(fetchCount, 5000)
return () => clearInterval(i)
}, [])
return (
<aside className={cn('hidden md:flex flex-col border-r border-hairline-dark bg-surface-card transition-all duration-300', col ? 'w-[68px]' : 'w-[240px]')}>
<div className="flex items-center h-16 px-4 border-b border-hairline-dark">
<div className="flex items-center gap-2.5 overflow-hidden">
<div className="w-8 h-8 rounded-lg bg-brand-yellow flex items-center justify-center flex-shrink-0"><Zap className="w-5 h-5 text-ink" /></div>
{!col && <span className="text-title-sm text-brand-yellow font-bold tracking-tight animate-slide-in-right">FlowState</span>}
</div>
</div>
<nav className="flex-1 py-4 px-2 space-y-1">
{nav.map(item => {
const active = path === item.href || (item.href !== '/' && path.startsWith(item.href))
return (
<Link key={item.href} href={item.href} className={cn('flex items-center gap-3 px-3 py-2.5 rounded-lg text-nav transition-all duration-200', active ? 'bg-brand-yellow/10 text-brand-yellow' : 'text-muted hover:text-[#eaecef] hover:bg-surface-elevated', col && 'justify-center px-2')}>
<item.icon className={cn('w-5 h-5 flex-shrink-0', active && 'text-brand-yellow')} />
{!col && <span>{item.label}</span>}
</Link>
)
})}
</nav>
<div className="p-2 border-t border-hairline-dark">
{!col && (
<div className="mx-3 mb-3 p-3 rounded-xl bg-brand-yellow/5 border border-brand-yellow/20">
<div className="flex items-center gap-2 mb-1"><Shield className="w-4 h-4 text-trading-up" /><span className="text-caption text-trading-up font-semibold">AI Agent Active</span></div>
<p className="text-caption text-muted">
{eventCount === null
? 'Connecting...'
: eventCount === 0
? 'No events this session'
: `${eventCount} event${eventCount === 1 ? '' : 's'} fired today`}
</p>
</div>
)}
<button onClick={() => setCol(!col)} className="flex items-center gap-3 px-3 py-2.5 rounded-lg text-nav text-muted hover:text-[#eaecef] hover:bg-surface-elevated w-full transition-colors">
{col ? <ChevronRight className="w-5 h-5" /> : <ChevronLeft className="w-5 h-5" />}
{!col && <span>Collapse</span>}
</button>
</div>
</aside>
)
}