import { useState, useEffect, useRef } from 'react' import { Link } from 'react-router-dom' import { BarChart3, Clock, Star, CheckCircle2, ArrowUpRight, AlertTriangle, Lightbulb, Loader2, Ticket, RotateCcw, } from 'lucide-react' import { AreaChart, Area, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, } from 'recharts' import { cn } from '@/lib/utils' import { flowpilotAnalyticsApi } from '@/api' import { toast } from '@/lib/toast' import CoverageHeatmap from '@/components/analytics/CoverageHeatmap' import FlowQualityTable from '@/components/analytics/FlowQualityTable' import PsaMetricsPanel from '@/components/analytics/PsaMetricsPanel' import type { FlowPilotDashboard, KnowledgeGapReport, KnowledgeGap, CoverageResponse, FlowQualityResponse, EnhancedPsaMetrics, } from '@/types/flowpilot-analytics' const PERIOD_OPTIONS = [ { value: '7d', label: 'Last 7 days' }, { value: '30d', label: 'Last 30 days' }, { value: '90d', label: 'Last 90 days' }, ] const TABS = [ { id: 'overview', label: 'Overview' }, { id: 'coverage', label: 'Coverage' }, { id: 'quality', label: 'Flow Quality' }, { id: 'psa', label: 'PSA' }, ] as const type TabId = typeof TABS[number]['id'] const SEVERITY_STYLES = { high: 'bg-rose-500/10 text-rose-500 border-rose-500/20', medium: 'bg-amber-400/10 text-amber-400 border-amber-400/20', low: 'bg-blue-400/10 text-blue-400 border-blue-400/20', } export default function FlowPilotAnalyticsPage() { const [period, setPeriod] = useState('30d') const [activeTab, setActiveTab] = useState('overview') const [dashboard, setDashboard] = useState(null) const [gaps, setGaps] = useState(null) const [loading, setLoading] = useState(true) // Lazy-loaded tab data const [coverageData, setCoverageData] = useState(null) const [coverageLoading, setCoverageLoading] = useState(false) const [qualityData, setQualityData] = useState(null) const [qualityLoading, setQualityLoading] = useState(false) const [psaData, setPsaData] = useState(null) const [psaLoading, setPsaLoading] = useState(false) // Error states per tab const [coverageError, setCoverageError] = useState(false) const [qualityError, setQualityError] = useState(false) const [psaError, setPsaError] = useState(false) const [retryKey, setRetryKey] = useState(0) // Track which period each tab was loaded for const coveragePeriodRef = useRef(null) const qualityPeriodRef = useRef(null) const psaPeriodRef = useRef(null) // Load overview data useEffect(() => { setLoading(true) Promise.all([ flowpilotAnalyticsApi.getDashboard(period), flowpilotAnalyticsApi.getKnowledgeGaps(period), ]) .then(([d, g]) => { setDashboard(d) setGaps(g) }) .catch(() => toast.error('Failed to load analytics')) .finally(() => setLoading(false)) // Reset lazy-loaded data when period changes coveragePeriodRef.current = null qualityPeriodRef.current = null psaPeriodRef.current = null setCoverageData(null) setQualityData(null) setPsaData(null) setCoverageLoading(true) setQualityLoading(true) setPsaLoading(true) setCoverageError(false) setQualityError(false) setPsaError(false) }, [period]) // Lazy-load tab data on tab switch or period change useEffect(() => { if (activeTab === 'coverage' && coveragePeriodRef.current !== period) { setCoverageLoading(true) setCoverageError(false) flowpilotAnalyticsApi.getCoverage(period) .then((data) => { setCoverageData(data) coveragePeriodRef.current = period }) .catch(() => { toast.error('Failed to load coverage data') setCoverageError(true) }) .finally(() => setCoverageLoading(false)) } if (activeTab === 'quality' && qualityPeriodRef.current !== period) { setQualityLoading(true) setQualityError(false) flowpilotAnalyticsApi.getFlowQuality(period) .then((data) => { setQualityData(data) qualityPeriodRef.current = period }) .catch(() => { toast.error('Failed to load flow quality data') setQualityError(true) }) .finally(() => setQualityLoading(false)) } if (activeTab === 'psa' && psaPeriodRef.current !== period) { setPsaLoading(true) setPsaError(false) flowpilotAnalyticsApi.getPsaMetrics(period) .then((data) => { setPsaData(data) psaPeriodRef.current = period }) .catch(() => { toast.error('Failed to load PSA metrics') setPsaError(true) }) .finally(() => setPsaLoading(false)) } }, [activeTab, period, retryKey]) if (loading) { return (
) } if (!dashboard) { return (

Failed to load analytics

) } const conf = dashboard.confidence_breakdown return (
{/* Header */}

FlowPilot Analytics

Team Analytics
{/* Tab bar */}
{TABS.map((tab) => ( ))}
{/* Tab content */} {activeTab === 'overview' && (
{/* Top row — Key metrics */}
{/* Second row — Charts */}
{/* MTTR Trend */}

MTTR Trend

{dashboard.mttr_trend.length > 0 ? ( new Date(d).toLocaleDateString([], { month: 'short', day: 'numeric' })} /> `${Math.round(v)}m`} /> [`${Math.round(v ?? 0)}m`, 'MTTR']} /> ) : (
No data for this period
)}
{/* Domain Breakdown */}

