feat(dashboard): FlowPilot cockpit dashboard + sidebar redesign
- Replace QuickStartPage with FlowPilot-centric dashboard - Add StartSessionInput with Guided/Chat mode toggle - Add PendingEscalations, ActiveFlowPilotSessions, PerformanceCards - Add KnowledgeBaseCards, TeamSummary, RecentFlowPilotSessions - Every number/card is a portal to its detail page - Restructure sidebar: Resolve/Knowledge/Insights sections - Remove redundant nav items (FlowPilot, Flow Editor, Flow Assist, etc.) - Wire prefill from dashboard input to FlowPilot intake - Update mobile nav to match new sidebar structure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
95
frontend/src/components/dashboard/PerformanceCards.tsx
Normal file
95
frontend/src/components/dashboard/PerformanceCards.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { CheckCircle, Clock, TrendingUp, Timer } from 'lucide-react'
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
import { sidebarApi } from '@/api'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface StatCard {
|
||||
label: string
|
||||
value: string | number
|
||||
icon: LucideIcon
|
||||
iconColor: string
|
||||
href: string
|
||||
highlight?: boolean
|
||||
}
|
||||
|
||||
export function PerformanceCards() {
|
||||
const navigate = useNavigate()
|
||||
const [resolved, setResolved] = useState(0)
|
||||
const [active, setActive] = useState(0)
|
||||
const [totalMinutes, setTotalMinutes] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
sidebarApi.getStats()
|
||||
.then((stats) => {
|
||||
setResolved(stats.resolved_today)
|
||||
setActive(stats.active_count)
|
||||
setTotalMinutes(stats.total_session_minutes_today)
|
||||
})
|
||||
.catch(() => {})
|
||||
}, [])
|
||||
|
||||
const avgMttr = resolved > 0 ? Math.round(totalMinutes / resolved) : 0
|
||||
|
||||
const cards: StatCard[] = [
|
||||
{
|
||||
label: 'Resolved Today',
|
||||
value: resolved,
|
||||
icon: CheckCircle,
|
||||
iconColor: '#34d399',
|
||||
href: '/analytics',
|
||||
highlight: true,
|
||||
},
|
||||
{
|
||||
label: 'Avg Resolution',
|
||||
value: avgMttr > 0 ? `${avgMttr}m` : '\u2014',
|
||||
icon: Clock,
|
||||
iconColor: '#22d3ee',
|
||||
href: '/analytics',
|
||||
},
|
||||
{
|
||||
label: 'Active Now',
|
||||
value: active,
|
||||
icon: TrendingUp,
|
||||
iconColor: '#38bdf8',
|
||||
href: '/sessions?filter=active',
|
||||
},
|
||||
{
|
||||
label: 'Session Time',
|
||||
value: totalMinutes > 0 ? `${totalMinutes}m` : '\u2014',
|
||||
icon: Timer,
|
||||
iconColor: '#fbbf24',
|
||||
href: '/analytics',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||
{cards.map((card, i) => (
|
||||
<button
|
||||
key={card.label}
|
||||
onClick={() => navigate(card.href)}
|
||||
className={cn(
|
||||
'glass-card p-4 text-left fade-in',
|
||||
i === 0 && 'active-glow'
|
||||
)}
|
||||
style={{ animationDelay: `${400 + i * 60}ms` }}
|
||||
>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="font-label text-[0.5625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
{card.label}
|
||||
</p>
|
||||
<card.icon size={14} style={{ color: card.iconColor }} />
|
||||
</div>
|
||||
<p className={cn(
|
||||
'font-heading text-2xl font-extrabold tracking-tight',
|
||||
card.highlight ? 'text-gradient-brand' : 'text-foreground'
|
||||
)}>
|
||||
{card.value}
|
||||
</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user