import React, { useState, useEffect } from 'react'; import { Calendar, Clock, BarChart2, Database, ChevronLeft, ChevronRight, Rocket, Loader2, LineChart } from 'lucide-react'; import type { Model, RetrainingJob, RetrainingRun } from '../types'; import { router } from "@inertiajs/react"; interface ModelDetailsProps { model: Model; onBack: () => void; } interface PaginatedRuns { runs: RetrainingRun[]; total_count: number; limit: number; offset: number; } const ITEMS_PER_PAGE = 3; const METRIC_MAPPINGS: Record = { root_mean_squared_error: 'rmse', mean_absolute_error: 'mae', mean_squared_error: 'mse', r2_score: 'r2', accuracy_score: 'accuracy', precision_score: 'precision', recall_score: 'recall' }; export function ModelDetails({ model, onBack, rootPath }: ModelDetailsProps) { const [activeTab, setActiveTab] = useState<'overview' | 'dataset'>('overview'); const [runs, setRuns] = useState(model.retraining_runs?.runs || []); const [loading, setLoading] = useState(false); const [pagination, setPagination] = useState({ offset: 0, limit: 20, total_count: model.retraining_runs?.total_count || 0 }); const [currentPage, setCurrentPage] = useState(1); const dataset = model.dataset; const job = model.retraining_job; const hasMoreRuns = pagination.offset + pagination.limit < pagination.total_count; useEffect(() => { let pollInterval: number | undefined; const deployingRun = runs.find(run => run.is_deploying); if (deployingRun) { pollInterval = window.setInterval(async () => { router.get(window.location.href, { preserveScroll: true, preserveState: true, only: ['runs'] }) }, 2000); } return () => { if (pollInterval) { window.clearInterval(pollInterval); } }; }, [runs]); const handleDeploy = async (run: RetrainingRun) => { if (run.is_deploying) return; const updatedRuns = runs.map(r => r.id === run.id ? { ...r, is_deploying: true } : r ); setRuns(updatedRuns); try { await router.post(`${rootPath}/models/${model.id}/deploys`, { retraining_run_id: run.id }, { preserveScroll: true, preserveState: true }); } catch (error) { console.error('Failed to deploy model:', error); // Reset deploying state on error const resetRuns = runs.map(r => r.id === run.id ? { ...r, is_deploying: false } : r ); setRuns(resetRuns); } }; useEffect(() => { const totalPages = Math.ceil(runs.length / ITEMS_PER_PAGE); const remainingPages = totalPages - currentPage; if (remainingPages <= 2 && hasMoreRuns) { loadMoreRuns(); } }, [currentPage, runs, hasMoreRuns]); const totalPages = Math.ceil(runs.length / ITEMS_PER_PAGE); const paginatedRuns = runs.slice( (currentPage - 1) * ITEMS_PER_PAGE, currentPage * ITEMS_PER_PAGE ); const updateCurrentPage = (newPage) => { setCurrentPage(newPage); if (totalPages - newPage < 2 && hasMoreRuns) { loadMoreRuns(); } } const isCurrentlyDeployed = (run: RetrainingRun) => { return run.status === 'deployed'; }; const formatMetricKey = (key: string) => { return METRIC_MAPPINGS[key] || key; }; return (

{model.name}

Version: {model.version} • Type: {model.formatted_model_type}

{model.deployment_status}
{job && (

Training Schedule

{job.active ? `Runs ${job.formatted_frequency}` : "None (Triggered Manually)"}
{ job.active && (
at {job.at.hour}:00
) }
)}
{activeTab === 'overview' ? (

Retraining Runs

Page {currentPage} of {totalPages}
{paginatedRuns.map((run, index) => (
{ !isCurrentlyDeployed(run) && ( {run.status} ) } {isCurrentlyDeployed(run) && ( deployed )} {run.metrics_url && ( metrics )}
{new Date(run.started_at).toLocaleString()} {run.status === 'success' && run.deployable && (
{isCurrentlyDeployed(run) ? ( deployed ) : ( )}
)}
{run && run.metrics && (
{Object.entries( run.metrics as Record ).map(([key, value]) => (
{formatMetricKey(key)}
{value.toFixed(4)}
))}
)}
))}
) : ( dataset && (

{dataset.name}

Columns

{dataset.columns.map(column => (
{column.name} {column.type}
))}

Statistics

Total Rows {dataset.num_rows.toLocaleString()}
Last Updated {new Date(dataset.updated_at).toLocaleDateString()}
) )}
); }