muthuk1 commited on
Commit
7200823
·
verified ·
1 Parent(s): 820c67c

Upload complete FlowState project source code

Browse files
src/app/(dashboard)/agent/page.tsx ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { cn } from '@/lib/utils'
3
+ import { Brain, Zap, Shield, Eye, Play, Pause, Bot, CheckCircle, ArrowRight, Cpu, RefreshCw, GitBranch, Target, Activity, AlertTriangle } from 'lucide-react'
4
+ import { useState, useEffect } from 'react'
5
+
6
+ const msgs = ['🔍 Scanning 312,847 active wallets...', '⚠️ Critical: 7xKXtg...AsU inactive 10d', '🎁 Gift sent: 0.5 SOL via Torque MCP', '📊 Leaderboard: 12,847 scored', '🎟️ Raffle: 9WzDXw...WWM 3x tickets', '🔥 Comeback: HN7cAB...WrH after 12d', '💰 Rebate 2x activated for HN7cAB...WrH', '🤖 Auto-creating Weekend Streak Challenge', '✅ Campaign created — Budget: 5,000 SOL', '📈 Retention up 0.5%', '🛡️ Sybil check: 99.7% legit', '🎯 Targeting 1,234 wallets']
7
+
8
+ export default function AgentPage() {
9
+ const [running, setRunning] = useState(true)
10
+ const [tab, setTab] = useState<'feed' | 'config'>('feed')
11
+ const [feed, setFeed] = useState<{text:string;time:string}[]>([])
12
+
13
+ useEffect(() => {
14
+ const init = msgs.slice(0,6).map((t,i) => ({text:t, time: new Date(Date.now()-i*120000).toLocaleTimeString('en-US',{hour12:false})}))
15
+ setFeed(init)
16
+ if (!running) return
17
+ let c = 6
18
+ const iv = setInterval(() => { const t = msgs[c++ % msgs.length]; setFeed(p => [{text:t, time: new Date().toLocaleTimeString('en-US',{hour12:false})}, ...p].slice(0,25)) }, 3000)
19
+ return () => clearInterval(iv)
20
+ }, [running])
21
+
22
+ return (
23
+ <div className="p-6 space-y-6">
24
+ <div className="flex items-center justify-between">
25
+ <div className="flex items-center gap-4">
26
+ <div className={cn('w-12 h-12 rounded-xl flex items-center justify-center', running ? 'bg-trading-up/10 animate-pulse-glow' : 'bg-surface-elevated')}><Brain className={cn('w-6 h-6', running ? 'text-trading-up' : 'text-muted')} /></div>
27
+ <div><h1 className="text-display-sm text-[#eaecef]">AI Agent</h1><p className="text-body-md text-muted mt-0.5">Autonomous churn detection & retention engine</p></div>
28
+ </div>
29
+ <div className="flex items-center gap-3">
30
+ <div className={cn('px-4 py-2 rounded-lg border', running ? 'bg-trading-up/10 border-trading-up/30 text-trading-up' : 'bg-surface-card border-hairline-dark text-muted')}><div className="flex items-center gap-2"><div className={cn('w-2.5 h-2.5 rounded-full', running ? 'bg-trading-up animate-pulse' : 'bg-muted')} /><span className="text-button font-semibold">{running ? 'RUNNING' : 'PAUSED'}</span></div></div>
31
+ <button onClick={() => setRunning(!running)} className={cn('flex items-center gap-2 px-5 py-2.5 rounded-md text-button font-semibold transition', running ? 'bg-trading-down/10 text-trading-down border border-trading-down/30' : 'bg-brand-yellow text-ink')}>{running ? <><Pause className="w-4 h-4" />Pause</> : <><Play className="w-4 h-4" />Start</>}</button>
32
+ </div>
33
+ </div>
34
+ <div className="grid grid-cols-4 gap-4">
35
+ {[{i:Activity,l:'Actions Today',v:'3,847',c:'text-brand-yellow'},{i:Eye,l:'Wallets Scanned',v:'312.8K',c:'text-info'},{i:Shield,l:'Wallets Saved',v:'15.2K',c:'text-trading-up'},{i:Target,l:'Churn Prevented',v:'23.9K',c:'text-brand-yellow'}].map(s => { const I = s.i; return (
36
+ <div key={s.l} className="rounded-xl bg-surface-card border border-hairline-dark p-4"><div className="flex items-center gap-2 mb-2"><I className={cn('w-4 h-4', s.c)} /><span className="text-caption text-muted">{s.l}</span></div><p className={cn('font-mono text-title-lg tabular-nums', s.c === 'text-info' ? 'text-[#eaecef]' : s.c)}>{s.v}</p></div>
37
+ )})}
38
+ </div>
39
+ <div className="flex items-center gap-1 p-1 rounded-lg bg-surface-card border border-hairline-dark w-fit">
40
+ {(['feed','config'] as const).map(t => <button key={t} onClick={() => setTab(t)} className={cn('px-5 py-2 rounded-md text-button transition capitalize', tab === t ? 'bg-brand-yellow text-ink font-semibold' : 'text-muted hover:text-[#eaecef]')}>{t === 'feed' ? 'Live Feed' : 'Configuration'}</button>)}
41
+ </div>
42
+ {tab === 'feed' && (
43
+ <div className="rounded-xl bg-surface-card border border-hairline-dark overflow-hidden">
44
+ <div className="p-4 border-b border-hairline-dark flex items-center justify-between"><div className="flex items-center gap-2"><Bot className="w-4 h-4 text-brand-yellow" /><span className="text-title-sm">Real-Time Agent Feed</span>{running && <div className="w-2 h-2 rounded-full bg-trading-up animate-pulse" />}</div><span className="text-caption text-muted">{feed.length} messages</span></div>
45
+ <div className="max-h-[500px] overflow-y-auto divide-y divide-hairline-dark/50">{feed.map((m,i) => (
46
+ <div key={`${m.time}-${i}`} className={cn('flex items-start gap-4 px-5 py-3', i===0 && running && 'animate-slide-up bg-brand-yellow/5')}><span className="font-mono text-caption text-muted tabular-nums whitespace-nowrap mt-0.5">{m.time}</span><span className="text-body-sm text-[#eaecef]">{m.text}</span></div>
47
+ ))}</div>
48
+ </div>
49
+ )}
50
+ {tab === 'config' && (
51
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
52
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-5">
53
+ <h3 className="text-title-sm mb-4 flex items-center gap-2"><AlertTriangle className="w-4 h-4 text-brand-yellow" />Detection Thresholds</h3>
54
+ {[{l:'critical',d:10,v:90},{l:'high',d:7,v:60},{l:'medium',d:5,v:30}].map(t => (
55
+ <div key={t.l} className="p-3 rounded-lg bg-surface-elevated border border-hairline-dark/50 mb-3">
56
+ <span className={cn('text-caption font-semibold uppercase', t.l==='critical'?'text-trading-down':t.l==='high'?'text-[#ff9500]':'text-brand-yellow')}>{t.l}</span>
57
+ <div className="grid grid-cols-2 gap-3 mt-2"><div><span className="text-caption text-muted">Inactive Days</span><p className="font-mono text-num-md text-[#eaecef]">&ge; {t.d}</p></div><div><span className="text-caption text-muted">Volume Drop</span><p className="font-mono text-num-md text-[#eaecef]">&ge; {t.v}%</p></div></div>
58
+ </div>
59
+ ))}
60
+ </div>
61
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-5">
62
+ <h3 className="text-title-sm mb-4 flex items-center gap-2"><Cpu className="w-4 h-4 text-brand-yellow" />Agent Config</h3>
63
+ {[['Scan Interval','30s'],['Protocols','6'],['Torque MCP','Connected'],['Helius','Active'],['Sybil Filter','Enabled']].map(([k,v]) => (
64
+ <div key={k} className="flex items-center justify-between py-2 border-b border-hairline-dark/50"><span className="text-body-sm text-muted">{k}</span><span className={cn('font-mono text-num-sm', v==='Connected'||v==='Active'||v==='Enabled' ? 'text-trading-up font-semibold' : 'text-[#eaecef]')}>{v}</span></div>
65
+ ))}
66
+ </div>
67
+ </div>
68
+ )}
69
+ <div className="rounded-xl bg-brand-yellow/5 border border-brand-yellow/20 p-6">
70
+ <h3 className="text-title-sm text-brand-yellow mb-3">How FlowState AI Agent Works</h3>
71
+ <div className="grid grid-cols-5 gap-3 items-center">
72
+ {[{i:Eye,l:'Monitor',d:'Helius webhooks scan txns'},{i:Brain,l:'Detect',d:'AI scores churn risk'},{i:Zap,l:'Decide',d:'Select incentive type'},{i:Bot,l:'Execute',d:'Fire via Torque MCP'},{i:RefreshCw,l:'Learn',d:'Track outcomes'}].map((s,i) => (
73
+ <div key={s.l} className="text-center"><div className="w-10 h-10 rounded-lg bg-surface-card border border-brand-yellow/30 flex items-center justify-center mx-auto mb-2"><s.i className="w-5 h-5 text-brand-yellow" /></div><p className="text-caption text-brand-yellow font-semibold">{s.l}</p><p className="text-[10px] text-muted mt-0.5">{s.d}</p></div>
74
+ ))}
75
+ </div>
76
+ </div>
77
+ </div>
78
+ )
79
+ }
src/app/(dashboard)/analytics/page.tsx ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { TrendingUp, TrendingDown, Target, Zap, Award, Clock } from 'lucide-react'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const cohorts = [
6
+ { week: 'Mar 3', d1: 100, d7: 72, d14: 58, d30: 41, d60: 28 },
7
+ { week: 'Mar 10', d1: 100, d7: 74, d14: 61, d30: 44, d60: 31 },
8
+ { week: 'Mar 17', d1: 100, d7: 76, d14: 63, d30: 47, d60: 33 },
9
+ { week: 'Mar 24', d1: 100, d7: 78, d14: 65, d30: 49, d60: 35 },
10
+ { week: 'Mar 31', d1: 100, d7: 79, d14: 67, d30: 52, d60: 0 },
11
+ { week: 'Apr 7', d1: 100, d7: 81, d14: 69, d30: 0, d60: 0 },
12
+ { week: 'Apr 14', d1: 100, d7: 83, d14: 0, d30: 0, d60: 0 },
13
+ { week: 'Apr 21', d1: 100, d7: 0, d14: 0, d30: 0, d60: 0 },
14
+ ]
15
+
16
+ const events = [
17
+ { event: 'churn_risk_high', count: 2341, color: '#f6465d', max: 34567 },
18
+ { event: 'churn_risk_medium', count: 5678, color: '#ff9500', max: 34567 },
19
+ { event: 'comeback_detected', count: 8923, color: '#0ecb81', max: 34567 },
20
+ { event: 'streak_maintained', count: 34567, color: '#FCD535', max: 34567 },
21
+ { event: 'volume_milestone', count: 12345, color: '#2dbdb6', max: 34567 },
22
+ { event: 'referral_from_saved', count: 4567, color: '#a78bfa', max: 34567 },
23
+ { event: 'inactivity_detected', count: 21011, color: '#707a8a', max: 34567 },
24
+ ]
25
+
26
+ function fmt(n: number) { return n >= 1000 ? `${(n/1000).toFixed(1)}K` : String(n) }
27
+
28
+ function getColor(v: number) {
29
+ if (v === 0) return 'bg-surface-elevated text-muted'
30
+ if (v >= 75) return 'bg-trading-up/20 text-trading-up'
31
+ if (v >= 50) return 'bg-brand-yellow/15 text-brand-yellow'
32
+ if (v >= 30) return 'bg-[#ff9500]/15 text-[#ff9500]'
33
+ return 'bg-trading-down/15 text-trading-down'
34
+ }
35
+
36
+ export default function AnalyticsPage() {
37
+ return (
38
+ <div className="p-6 space-y-6">
39
+ <div><h1 className="text-display-sm text-[#eaecef]">Analytics</h1><p className="text-body-md text-muted mt-1">Deep retention insights, cohort analysis & ROI tracking</p></div>
40
+ <div className="grid grid-cols-2 lg:grid-cols-5 gap-4">
41
+ {[
42
+ { label: 'Avg Retention', value: '67.3%', change: '+9.6%', up: true, icon: Target },
43
+ { label: 'Churn Rate', value: '4.2%', change: '-4.3%', up: false, icon: TrendingDown },
44
+ { label: 'Campaign ROI', value: '847%', change: '+15.2%', up: true, icon: TrendingUp },
45
+ { label: 'Saved Wallets', value: '15.2K', change: '+23.4%', up: true, icon: Award },
46
+ { label: 'Agent Actions', value: '3,847', change: '+12.1%', up: true, icon: Zap },
47
+ ].map(k => { const I = k.icon; return (
48
+ <div key={k.label} className="rounded-xl bg-surface-card border border-hairline-dark p-4">
49
+ <div className="flex items-center justify-between mb-2"><span className="text-caption text-muted">{k.label}</span><I className="w-4 h-4 text-muted" /></div>
50
+ <div className="font-mono text-title-lg text-[#eaecef] tabular-nums">{k.value}</div>
51
+ <span className={cn('font-mono text-num-sm', k.up ? 'text-trading-up' : 'text-trading-down')}>{k.change}</span>
52
+ </div>
53
+ )})}
54
+ </div>
55
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-5">
56
+ <div className="flex items-center justify-between mb-4">
57
+ <div><h3 className="text-title-sm">Retention Cohorts</h3><p className="text-caption text-muted mt-0.5">FlowState launched Mar 31</p></div>
58
+ <div className="flex items-center gap-2">
59
+ <div className="flex items-center gap-1"><div className="w-3 h-3 rounded-sm bg-trading-down/30" /><span className="text-[10px] text-muted">Low</span></div>
60
+ <div className="flex items-center gap-1"><div className="w-3 h-3 rounded-sm bg-brand-yellow/50" /><span className="text-[10px] text-muted">Mid</span></div>
61
+ <div className="flex items-center gap-1"><div className="w-3 h-3 rounded-sm bg-trading-up/80" /><span className="text-[10px] text-muted">High</span></div>
62
+ </div>
63
+ </div>
64
+ <table className="w-full">
65
+ <thead><tr className="border-b border-hairline-dark">
66
+ <th className="text-left text-caption text-muted uppercase px-4 py-2">Cohort</th>
67
+ <th className="text-center text-caption text-muted uppercase px-4 py-2">Day 1</th>
68
+ <th className="text-center text-caption text-muted uppercase px-4 py-2">Day 7</th>
69
+ <th className="text-center text-caption text-muted uppercase px-4 py-2">Day 14</th>
70
+ <th className="text-center text-caption text-muted uppercase px-4 py-2">Day 30</th>
71
+ <th className="text-center text-caption text-muted uppercase px-4 py-2">Day 60</th>
72
+ </tr></thead>
73
+ <tbody>{cohorts.map(c => (
74
+ <tr key={c.week} className="border-b border-hairline-dark/50">
75
+ <td className="px-4 py-2"><div className="flex items-center gap-2"><Clock className="w-3.5 h-3.5 text-muted" /><span className="text-body-sm text-muted font-mono">{c.week}</span></div></td>
76
+ {[c.d1, c.d7, c.d14, c.d30, c.d60].map((v, i) => (
77
+ <td key={i} className="px-4 py-2 text-center"><span className={cn('inline-block min-w-[48px] px-2 py-1 rounded font-mono text-num-sm tabular-nums', getColor(v))}>{v === 0 ? '\u2014' : `${v}%`}</span></td>
78
+ ))}
79
+ </tr>
80
+ ))}</tbody>
81
+ </table>
82
+ <div className="mt-4 p-3 rounded-lg bg-trading-up/5 border border-trading-up/20">
83
+ <p className="text-body-sm text-trading-up">📈 <strong>FlowState Impact:</strong> Cohorts after Mar 31 show +7pp higher day-7 retention and +11pp higher day-14 retention.</p>
84
+ </div>
85
+ </div>
86
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-5">
87
+ <h3 className="text-title-sm mb-4">Torque Custom Events Breakdown</h3>
88
+ <div className="space-y-4">{events.map(e => (
89
+ <div key={e.event} className="space-y-1.5">
90
+ <div className="flex items-center justify-between"><code className="font-mono text-caption text-muted">{e.event}</code><span className="font-mono text-num-sm text-[#eaecef] tabular-nums">{fmt(e.count)}</span></div>
91
+ <div className="h-2 rounded-full bg-surface-elevated overflow-hidden"><div className="h-full rounded-full transition-all duration-700" style={{ width: `${(e.count / e.max) * 100}%`, backgroundColor: e.color }} /></div>
92
+ </div>
93
+ ))}</div>
94
+ </div>
95
+ </div>
96
+ )
97
+ }
src/app/(dashboard)/campaigns/page.tsx ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { Plus, Bot, Users, Activity, DollarSign, Trophy, Gift, Ticket, Percent } from 'lucide-react'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const campaigns = [
6
+ { id: '1', name: 'Weekly Volume Champions', type: 'leaderboard' as const, status: 'active', desc: 'Top 50 traders by weekly swap volume earn SOL rewards', budget: 50000, participants: 12847, events: 89432, ai: true, formula: 'SUM(swap_volume)' },
7
+ { id: '2', name: 'Comeback Raffle', type: 'raffle' as const, status: 'active', desc: 'Returning users after 7+ days get raffle tickets', budget: 25000, participants: 3456, events: 8923, ai: true },
8
+ { id: '3', name: 'Anti-Churn Gift Drop', type: 'gift' as const, status: 'active', desc: 'High churn risk wallets receive 0.5 SOL gift', budget: 15000, participants: 1234, events: 4567, ai: true },
9
+ { id: '4', name: 'Streak Multiplier Rebate', type: 'rebate' as const, status: 'active', desc: '7+ day streak unlocks 2x fee rebate for 48h', budget: 75000, participants: 8932, events: 45678, ai: true, formula: 'streak_days >= 7' },
10
+ { id: '5', name: 'DeFi Explorer Rewards', type: 'leaderboard' as const, status: 'active', desc: 'Multi-protocol users rank higher', budget: 30000, participants: 6789, events: 23456, ai: false, formula: 'COUNT(protocols) * SUM(volume)' },
11
+ { id: '6', name: 'New User Welcome Gift', type: 'gift' as const, status: 'ended', desc: 'First-time users who complete 3 swaps receive SOL', budget: 10000, participants: 4567, events: 12345, ai: false },
12
+ ]
13
+
14
+ const typeConfig = { leaderboard: { icon: Trophy, color: 'text-brand-yellow', bg: 'bg-brand-yellow/10', label: 'Leaderboard' }, gift: { icon: Gift, color: 'text-brand-turquoise', bg: 'bg-brand-turquoise/10', label: 'Gift' }, raffle: { icon: Ticket, color: 'text-[#a78bfa]', bg: 'bg-[#a78bfa]/10', label: 'Raffle' }, rebate: { icon: Percent, color: 'text-trading-up', bg: 'bg-trading-up/10', label: 'Rebate' } }
15
+ const statusColors = { active: 'bg-trading-up/10 text-trading-up', ended: 'bg-brand-yellow/10 text-brand-yellow', draft: 'bg-muted/10 text-muted', distributed: 'bg-brand-turquoise/10 text-brand-turquoise' }
16
+
17
+ function fmt(n: number) { return n >= 1000 ? `${(n/1000).toFixed(1)}K` : String(n) }
18
+ function fmtC(n: number) { return `$${n.toLocaleString()}` }
19
+
20
+ export default function CampaignsPage() {
21
+ return (
22
+ <div className="p-6 space-y-6">
23
+ <div className="flex items-center justify-between">
24
+ <div><h1 className="text-display-sm text-[#eaecef]">Campaigns</h1><p className="text-body-md text-muted mt-1">Manage Torque campaigns — leaderboards, rebates, raffles & gifts</p></div>
25
+ <button className="flex items-center gap-2 px-5 py-2.5 rounded-md bg-brand-yellow text-ink text-button font-semibold hover:bg-brand-yellow-active transition"><Plus className="w-4 h-4" />Create Campaign</button>
26
+ </div>
27
+ <div className="grid grid-cols-4 gap-4">
28
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-4"><span className="text-caption text-muted">Total Budget</span><p className="font-mono text-title-lg text-[#eaecef] mt-1">{fmtC(205000)}</p></div>
29
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-4"><span className="text-caption text-muted">Distributed</span><p className="font-mono text-title-lg text-trading-up mt-1">{fmtC(42801)}</p></div>
30
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-4"><span className="text-caption text-muted">Total Participants</span><p className="font-mono text-title-lg text-[#eaecef] mt-1">{fmt(47825)}</p></div>
31
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-4"><div className="flex items-center gap-1.5"><Bot className="w-3.5 h-3.5 text-brand-yellow" /><span className="text-caption text-muted">AI Created</span></div><p className="font-mono text-title-lg text-brand-yellow mt-1">4/6</p></div>
32
+ </div>
33
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
34
+ {campaigns.map(c => {
35
+ const tc = typeConfig[c.type]; const Icon = tc.icon; const sc = statusColors[c.status as keyof typeof statusColors] || statusColors.active
36
+ return (
37
+ <div key={c.id} className="rounded-xl bg-surface-card border border-hairline-dark p-5 hover:border-brand-yellow/30 transition-all group cursor-pointer">
38
+ <div className="flex items-start justify-between mb-3">
39
+ <div className="flex items-center gap-2">
40
+ <div className={cn('inline-flex items-center gap-1.5 px-2.5 py-1 rounded-md', tc.bg)}><Icon className={cn('w-3.5 h-3.5', tc.color)} /><span className={cn('text-caption font-semibold', tc.color)}>{tc.label}</span></div>
41
+ <span className={cn('text-[10px] font-semibold uppercase px-2 py-0.5 rounded-pill', sc)}>{c.status}</span>
42
+ </div>
43
+ {c.ai && <div className="flex items-center gap-1 px-2 py-0.5 rounded-pill bg-brand-yellow/10"><Bot className="w-3 h-3 text-brand-yellow" /><span className="text-[10px] text-brand-yellow font-semibold">AI</span></div>}
44
+ </div>
45
+ <h3 className="text-title-sm text-[#eaecef] group-hover:text-brand-yellow transition-colors">{c.name}</h3>
46
+ <p className="text-body-sm text-muted mt-1 line-clamp-2">{c.desc}</p>
47
+ {c.formula && <code className="inline-block mt-2 text-[11px] font-mono text-brand-yellow/70 bg-brand-yellow/5 px-2 py-0.5 rounded">{c.formula}</code>}
48
+ <div className="grid grid-cols-3 gap-3 mt-4 pt-4 border-t border-hairline-dark/50">
49
+ <div><div className="flex items-center gap-1 text-caption text-muted"><Users className="w-3 h-3" /><span>Users</span></div><span className="font-mono text-num-sm text-[#eaecef]">{fmt(c.participants)}</span></div>
50
+ <div><div className="flex items-center gap-1 text-caption text-muted"><Activity className="w-3 h-3" /><span>Events</span></div><span className="font-mono text-num-sm text-[#eaecef]">{fmt(c.events)}</span></div>
51
+ <div><div className="flex items-center gap-1 text-caption text-muted"><DollarSign className="w-3 h-3" /><span>Budget</span></div><span className="font-mono text-num-sm text-brand-yellow">{fmtC(c.budget)}</span></div>
52
+ </div>
53
+ </div>
54
+ )
55
+ })}
56
+ </div>
57
+ </div>
58
+ )
59
+ }
src/app/(dashboard)/layout.tsx ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Sidebar } from '@/components/layout/Sidebar'
2
+ import { Topbar } from '@/components/layout/Topbar'
3
+
4
+ export default function DashboardLayout({ children }: { children: React.ReactNode }) {
5
+ return (
6
+ <div className="flex h-screen overflow-hidden bg-canvas-dark">
7
+ <Sidebar />
8
+ <div className="flex flex-col flex-1 min-w-0">
9
+ <Topbar />
10
+ <main className="flex-1 overflow-y-auto">{children}</main>
11
+ </div>
12
+ </div>
13
+ )
14
+ }
src/app/(dashboard)/leaderboard/page.tsx ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { Trophy, Crown, Medal, Award, Flame, ArrowUp, ArrowDown } from 'lucide-react'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const entries = [
6
+ { rank: 1, wallet: 'BKiKp1...mS2', score: 98750, change: 12.5, volume: '$28.5M', streak: 62, protocols: ['Jupiter','Raydium','Drift','Marginfi','Kamino','Tensor'], rewards: '$5,000' },
7
+ { rank: 2, wallet: '5Q544f...4j1', score: 87234, change: 8.3, volume: '$15.2M', streak: 47, protocols: ['Jupiter','Raydium','Drift','Marginfi','Kamino'], rewards: '$3,500' },
8
+ { rank: 3, wallet: 'Fq8xSc...BHu', score: 76543, change: -2.1, volume: '$12.3M', streak: 31, protocols: ['Jupiter','Raydium','Drift'], rewards: '$2,500' },
9
+ { rank: 4, wallet: 'HN7cAB...WrH', score: 65432, change: 15.7, volume: '$8.7M', streak: 14, protocols: ['Jupiter','Raydium','Drift','Kamino'], rewards: '$1,800' },
10
+ { rank: 5, wallet: '4zMMC9...cDU', score: 54321, change: 3.4, volume: '$3.5M', streak: 5, protocols: ['Kamino','Jupiter'], rewards: '$1,200' },
11
+ ]
12
+
13
+ export default function LeaderboardPage() {
14
+ return (
15
+ <div className="p-6 space-y-6">
16
+ <div>
17
+ <h1 className="text-display-sm text-[#eaecef]">Leaderboard</h1>
18
+ <p className="text-body-md text-muted mt-1">Cross-protocol DeFi Power Rankings powered by Torque</p>
19
+ </div>
20
+ <div className="grid grid-cols-3 gap-4">
21
+ {entries.slice(0,3).map((e,i) => {
22
+ const icons = [Crown, Medal, Award]
23
+ const colors = ['text-brand-yellow','text-[#c0c0c0]','text-[#cd7f32]']
24
+ const Icon = icons[i]
25
+ return (
26
+ <div key={e.rank} className={cn('rounded-xl bg-surface-card border p-6 text-center', i===0 ? 'border-brand-yellow/40 bg-brand-yellow/5 glow-yellow' : 'border-hairline-dark')}>
27
+ <Icon className={cn('w-8 h-8 mx-auto mb-3', colors[i])} />
28
+ <div className="font-mono text-num-display text-[#eaecef]">#{e.rank}</div>
29
+ <p className="text-body-md text-muted font-mono mt-1">{e.wallet}</p>
30
+ <div className="mt-4 font-mono text-title-lg text-brand-yellow">{e.score.toLocaleString()}</div>
31
+ <p className="text-caption text-muted mt-1">Score</p>
32
+ </div>
33
+ )
34
+ })}
35
+ </div>
36
+ <div className="rounded-xl bg-surface-card border border-hairline-dark overflow-hidden">
37
+ <table className="w-full">
38
+ <thead><tr className="border-b border-hairline-dark bg-surface-elevated/50">
39
+ <th className="text-left text-caption text-muted uppercase px-5 py-3 w-16">Rank</th>
40
+ <th className="text-left text-caption text-muted uppercase px-5 py-3">Wallet</th>
41
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">Score</th>
42
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">24h</th>
43
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">Volume</th>
44
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">Streak</th>
45
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">Rewards</th>
46
+ </tr></thead>
47
+ <tbody>{entries.map(e => (
48
+ <tr key={e.rank} className="border-b border-hairline-dark/50 hover:bg-surface-hover transition-colors">
49
+ <td className="px-5 py-4"><span className={cn('font-mono text-num-md font-bold', e.rank<=3 ? 'text-brand-yellow' : 'text-muted')}>{e.rank}</span></td>
50
+ <td className="px-5 py-4 font-mono text-body-md">{e.wallet}</td>
51
+ <td className="px-5 py-4 text-right font-mono text-num-md tabular-nums">{e.score.toLocaleString()}</td>
52
+ <td className="px-5 py-4 text-right"><div className="flex items-center justify-end gap-1">{e.change>=0 ? <ArrowUp className="w-3.5 h-3.5 text-trading-up"/> : <ArrowDown className="w-3.5 h-3.5 text-trading-down"/>}<span className={cn('font-mono text-num-sm', e.change>=0 ? 'text-trading-up' : 'text-trading-down')}>{e.change>=0?'+':''}{e.change}%</span></div></td>
53
+ <td className="px-5 py-4 text-right font-mono text-num-sm text-muted">{e.volume}</td>
54
+ <td className="px-5 py-4 text-right"><div className="flex items-center justify-end gap-1"><Flame className={cn('w-3.5 h-3.5', e.streak>=30?'text-brand-yellow':'text-trading-up')}/><span className="font-mono text-num-sm">{e.streak}d</span></div></td>
55
+ <td className="px-5 py-4 text-right font-mono text-num-sm text-brand-yellow">{e.rewards}</td>
56
+ </tr>
57
+ ))}</tbody>
58
+ </table>
59
+ </div>
60
+ <div className="rounded-xl bg-brand-yellow/5 border border-brand-yellow/20 p-5 flex items-center gap-4">
61
+ <Trophy className="w-6 h-6 text-brand-yellow" />
62
+ <div>
63
+ <h3 className="text-title-sm text-brand-yellow">Scoring Formula</h3>
64
+ <code className="font-mono text-brand-yellow/80 bg-surface-card px-2 py-0.5 rounded text-body-sm">SCORE = COUNT(protocols) x SUM(swap_volume) x STREAK_MULTIPLIER(days)</code>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ )
69
+ }
src/app/(dashboard)/page.tsx ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { Users, ShieldAlert, HeartPulse, TrendingUp, Bot, Flame } from 'lucide-react'
3
+ import { StatCard } from '@/components/ui/StatCard'
4
+
5
+ export default function DashboardPage() {
6
+ return (
7
+ <div className="p-6 space-y-6">
8
+ <div>
9
+ <h1 className="text-display-sm text-[#eaecef]">Dashboard</h1>
10
+ <p className="text-body-md text-muted mt-1">Real-time churn detection and autonomous retention</p>
11
+ </div>
12
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
13
+ <StatCard title="Active Wallets" value="312.8K" change={12.3} changeLabel="vs last week" icon={Users} variant="yellow" />
14
+ <StatCard title="Wallets At Risk" value="23.9K" change={-8.7} changeLabel="vs last week" icon={ShieldAlert} variant="red" />
15
+ <StatCard title="Wallets Saved" value="15.2K" change={23.4} changeLabel="vs last week" icon={HeartPulse} variant="green" />
16
+ <StatCard title="ROI" value="847%" change={15.2} changeLabel="vs last week" icon={TrendingUp} variant="yellow" />
17
+ </div>
18
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-5">
19
+ <div className="flex items-center gap-2 mb-4">
20
+ <Bot className="w-4 h-4 text-brand-yellow" />
21
+ <span className="text-title-sm">AI Agent Feed</span>
22
+ <div className="w-2 h-2 rounded-full bg-trading-up animate-pulse" />
23
+ </div>
24
+ <div className="space-y-2">
25
+ {['Scanning 312,847 active wallets for churn signals...', 'Critical: Wallet 7xKXtg...AsU inactive 10 days', 'Gift sent: 0.5 SOL via Torque MCP', 'Leaderboard updated: 12,847 participants scored', 'Comeback detected: HN7cAB...WrH returned after 12 days'].map((msg, i) => (
26
+ <div key={i} className="flex items-start gap-3 px-4 py-2 border-b border-hairline-dark/50">
27
+ <span className="font-mono text-caption text-muted tabular-nums whitespace-nowrap">14:2{i}:00</span>
28
+ <span className="text-body-sm text-[#eaecef]">{['\u{1F50D}','\u26A0\uFE0F','\u{1F381}','\u{1F4CA}','\u{1F525}'][i]} {msg}</span>
29
+ </div>
30
+ ))}
31
+ </div>
32
+ </div>
33
+ <div className="rounded-xl bg-surface-card border border-hairline-dark p-5">
34
+ <h3 className="text-title-sm mb-4">Protocol Performance</h3>
35
+ <table className="w-full">
36
+ <thead>
37
+ <tr className="border-b border-hairline-dark">
38
+ <th className="text-left text-caption text-muted uppercase px-4 py-3">Protocol</th>
39
+ <th className="text-right text-caption text-muted uppercase px-4 py-3">Users</th>
40
+ <th className="text-right text-caption text-muted uppercase px-4 py-3">Retention</th>
41
+ <th className="text-right text-caption text-muted uppercase px-4 py-3">Streak</th>
42
+ </tr>
43
+ </thead>
44
+ <tbody>
45
+ {[{p:'Jupiter',u:'234.6K',r:72,s:12,c:'#22d3ee'},{p:'Raydium',u:'189.2K',r:64,s:8,c:'#a78bfa'},{p:'Drift',u:'87.7K',r:68,s:10,c:'#f472b6'},{p:'Marginfi',u:'67.9K',r:74,s:15,c:'#34d399'},{p:'Kamino',u:'45.7K',r:76,s:18,c:'#fbbf24'}].map((row) => (
46
+ <tr key={row.p} className="border-b border-hairline-dark/50 hover:bg-surface-hover transition-colors">
47
+ <td className="px-4 py-3"><div className="flex items-center gap-2"><div className="w-3 h-3 rounded-full" style={{backgroundColor:row.c}} /><span className="text-body-md">{row.p}</span></div></td>
48
+ <td className="text-right px-4 py-3 font-mono text-num-md tabular-nums">{row.u}</td>
49
+ <td className="text-right px-4 py-3"><span className={row.r >= 70 ? 'font-mono text-num-sm text-trading-up' : 'font-mono text-num-sm text-brand-yellow'}>{row.r}%</span></td>
50
+ <td className="text-right px-4 py-3"><div className="flex items-center justify-end gap-1"><Flame className="w-3.5 h-3.5 text-brand-yellow" /><span className="font-mono text-num-sm">{row.s}d</span></div></td>
51
+ </tr>
52
+ ))}
53
+ </tbody>
54
+ </table>
55
+ </div>
56
+ </div>
57
+ )
58
+ }
src/app/(dashboard)/wallets/page.tsx ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { cn } from '@/lib/utils'
3
+ import { Search, Flame, ExternalLink, ChevronRight } from 'lucide-react'
4
+ import { useState } from 'react'
5
+
6
+ type Risk = 'critical' | 'high' | 'medium' | 'low' | 'safe'
7
+ const wallets = [
8
+ { addr: '7xKXtg...AsU', risk: 'critical' as Risk, score: 94, vol: '$847K', streak: 0, protocols: ['Jupiter','Raydium'], last: '10d ago', saved: 0 },
9
+ { addr: '9WzDXw...WWM', risk: 'high' as Risk, score: 78, vol: '$1.2M', streak: 2, protocols: ['Drift','Marginfi','Jupiter'], last: '5d ago', saved: 1 },
10
+ { addr: '4zMMC9...cDU', risk: 'medium' as Risk, score: 52, vol: '$3.5M', streak: 5, protocols: ['Kamino','Jupiter'], last: '2d ago', saved: 0 },
11
+ { addr: 'HN7cAB...WrH', risk: 'low' as Risk, score: 23, vol: '$8.7M', streak: 14, protocols: ['Jupiter','Raydium','Drift','Kamino'], last: '1h ago', saved: 2 },
12
+ { addr: '5Q544f...4j1', risk: 'safe' as Risk, score: 8, vol: '$15.2M', streak: 47, protocols: ['Jupiter','Raydium','Drift','Marginfi','Kamino'], last: '30m ago', saved: 3 },
13
+ { addr: 'DRpbCB...1hy', risk: 'critical' as Risk, score: 91, vol: '$235K', streak: 0, protocols: ['Raydium'], last: '12d ago', saved: 0 },
14
+ { addr: 'BKiKp1...mS2', risk: 'safe' as Risk, score: 5, vol: '$28.5M', streak: 62, protocols: ['Jupiter','Raydium','Drift','Marginfi','Kamino','Tensor'], last: '15m ago', saved: 5 },
15
+ ]
16
+
17
+ const riskCfg: Record<Risk, { label: string; color: string; bg: string; dot: string }> = {
18
+ critical: { label: 'CRITICAL', color: 'text-trading-down', bg: 'bg-trading-down/10', dot: 'bg-trading-down' },
19
+ high: { label: 'HIGH', color: 'text-[#ff9500]', bg: 'bg-[#ff9500]/10', dot: 'bg-[#ff9500]' },
20
+ medium: { label: 'MEDIUM', color: 'text-brand-yellow', bg: 'bg-brand-yellow/10', dot: 'bg-brand-yellow' },
21
+ low: { label: 'LOW', color: 'text-trading-up', bg: 'bg-trading-up/10', dot: 'bg-trading-up' },
22
+ safe: { label: 'SAFE', color: 'text-brand-turquoise', bg: 'bg-brand-turquoise/10', dot: 'bg-brand-turquoise' },
23
+ }
24
+
25
+ export default function WalletsPage() {
26
+ const [filter, setFilter] = useState<Risk | 'all'>('all')
27
+ const filtered = wallets.filter(w => filter === 'all' || w.risk === filter)
28
+ return (
29
+ <div className="p-6 space-y-6">
30
+ <div><h1 className="text-display-sm text-[#eaecef]">Wallets</h1><p className="text-body-md text-muted mt-1">Monitor wallet health, churn risk & activity patterns</p></div>
31
+ <div className="grid grid-cols-2 lg:grid-cols-6 gap-3">
32
+ {(['all','critical','high','medium','low','safe'] as const).map(r => (
33
+ <button key={r} onClick={() => setFilter(r)} className={cn('rounded-xl border p-3 text-center transition-all', filter === r ? 'border-brand-yellow/40 bg-brand-yellow/5' : 'border-hairline-dark bg-surface-card hover:border-brand-yellow/20')}>
34
+ <span className="text-caption text-muted capitalize">{r}</span>
35
+ <p className="font-mono text-title-md text-[#eaecef] tabular-nums mt-0.5">{r === 'all' ? wallets.length : wallets.filter(w => w.risk === r).length}</p>
36
+ </button>
37
+ ))}
38
+ </div>
39
+ <div className="rounded-xl bg-surface-card border border-hairline-dark overflow-hidden">
40
+ <table className="w-full">
41
+ <thead><tr className="border-b border-hairline-dark bg-surface-elevated/50">
42
+ <th className="text-left text-caption text-muted uppercase px-5 py-3">Wallet</th>
43
+ <th className="text-center text-caption text-muted uppercase px-5 py-3">Risk</th>
44
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">Score</th>
45
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">Volume</th>
46
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">Streak</th>
47
+ <th className="text-center text-caption text-muted uppercase px-5 py-3">Protocols</th>
48
+ <th className="text-right text-caption text-muted uppercase px-5 py-3">Last Active</th>
49
+ <th className="w-10"></th>
50
+ </tr></thead>
51
+ <tbody>{filtered.map(w => { const rc = riskCfg[w.risk]; return (
52
+ <tr key={w.addr} className="border-b border-hairline-dark/50 hover:bg-surface-hover transition-colors group">
53
+ <td className="px-5 py-4"><div className="flex items-center gap-2"><span className="font-mono text-body-md">{w.addr}</span><ExternalLink className="w-3 h-3 text-muted opacity-0 group-hover:opacity-100 transition" /></div></td>
54
+ <td className="px-5 py-4 text-center"><div className={cn('inline-flex items-center gap-1.5 px-3 py-1 rounded-pill', rc.bg)}><div className={cn('w-2 h-2 rounded-full', rc.dot)} /><span className={cn('font-mono text-caption font-semibold', rc.color)}>{rc.label}</span></div></td>
55
+ <td className="px-5 py-4 text-right"><div className="flex items-center justify-end gap-2"><div className="w-16 h-1.5 rounded-full bg-surface-elevated overflow-hidden"><div className={cn('h-full rounded-full', w.score >= 80 ? 'bg-trading-down' : w.score >= 60 ? 'bg-[#ff9500]' : w.score >= 40 ? 'bg-brand-yellow' : w.score >= 20 ? 'bg-trading-up' : 'bg-brand-turquoise')} style={{ width: `${w.score}%` }} /></div><span className="font-mono text-num-sm text-muted tabular-nums w-8 text-right">{w.score}</span></div></td>
56
+ <td className="px-5 py-4 text-right font-mono text-num-sm tabular-nums">{w.vol}</td>
57
+ <td className="px-5 py-4 text-right"><div className="flex items-center justify-end gap-1"><Flame className={cn('w-3.5 h-3.5', w.streak >= 30 ? 'text-brand-yellow' : w.streak >= 7 ? 'text-trading-up' : w.streak === 0 ? 'text-trading-down' : 'text-muted')} /><span className="font-mono text-num-sm">{w.streak}d</span></div></td>
58
+ <td className="px-5 py-4"><div className="flex flex-wrap justify-center gap-1">{w.protocols.slice(0,3).map(p => <span key={p} className="text-[10px] px-1.5 py-0.5 rounded bg-surface-elevated text-muted">{p}</span>)}{w.protocols.length > 3 && <span className="text-[10px] px-1.5 py-0.5 rounded bg-surface-elevated text-muted">+{w.protocols.length-3}</span>}</div></td>
59
+ <td className="px-5 py-4 text-right text-body-sm text-muted">{w.last}</td>
60
+ <td className="px-2 py-4"><ChevronRight className="w-4 h-4 text-muted opacity-0 group-hover:opacity-100 transition" /></td>
61
+ </tr>
62
+ )})}</tbody>
63
+ </table>
64
+ </div>
65
+ </div>
66
+ )
67
+ }
src/app/api/agent/scan/route.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ import { NextResponse } from 'next/server'
2
+ export async function POST() { return NextResponse.json({ detections: [], count: 0 }) }
3
+ export async function GET() { return NextResponse.json({ status: 'active', capabilities: ['churn_detection','auto_campaign_creation','comeback_detection','streak_tracking'] }) }
src/app/api/torque/campaigns/route.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ import { NextResponse } from 'next/server'
2
+ export async function POST() { return NextResponse.json({ success: true, campaignId: `demo-camp-${Date.now()}` }) }
3
+ export async function GET() { return NextResponse.json({ campaigns: [] }) }
src/app/api/torque/events/route.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ import { NextResponse } from 'next/server'
2
+ export async function POST() { return NextResponse.json({ success: true, eventId: `demo-${Date.now()}` }) }
3
+ export async function GET() { return NextResponse.json({ status: 'ok', events: ['churn_risk_high','churn_risk_medium','comeback_detected','streak_maintained','volume_milestone','referral_from_saved','inactivity_detected'] }) }
src/components/layout/Sidebar.tsx ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import Link from 'next/link'
3
+ import { usePathname } from 'next/navigation'
4
+ import { cn } from '@/lib/utils'
5
+ import { LayoutDashboard, Trophy, Megaphone, BarChart3, Wallet, Bot, Zap, Shield, ChevronLeft, ChevronRight } from 'lucide-react'
6
+ import { useState } from 'react'
7
+
8
+ const navItems = [
9
+ { label: 'Dashboard', href: '/', icon: LayoutDashboard },
10
+ { label: 'Leaderboard', href: '/leaderboard', icon: Trophy },
11
+ { label: 'Campaigns', href: '/campaigns', icon: Megaphone },
12
+ { label: 'Analytics', href: '/analytics', icon: BarChart3 },
13
+ { label: 'Wallets', href: '/wallets', icon: Wallet },
14
+ { label: 'AI Agent', href: '/agent', icon: Bot },
15
+ ]
16
+
17
+ export function Sidebar() {
18
+ const pathname = usePathname()
19
+ const [collapsed, setCollapsed] = useState(false)
20
+ return (
21
+ <aside className={cn('hidden md:flex flex-col border-r border-hairline-dark bg-surface-card transition-all duration-300', collapsed ? 'w-[68px]' : 'w-[240px]')}>
22
+ <div className="flex items-center h-16 px-4 border-b border-hairline-dark">
23
+ <div className="flex items-center gap-2.5 overflow-hidden">
24
+ <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>
25
+ {!collapsed && <span className="text-title-sm text-brand-yellow font-bold tracking-tight animate-slide-in-right">FlowState</span>}
26
+ </div>
27
+ </div>
28
+ <nav className="flex-1 py-4 px-2 space-y-1">
29
+ {navItems.map(item => {
30
+ const isActive = pathname === item.href || (item.href !== '/' && pathname.startsWith(item.href))
31
+ return (
32
+ <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', isActive ? 'bg-brand-yellow/10 text-brand-yellow' : 'text-muted hover:text-[#eaecef] hover:bg-surface-elevated', collapsed && 'justify-center px-2')}>
33
+ <item.icon className={cn('w-5 h-5 flex-shrink-0', isActive && 'text-brand-yellow')} />
34
+ {!collapsed && <span>{item.label}</span>}
35
+ </Link>
36
+ )
37
+ })}
38
+ </nav>
39
+ <div className="p-2 border-t border-hairline-dark">
40
+ {!collapsed && <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">3,847 actions today</p></div>}
41
+ <button onClick={() => setCollapsed(!collapsed)} 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">
42
+ {collapsed ? <ChevronRight className="w-5 h-5" /> : <ChevronLeft className="w-5 h-5" />}
43
+ {!collapsed && <span>Collapse</span>}
44
+ </button>
45
+ </div>
46
+ </aside>
47
+ )
48
+ }
src/components/layout/Topbar.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { Bell, Search, Globe, Wifi } from 'lucide-react'
3
+ import { useState, useEffect } from 'react'
4
+
5
+ export function Topbar() {
6
+ const [time, setTime] = useState('')
7
+ useEffect(() => { const u = () => setTime(new Date().toLocaleTimeString('en-US', { hour12: false })); u(); const i = setInterval(u, 1000); return () => clearInterval(i) }, [])
8
+ return (
9
+ <header className="h-14 border-b border-hairline-dark bg-surface-card/80 backdrop-blur-sm flex items-center justify-between px-6">
10
+ <div className="relative">
11
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted" />
12
+ <input type="text" placeholder="Search wallets, campaigns..." className="w-64 h-9 pl-9 pr-4 rounded-lg bg-surface-elevated border border-hairline-dark text-body-sm text-[#eaecef] placeholder:text-muted focus:outline-none focus:ring-1 focus:ring-brand-yellow/50" />
13
+ </div>
14
+ <div className="flex items-center gap-4">
15
+ <div className="flex items-center gap-2 px-3 py-1.5 rounded-pill bg-trading-up/10 border border-trading-up/20"><div className="w-2 h-2 rounded-full bg-trading-up animate-pulse" /><span className="text-caption text-trading-up font-semibold">LIVE</span></div>
16
+ <span className="font-mono text-num-sm text-muted tabular-nums">{time}</span>
17
+ <button className="flex items-center gap-1.5 text-muted hover:text-[#eaecef] transition"><Wifi className="w-4 h-4 text-trading-up" /><span className="text-caption">Solana</span></button>
18
+ <button className="relative p-2 rounded-lg hover:bg-surface-elevated transition"><Bell className="w-4 h-4 text-muted" /><span className="absolute top-1 right-1 w-2 h-2 bg-brand-yellow rounded-full" /></button>
19
+ <button className="flex items-center gap-2 px-4 py-2 rounded-md bg-brand-yellow text-ink text-button font-semibold hover:bg-brand-yellow-active transition"><Globe className="w-4 h-4" />Connect</button>
20
+ </div>
21
+ </header>
22
+ )
23
+ }
src/components/ui/StatCard.tsx ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import { cn } from '@/lib/utils'
3
+ import { LucideIcon } from 'lucide-react'
4
+
5
+ interface StatCardProps { title: string; value: string; change?: number; changeLabel?: string; icon: LucideIcon; variant?: 'default' | 'yellow' | 'green' | 'red' }
6
+
7
+ export function StatCard({ title, value, change, changeLabel, icon: Icon, variant = 'default' }: StatCardProps) {
8
+ const vs: Record<string, string> = { default: 'border-hairline-dark', yellow: 'border-brand-yellow/20 bg-brand-yellow/5', green: 'border-trading-up/20 bg-trading-up/5', red: 'border-trading-down/20 bg-trading-down/5' }
9
+ const is: Record<string, string> = { default: 'bg-surface-elevated text-muted', yellow: 'bg-brand-yellow/10 text-brand-yellow', green: 'bg-trading-up/10 text-trading-up', red: 'bg-trading-down/10 text-trading-down' }
10
+ return (
11
+ <div className={cn('p-5 rounded-xl bg-surface-card border transition-all hover:border-brand-yellow/30', vs[variant])}>
12
+ <div className="flex items-start justify-between mb-3">
13
+ <span className="text-caption text-muted uppercase tracking-wider">{title}</span>
14
+ <div className={cn('p-2 rounded-lg', is[variant])}><Icon className="w-4 h-4" /></div>
15
+ </div>
16
+ <div className="font-mono text-num-display text-[#eaecef] tabular-nums leading-none">{value}</div>
17
+ {(change !== undefined || changeLabel) && (
18
+ <div className="mt-2 flex items-center gap-1.5">
19
+ {change !== undefined && <span className={cn('text-num-sm font-mono tabular-nums', change >= 0 ? 'text-trading-up' : 'text-trading-down')}>{change >= 0 ? '+' : ''}{change.toFixed(1)}%</span>}
20
+ {changeLabel && <span className="text-caption text-muted">{changeLabel}</span>}
21
+ </div>
22
+ )}
23
+ </div>
24
+ )
25
+ }