import { useState } from 'react'; import type { AnalyzedEntry, DataQualityReport } from '../../types'; import { AlertTriangle, Copy, Globe, TrendingDown, ChevronDown, ChevronUp } from 'lucide-react'; interface DataQualityPanelProps { report: DataQualityReport; entries?: AnalyzedEntry[]; } type DrillDownType = 'low_confidence' | 'mixed_language' | 'duplicates' | null; export function DataQualityPanel({ report, entries = [] }: DataQualityPanelProps) { const [drillDown, setDrillDown] = useState(null); const issueCount = report.low_confidence_count + report.mixed_language_count + report.duplicate_count; const healthScore = Math.max(0, 100 - (issueCount / Math.max(1, report.total_entries)) * 100); const toggleDrillDown = (type: DrillDownType) => { setDrillDown(drillDown === type ? null : type); }; const getDrillDownEntries = (): AnalyzedEntry[] => { if (!drillDown || entries.length === 0) return []; const idSet = new Set( drillDown === 'low_confidence' ? report.low_confidence_entries : drillDown === 'mixed_language' ? report.mixed_language_entries : report.duplicate_entries ); return entries.filter((e) => idSet.has(e.id)); }; const drillDownEntries = getDrillDownEntries(); return (
toggleDrillDown('low_confidence')} role="button" tabIndex={0} onKeyDown={(e) => e.key === 'Enter' && toggleDrillDown('low_confidence')} >
Low Confidence
{report.low_confidence_count}
predictions below 50% confidence {report.low_confidence_count > 0 && ( drillDown === 'low_confidence' ? : )}
toggleDrillDown('mixed_language')} role="button" tabIndex={0} onKeyDown={(e) => e.key === 'Enter' && toggleDrillDown('mixed_language')} >
Mixed Language
{report.mixed_language_count}
entries differ from majority language {report.mixed_language_count > 0 && ( drillDown === 'mixed_language' ? : )}
toggleDrillDown('duplicates')} role="button" tabIndex={0} onKeyDown={(e) => e.key === 'Enter' && toggleDrillDown('duplicates')} >
Duplicates
{report.duplicate_count}
exact or near-duplicate entries {report.duplicate_count > 0 && ( drillDown === 'duplicates' ? : )}
{/* Drill-down entries table */} {drillDown && (

{drillDown === 'low_confidence' ? 'Low Confidence Entries' : drillDown === 'mixed_language' ? 'Mixed Language Entries' : 'Duplicate Entries'}

{drillDownEntries.length > 0 ? `${drillDownEntries.length} entries` : `${drillDown === 'low_confidence' ? report.low_confidence_count : drillDown === 'mixed_language' ? report.mixed_language_count : report.duplicate_count} entry IDs found`}
{drillDownEntries.length > 0 ? ( {drillDown === 'low_confidence' && } {drillDownEntries.slice(0, 50).map((entry) => ( {drillDown === 'low_confidence' && ( )} ))}
Text Sentiment Confidence LanguageScore
{entry.text} {entry.sentiment.label} {(entry.sentiment.confidence * 100).toFixed(1)}% {entry.language.language} {entry.sentiment.score.toFixed(3)}
) : (

Entry details not available. IDs:

{(drillDown === 'low_confidence' ? report.low_confidence_entries : drillDown === 'mixed_language' ? report.mixed_language_entries : report.duplicate_entries).slice(0, 20).map((id) => ( {id} ))}
)}
)}

Data Health Score

80 ? 'badge-positive' : healthScore > 50 ? 'badge-warning' : 'badge-negative'}`}> {healthScore.toFixed(0)}%
80 ? 'var(--success)' : healthScore > 50 ? 'var(--warning)' : 'var(--danger)', }} />
Language Distribution
{Object.entries(report.language_distribution).map(([lang, count]) => ( {lang}: {count} ))}
Average Confidence
{(report.avg_confidence * 100).toFixed(1)}%
{issueCount > 0 && (
{issueCount} data quality issue{issueCount !== 1 ? 's' : ''} detected. Click the cards above to review affected entries.
)}
); }