/** * 🏥 Data Health Card - Shows data quality score before training * * Features: * - Overall health score (0-100) with grade * - Issue count by severity * - Expandable recommendations list */ import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Activity, AlertTriangle, AlertCircle, CheckCircle, ChevronDown, ChevronUp, RefreshCw, Info } from 'lucide-react'; import { useUserStore } from '@/store/userStore'; import { getUserIdSync } from '@/utils/userId'; interface HealthIssue { severity: string; category: string; column: string | null; description: string; recommendation: string; } interface HealthResponse { success: boolean; overall_score: number; grade: string; issues: HealthIssue[]; recommendations: string[]; metrics: Record; column_scores: Record; } interface DataHealthCardProps { fileName: string; targetColumn?: string; onHealthChecked?: (health: HealthResponse) => void; } const DataHealthCard: React.FC = ({ fileName, targetColumn, onHealthChecked }) => { const { isDark } = useUserStore(); const [loading, setLoading] = useState(false); const [health, setHealth] = useState(null); const [error, setError] = useState(null); const [expanded, setExpanded] = useState(false); // Check health when file changes - reset state first useEffect(() => { // Reset state when file changes setHealth(null); setError(null); setLoading(true); if (fileName) { checkHealth(); } else { setLoading(false); } }, [fileName, targetColumn]); const checkHealth = async () => { try { setLoading(true); setError(null); const userId = getUserIdSync(); const response = await fetch('/api/v1/automl/health', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file_name: fileName, target_column: targetColumn, user_id: userId }) }); const data = await response.json(); if (response.ok && data.success) { setHealth(data); onHealthChecked?.(data); } else { setError(data.detail || 'Health check failed'); } } catch (err) { setError('Failed to check data health'); } finally { setLoading(false); } }; const getGradeColor = (grade: string) => { switch (grade) { case 'A': return '#10b981'; // Green case 'B': return '#22c55e'; // Light green case 'C': return '#f59e0b'; // Yellow case 'D': return '#f97316'; // Orange case 'F': return '#ef4444'; // Red default: return '#6b7280'; // Gray } }; const getScoreColor = (score: number) => { if (score >= 80) return '#10b981'; if (score >= 60) return '#f59e0b'; return '#ef4444'; }; const getSeverityIcon = (severity: string) => { switch (severity) { case 'critical': return ; case 'warning': return ; default: return ; } }; if (loading) { return (
Analyzing data quality...
); } if (error) { return (
Health check failed: {error}
); } if (!health) return null; const criticalCount = health.issues.filter(i => i.severity === 'critical').length; const warningCount = health.issues.filter(i => i.severity === 'warning').length; return ( {/* Header with Score */}
{health.grade}
Data Health Score
{health.overall_score.toFixed(0)}/100 {criticalCount > 0 && ( {criticalCount} critical )} {warningCount > 0 && ( {warningCount} warnings )}
{/* Progress Bar */}
{/* Expandable Details */} {expanded && (
{/* Issues List */} {health.issues.length > 0 && (

Issues Found ({health.issues.length})

{health.issues.slice(0, 5).map((issue, i) => (
{getSeverityIcon(issue.severity)}

{issue.description}

{issue.column && ( Column: {issue.column} )}
))}
)} {/* Recommendations */} {health.recommendations.length > 0 && (

💡 Recommendations

    {health.recommendations.slice(0, 3).map((rec, i) => (
  • {rec}
  • ))}
)}
)}
); }; export default DataHealthCard;