'use client'; import { useEffect, useState } from 'react'; import { getAnnotationDistribution, DistributionDimension, DistributionData, DistributionItem, getAnnotatedSamples, AnnotatedSample } from '@/app/actions/annotation-stats'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; import WaveformPlayer from './WaveformPlayer'; interface AnnotationDistributionProps { experimentId: number; } export default function AnnotationDistribution({ experimentId }: AnnotationDistributionProps) { const [dimension, setDimension] = useState('dialect'); const [data, setData] = useState(null); const [samples, setSamples] = useState([]); const [loading, setLoading] = useState(true); const [samplesLoading, setSamplesLoading] = useState(true); const [error, setError] = useState(null); const [samplesError, setSamplesError] = useState(null); const dimensions: { value: DistributionDimension; label: string }[] = [ { value: 'dialect', label: 'Dialect' }, { value: 'utteranceId', label: 'Utterance ID' }, { value: 'model', label: 'Model' }, { value: 'speaker', label: 'Speaker' }, ]; useEffect(() => { const fetchData = async () => { try { setLoading(true); const result = await getAnnotationDistribution(experimentId, dimension); setData(result); setError(null); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load distribution'); setData(null); } finally { setLoading(false); } }; fetchData(); }, [experimentId, dimension]); useEffect(() => { const fetchSamples = async () => { try { setSamplesLoading(true); const result = await getAnnotatedSamples(experimentId); setSamples(result); setSamplesError(null); } catch (err) { setSamplesError(err instanceof Error ? err.message : 'Failed to load samples'); setSamples([]); } finally { setSamplesLoading(false); } }; fetchSamples(); }, [experimentId]); // Transform data for recharts const chartData = data?.items.map((item) => ({ name: item.label, percentage: data.total > 0 ? ((item.count / data.total) * 100).toFixed(1) : 0, ...item, })) || []; // Calculate per-sample distribution (histogram of annotation counts) const sampleDistribution: { annotationCount: number; sampleCount: number; percentage: string }[] = []; const countMap = new Map(); samples.forEach((sample) => { const count = sample.annotationCount; countMap.set(count, (countMap.get(count) || 0) + 1); }); // Convert to sorted array with percentages const totalSamples = samples.length; Array.from(countMap.entries()) .map(([count, sampleCount]) => ({ annotationCount: count, sampleCount, percentage: totalSamples > 0 ? ((sampleCount / totalSamples) * 100).toFixed(1) : '0', })) .sort((a, b) => a.annotationCount - b.annotationCount) .forEach((item) => sampleDistribution.push(item)); return (

Annotation Distribution

{/* Dimension Selector */}
{loading && (
Loading distribution data...
)} {error && (
Error: {error}
)} {data && !loading && (
{/* Chart Section */}
{chartData.length > 0 ? ( { if (name === 'count') return [value, 'Count']; return [value, name]; }} content={({ active, payload }) => { if (active && payload && payload.length) { const data = payload[0].payload; return (

{data.name}

Count: {data.count}

Percentage: {data.percentage}%

); } return null; }} />
) : (
No annotations found for this distribution
)}
)} {/* Sample Annotation Distribution Chart */} {!samplesLoading && sampleDistribution.length > 0 && (

Sample Annotation Coverage

{ if (active && payload && payload.length) { const data = payload[0].payload; return (

{data.annotationCount} annotation{data.annotationCount !== 1 ? 's' : ''}

{data.percentage}% of samples

({data.sampleCount} sample{data.sampleCount !== 1 ? 's' : ''})

); } return null; }} />
)}
); }