/** * 🗄️ Model History Component * * Displays all trained models for the current user with: * - Version history * - Metrics comparison * - Rollback functionality * - Delete options */ import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { History, Award, Clock, Target, TrendingUp, RotateCcw, Trash2, ChevronRight, CheckCircle, AlertCircle, Loader, Database, Download, RefreshCw, } from 'lucide-react'; import { useConfirmModal } from '@/components/ui/ConfirmModal'; import { useToast } from '@/contexts/ToastContext'; import { getAuthHeadersSync } from '@/utils/userId'; interface ModelMetadata { model_id: string; user_id: string; model_name: string; task_type: string; target_column: string; feature_columns: string[]; metrics: Record; training_date: string; dataset_info: { n_features: number; n_numeric: number; n_categorical: number; n_text: number; is_nlp_task: boolean; }; version: number; is_active: boolean; } interface ModelHistoryProps { userId?: string; onModelChange?: () => void; // Callback when active model changes } const ModelHistory: React.FC = ({ userId = 'default', onModelChange }) => { const [models, setModels] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [rollingBack, setRollingBack] = useState(null); const [deleting, setDeleting] = useState(null); const [successMessage, setSuccessMessage] = useState(null); // Custom hooks for themed modals and toasts const { confirm, ConfirmModal } = useConfirmModal(); const toast = useToast(); // Fetch models on mount useEffect(() => { fetchModels(); }, [userId]); const fetchModels = async () => { setLoading(true); setError(null); try { const response = await fetch(`/api/v2/autonomous/models/${userId}`, { headers: getAuthHeadersSync() }); const data = await response.json(); if (data.success) { setModels(data.models || []); } else { setError(data.message || 'Failed to load models'); } } catch (err) { setError('Failed to connect to API'); console.error('Fetch models error:', err); } finally { setLoading(false); } }; const handleRollback = async (version: number) => { setRollingBack(version); setSuccessMessage(null); try { const response = await fetch('/api/v2/autonomous/models/rollback', { method: 'POST', headers: getAuthHeadersSync(), body: JSON.stringify({ user_id: userId, version }) }); const data = await response.json(); if (data.success) { setSuccessMessage(`Rolled back to version ${version}`); await fetchModels(); onModelChange?.(); // Notify parent to refresh overview } else { setError(data.detail || 'Rollback failed'); } } catch (err) { setError('Rollback failed'); } finally { setRollingBack(null); } }; const handleDelete = async (version?: number) => { const confirmed = await confirm({ title: version ? 'Delete Model Version' : 'Delete All Models', message: version ? `Are you sure you want to delete version ${version}? This action cannot be undone.` : 'Are you sure you want to delete all trained models? This will remove all model versions and cannot be undone.', confirmText: 'Delete', variant: 'danger', icon: }); if (!confirmed) return; setDeleting(version || -1); try { const url = version ? `/api/v2/autonomous/models/${userId}/version/${version}` : `/api/v2/autonomous/models/${userId}`; const response = await fetch(url, { method: 'DELETE', headers: getAuthHeadersSync() }); const data = await response.json(); if (data.success) { toast.deleted(version ? `Model version ${version} deleted` : 'All models deleted successfully'); await fetchModels(); } else { toast.error(data.detail || 'Delete failed'); } } catch (err) { toast.error('Failed to delete model(s)'); } finally { setDeleting(null); } }; const formatDate = (dateStr: string) => { try { const date = new Date(dateStr); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch { return dateStr; } }; const getMetricColor = (value: number) => { if (value >= 0.9) return '#10b981'; if (value >= 0.7) return '#f59e0b'; return '#ef4444'; }; if (loading) { return (

Loading model history...

); } if (error) { return (

Error loading models

{error}

); } if (models.length === 0) { return (

No Trained Models

Train a model in DataHub to see your model history here.

); } const activeModel = models.find(m => m.is_active); const historyModels = models.filter(m => !m.is_active); return (
{/* Header */}

Model History

{models.length} model version{models.length !== 1 ? 's' : ''} saved

{/* Success Message */} {successMessage && ( {successMessage} )} {/* Active Model */} {activeModel && (
ACTIVE
Version {activeModel.version}

Model

{activeModel.model_name}

Task

{activeModel.task_type}

Target

{activeModel.target_column}

Trained

{formatDate(activeModel.training_date)}

{/* Metrics */}
{Object.entries(activeModel.metrics).slice(0, 4).map(([key, value]) => (
{key.toUpperCase()}: {key === 'mae' || key === 'rmse' ? value.toFixed(3) : `${(value * 100).toFixed(1)}%`}
))}
)} {/* Version History */} {historyModels.length > 0 && (

Previous Versions

{historyModels.map((model, index) => (
v{model.version}

{model.model_name}

{formatDate(model.training_date)}

{/* Metrics preview */}
{Object.entries(model.metrics).slice(0, 2).map(([key, value]) => ( {key}: {(value * 100).toFixed(1)}% ))}
{/* Actions */}
))}
)} {/* Delete All */} {models.length > 1 && (
)} {/* Confirm Modal */}
); }; export default ModelHistory;