import { useEffect, useState } from 'react'; import Box from '@mui/material/Box'; import Card from '@mui/material/Card'; import CardActionArea from '@mui/material/CardActionArea'; import CardContent from '@mui/material/CardContent'; import Typography from '@mui/material/Typography'; import type { QualityGroupDetail } from '../types/labeleval'; import { getDatasets, getQualityGroups, getQualityGroupDetail, usePoll, } from '../services/labeleval'; import { useLabelEval } from '../context/LabelEvalContext'; import { glossaryKeyForStatus } from '../content/glossary'; import DatasetSelector from '../components/labeleval/DatasetSelector'; import { SectionCard, LoadingBox, ErrorNote, HBar, fmtInt, fmtPct, fmtNum, MetricCard, } from '../components/labeleval/shared'; const GROUP_COLORS: Record = { verified: '#66bb6a', non_verified: '#ffa726', hitl: '#42a5f5', rejected: '#ef5350', }; export default function DatasetsPage() { const { activeDatasetId, setActiveDatasetId } = useLabelEval(); const datasets = usePoll(getDatasets, 5000); const groups = usePoll( () => getQualityGroups(activeDatasetId), activeDatasetId ? 5000 : null, [activeDatasetId] ); const [groupDetail, setGroupDetail] = useState(null); const [detailLoading, setDetailLoading] = useState(false); const [detailError, setDetailError] = useState(null); const [verificationRates, setVerificationRates] = useState>({}); // DatasetSummary carries no verification rate, so pull it from each // dataset's quality groups whenever the dataset list changes. const datasetIdsKey = (datasets.data?.datasets ?? []).map((d) => d.dataset_id).join(','); useEffect(() => { let cancelled = false; const ids = datasetIdsKey ? datasetIdsKey.split(',') : []; if (ids.length === 0) return; Promise.all( ids.map((id) => getQualityGroups(id) .then((g) => [id, g.verification_rate] as const) .catch(() => null) ) ).then((entries) => { if (cancelled) return; const next: Record = {}; for (const e of entries) { if (e) next[e[0]] = e[1]; } setVerificationRates(next); }); return () => { cancelled = true; }; }, [datasetIdsKey]); useEffect(() => { setGroupDetail(null); setDetailError(null); }, [activeDatasetId]); const openGroup = async (groupId: string) => { setDetailLoading(true); setDetailError(null); try { setGroupDetail(await getQualityGroupDetail(groupId)); } catch (err) { setDetailError(err instanceof Error ? err.message : String(err)); } finally { setDetailLoading(false); } }; const failureRows = groupDetail ? Object.entries(groupDetail.failure_reason_counts).sort((a, b) => b[1] - a[1]) : []; const maxFailure = Math.max(1, ...failureRows.map(([, c]) => c)); return ( {datasets.loading && !datasets.data ? : null} {datasets.error && !datasets.data ? : null} {datasets.data ? ( ) : null} {activeDatasetId ? ( {groups.error && !groups.data ? : null} {groups.data ? ( <> {fmtInt(groups.data.total)} annotations · verification rate {fmtPct(groups.data.verification_rate)}. Click a group for its quality profile. {groups.data.groups.map((g) => ( void openGroup(g.group_id)}> {g.name.replace(/_/g, ' ')} {fmtInt(g.count)} {fmtPct(g.pct)} of annotations ))} ) : !groups.error ? ( ) : null} {detailLoading ? : null} {detailError ? : null} {groupDetail && !detailLoading ? ( {groupDetail.name.replace(/_/g, ' ').toUpperCase()} — quality profile {failureRows.length > 0 ? ( <> FAILURE REASON BREAKDOWN {failureRows.map(([reason, count]) => ( ))} ) : ( No failure reasons recorded for this group. )} ) : null} ) : ( Select a dataset above to drill into its quality groups. )} ); }