Sessions by Domain

{dashboard.sessions_by_domain.length > 0 ? ( ) : (
No domain data
)}
{/* Third row — Confidence + Knowledge Coverage */}
{/* Confidence Breakdown */}

Confidence Tiers

{/* Knowledge Coverage */}

Knowledge Coverage

Total Flows

{dashboard.knowledge_coverage.total_flows}

AI-Generated

{dashboard.knowledge_coverage.ai_generated_flows}

Pending Review

{dashboard.knowledge_coverage.total_proposals_pending}

Approved This Period

{dashboard.knowledge_coverage.proposals_approved_this_period}

{dashboard.knowledge_coverage.total_proposals_pending > 0 && ( Review {dashboard.knowledge_coverage.total_proposals_pending} pending proposals )}
{/* Fourth row — Knowledge Gaps */} {gaps && gaps.gaps.length > 0 ? (

Knowledge Gaps ({gaps.gaps.length})

{gaps.gaps.map((gap, i) => ( ))}
) : gaps && (

No knowledge gaps detected

Your flow coverage looks healthy for this period.

)}
)} {activeTab === 'coverage' && (
{coverageError ? ( { setCoverageError(false); coveragePeriodRef.current = null; setRetryKey((k) => k + 1) }} /> ) : coverageLoading ? (
) : coverageData ? ( ) : null}
)} {activeTab === 'quality' && (
{qualityError ? ( { setQualityError(false); qualityPeriodRef.current = null; setRetryKey((k) => k + 1) }} /> ) : qualityLoading ? (
) : qualityData ? ( ) : null}
)} {activeTab === 'psa' && (
{psaError ? ( { setPsaError(false); psaPeriodRef.current = null; setRetryKey((k) => k + 1) }} /> ) : psaLoading ? (
) : psaData ? ( ) : null}
)}
) } // ── Sub-components ── function MetricCard({ icon: Icon, label, value, iconColor, }: { icon: typeof BarChart3 label: string value: string | number iconColor: string }) { return (

{label}

{value}

) } function ConfidenceTierRow({ label, count, rate, color, total, }: { label: string count: number rate: number color: string total: number }) { const pct = total > 0 ? (count / total) * 100 : 0 return (
{label} {count} sessions · {rate.toFixed(1)}% resolved
) } function ErrorRetry({ label, onRetry }: { label: string; onRetry: () => void }) { return (

Failed to load {label}

) } function GapCard({ gap }: { gap: KnowledgeGap }) { const severityStyle = SEVERITY_STYLES[gap.severity as keyof typeof SEVERITY_STYLES] ?? SEVERITY_STYLES.low return (

{gap.title}

{gap.severity}

{gap.description}

{gap.suggested_action}

) }