import React, { useEffect, useState, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { Play, Pause, CheckCircle, Clock, RefreshCw, Zap, FileText, ChevronDown, ChevronUp, ChevronRight, Target, Activity, BarChart3, TrendingUp, ArrowRight, Folder, FolderOpen, Maximize2, X } from 'lucide-react'; import { useStudy } from '../context/StudyContext'; import { Study } from '../types'; import { apiClient } from '../api/client'; import { MarkdownRenderer } from '../components/MarkdownRenderer'; const Home: React.FC = () => { const { studies, setSelectedStudy, refreshStudies, isLoading } = useStudy(); const [selectedPreview, setSelectedPreview] = useState(null); const [readme, setReadme] = useState(''); const [readmeLoading, setReadmeLoading] = useState(false); const [sortField, setSortField] = useState<'name' | 'status' | 'trials' | 'bestValue'>('trials'); const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc'); const [expandedTopics, setExpandedTopics] = useState>(new Set()); const [isFullscreen, setIsFullscreen] = useState(false); const navigate = useNavigate(); // Group studies by topic, sorted by most recent first const studiesByTopic = useMemo(() => { const grouped: Record = {}; studies.forEach(study => { const topic = study.topic || 'Other'; if (!grouped[topic]) grouped[topic] = []; grouped[topic].push(study); }); // Sort studies within each topic by last_modified (most recent first) Object.keys(grouped).forEach(topic => { grouped[topic].sort((a, b) => { const aTime = a.last_modified ? new Date(a.last_modified).getTime() : 0; const bTime = b.last_modified ? new Date(b.last_modified).getTime() : 0; return bTime - aTime; // Descending (most recent first) }); }); // Get most recent study time for each topic (for topic sorting) const topicMostRecent: Record = {}; Object.keys(grouped).forEach(topic => { const mostRecent = grouped[topic][0]?.last_modified; topicMostRecent[topic] = mostRecent ? new Date(mostRecent).getTime() : 0; }); // Sort topics by most recent study (most recent first), 'Other' always last const sortedTopics = Object.keys(grouped).sort((a, b) => { if (a === 'Other') return 1; if (b === 'Other') return -1; return topicMostRecent[b] - topicMostRecent[a]; // Descending (most recent first) }); const result: Record = {}; sortedTopics.forEach(topic => { result[topic] = grouped[topic]; }); return result; }, [studies]); // Topics start collapsed by default - no initialization needed // Users can expand topics by clicking on them const toggleTopic = (topic: string) => { setExpandedTopics(prev => { const next = new Set(prev); if (next.has(topic)) { next.delete(topic); } else { next.add(topic); } return next; }); }; // Load README when a study is selected for preview useEffect(() => { if (selectedPreview) { loadReadme(selectedPreview.id); } else { setReadme(''); } }, [selectedPreview]); const loadReadme = async (studyId: string) => { setReadmeLoading(true); try { const response = await apiClient.getStudyReadme(studyId); setReadme(response.content || 'No README found for this study.'); } catch { setReadme('No README found for this study.'); } finally { setReadmeLoading(false); } }; const handleSelectStudy = (study: Study) => { setSelectedStudy(study); navigate('/dashboard'); }; const handleSort = (field: typeof sortField) => { if (sortField === field) { setSortDir(sortDir === 'asc' ? 'desc' : 'asc'); } else { setSortField(field); setSortDir('desc'); } }; // Sort studies const sortedStudies = useMemo(() => { return [...studies].sort((a, b) => { let aVal: any, bVal: any; switch (sortField) { case 'name': aVal = (a.name || a.id).toLowerCase(); bVal = (b.name || b.id).toLowerCase(); break; case 'trials': aVal = a.progress.current; bVal = b.progress.current; break; case 'bestValue': aVal = a.best_value ?? Infinity; bVal = b.best_value ?? Infinity; break; case 'status': const statusOrder = { running: 0, paused: 1, completed: 2, not_started: 3 }; aVal = statusOrder[a.status as keyof typeof statusOrder] ?? 4; bVal = statusOrder[b.status as keyof typeof statusOrder] ?? 4; break; default: return 0; } if (sortDir === 'asc') { return aVal < bVal ? -1 : aVal > bVal ? 1 : 0; } else { return aVal > bVal ? -1 : aVal < bVal ? 1 : 0; } }); }, [studies, sortField, sortDir]); // Aggregate stats const aggregateStats = useMemo(() => { const totalStudies = studies.length; const runningStudies = studies.filter(s => s.status === 'running').length; const completedStudies = studies.filter(s => s.status === 'completed').length; const totalTrials = studies.reduce((sum, s) => sum + s.progress.current, 0); const studiesWithValues = studies.filter(s => s.best_value !== null); const bestOverall = studiesWithValues.length > 0 ? studiesWithValues.reduce((best, curr) => (curr.best_value! < best.best_value!) ? curr : best ) : null; return { totalStudies, runningStudies, completedStudies, totalTrials, bestOverall }; }, [studies]); const getStatusIcon = (status: string) => { switch (status) { case 'running': return ; case 'paused': return ; case 'completed': return ; default: return ; } }; const getStatusColor = (status: string) => { switch (status) { case 'running': return 'text-green-400 bg-green-500/10'; case 'paused': return 'text-orange-400 bg-orange-500/10'; case 'completed': return 'text-blue-400 bg-blue-500/10'; default: return 'text-dark-400 bg-dark-600'; } }; return (
{/* Header */}

Atomizer

FEA Optimization Platform

{/* Aggregate Stats Cards */}
Total Studies
{aggregateStats.totalStudies}
Running
{aggregateStats.runningStudies}
Total Trials
{aggregateStats.totalTrials.toLocaleString()}
Best Overall
{aggregateStats.bestOverall?.best_value !== null && aggregateStats.bestOverall?.best_value !== undefined ? aggregateStats.bestOverall.best_value.toExponential(3) : 'N/A'}
{aggregateStats.bestOverall && (
{aggregateStats.bestOverall.name || aggregateStats.bestOverall.id}
)}
{/* Two-column layout: Table + Preview */}
{/* Study Table */}

Studies

{studies.length} studies
{isLoading ? (
Loading studies...
) : studies.length === 0 ? (

No studies found

Create a new study to get started

) : (
{Object.entries(studiesByTopic).map(([topic, topicStudies]) => { const isExpanded = expandedTopics.has(topic); const topicTrials = topicStudies.reduce((sum, s) => sum + s.progress.current, 0); const runningCount = topicStudies.filter(s => s.status === 'running').length; return (
{/* Topic Header */} {/* Topic Studies */} {isExpanded && (
{topicStudies.map((study) => { const completionPercent = study.progress.total > 0 ? Math.round((study.progress.current / study.progress.total) * 100) : 0; return (
setSelectedPreview(study)} className={`px-4 py-3 pl-12 flex items-center gap-4 border-t border-dark-700 hover:bg-dark-700 transition-colors cursor-pointer ${ selectedPreview?.id === study.id ? 'bg-primary-900/20' : '' }`} > {/* Study Name */}
{study.name || study.id} {study.name && ( {study.id} )}
{/* Status */} {getStatusIcon(study.status)} {study.status} {/* Progress */}
= 100 ? 'bg-green-500' : completionPercent >= 50 ? 'bg-primary-500' : 'bg-yellow-500' }`} style={{ width: `${Math.min(completionPercent, 100)}%` }} />
{study.progress.current}/{study.progress.total}
{/* Best Value */} {study.best_value !== null ? study.best_value.toExponential(2) : 'N/A'}
); })}
)}
); })}
)}
{/* Study Preview */}
{selectedPreview ? ( <> {/* Preview Header */}

{selectedPreview.name || selectedPreview.id}

Study Documentation

{/* Study Quick Stats */}
{getStatusIcon(selectedPreview.status)} {selectedPreview.status}
{selectedPreview.progress.current} / {selectedPreview.progress.total} trials
{selectedPreview.best_value !== null && (
Best: {selectedPreview.best_value.toExponential(4)}
)}
{/* README Content */}
{readmeLoading ? (
Loading documentation...
) : ( )}
) : (

Select a study to preview

Click on any row in the table

)}
{/* Fullscreen README Modal */} {isFullscreen && selectedPreview && (
{/* Modal Header */}

{selectedPreview.name || selectedPreview.id}

Study Documentation

{/* Modal Content */}
{readmeLoading ? (
Loading documentation...
) : ( )}
)}
); }; export default Home;