import React, { useState, useEffect } from 'react'; import { Play, CheckCircle, Settings, AlertTriangle, Loader2, ExternalLink, Sliders, Skull } from 'lucide-react'; import { apiClient, ProcessStatus } from '../../api/client'; import { useStudy } from '../../context/StudyContext'; interface ControlPanelProps { onStatusChange?: () => void; horizontal?: boolean; } export const ControlPanel: React.FC = ({ onStatusChange, horizontal = false }) => { const { selectedStudy, refreshStudies } = useStudy(); const [processStatus, setProcessStatus] = useState(null); const [actionInProgress, setActionInProgress] = useState(null); const [error, setError] = useState(null); const [showSettings, setShowSettings] = useState(false); // Settings for starting optimization const [settings, setSettings] = useState({ freshStart: false, maxIterations: 100, feaBatchSize: 5, tuneTrials: 30, ensembleSize: 3, patience: 5, }); // Validate top N const [validateTopN, setValidateTopN] = useState(5); useEffect(() => { if (selectedStudy) { fetchProcessStatus(); const interval = setInterval(fetchProcessStatus, 5000); return () => clearInterval(interval); } }, [selectedStudy]); const fetchProcessStatus = async () => { if (!selectedStudy) return; try { const status = await apiClient.getProcessStatus(selectedStudy.id); setProcessStatus(status); } catch (err) { // Process status endpoint might not exist yet setProcessStatus(null); } }; const handleStart = async () => { if (!selectedStudy) return; setActionInProgress('start'); setError(null); try { await apiClient.startOptimization(selectedStudy.id, { freshStart: settings.freshStart, maxIterations: settings.maxIterations, feaBatchSize: settings.feaBatchSize, tuneTrials: settings.tuneTrials, ensembleSize: settings.ensembleSize, patience: settings.patience, }); await fetchProcessStatus(); await refreshStudies(); onStatusChange?.(); } catch (err: any) { setError(err.message || 'Failed to start optimization'); } finally { setActionInProgress(null); } }; const handleStop = async () => { if (!selectedStudy) return; setActionInProgress('stop'); setError(null); try { await apiClient.stopOptimization(selectedStudy.id); await fetchProcessStatus(); await refreshStudies(); onStatusChange?.(); } catch (err: any) { setError(err.message || 'Failed to stop optimization'); } finally { setActionInProgress(null); } }; const handleValidate = async () => { if (!selectedStudy) return; setActionInProgress('validate'); setError(null); try { await apiClient.validateOptimization(selectedStudy.id, { topN: validateTopN }); await fetchProcessStatus(); await refreshStudies(); onStatusChange?.(); } catch (err: any) { setError(err.message || 'Failed to start validation'); } finally { setActionInProgress(null); } }; const handleLaunchOptuna = async () => { if (!selectedStudy) return; setActionInProgress('optuna'); setError(null); try { const result = await apiClient.launchOptunaDashboard(selectedStudy.id); window.open(result.url, '_blank'); } catch (err: any) { setError(err.message || 'Failed to launch Optuna dashboard'); } finally { setActionInProgress(null); } }; const isRunning = processStatus?.is_running || selectedStudy?.status === 'running'; // Horizontal layout for top of page if (horizontal) { return (
{/* Status */}
{isRunning ? ( <>
Running ) : ( <>
Stopped )}
{processStatus?.fea_count && ( FEA: {processStatus.fea_count} {processStatus.nn_count && ( <> | NN: {processStatus.nn_count} )} )}
{/* Main Actions */}
{isRunning ? ( ) : ( )}
{/* Settings Toggle */} {/* Error */} {error && (
{error}
)}
{/* Collapsible Settings */} {showSettings && (
setSettings({ ...settings, maxIterations: parseInt(e.target.value) || 100 })} className="w-16 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" />
setSettings({ ...settings, feaBatchSize: parseInt(e.target.value) || 5 })} className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" />
setSettings({ ...settings, patience: parseInt(e.target.value) || 5 })} className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" />
setSettings({ ...settings, tuneTrials: parseInt(e.target.value) || 30 })} className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" />
setSettings({ ...settings, ensembleSize: parseInt(e.target.value) || 3 })} className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" />
setValidateTopN(parseInt(e.target.value) || 5)} className="w-12 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm" />
)}
); } // Vertical layout (original sidebar layout) return (
{/* Header */}

Optimization Control

{/* Status */}
Status
{isRunning ? ( <>
Running ) : ( <>
Stopped )}
{processStatus && (
{processStatus.iteration && (
Iteration: {processStatus.iteration}
)} {processStatus.fea_count && (
FEA: {processStatus.fea_count} {processStatus.nn_count && ( <> | NN: {processStatus.nn_count} )}
)}
)}
{/* Settings Panel */} {showSettings && (
setSettings({ ...settings, maxIterations: parseInt(e.target.value) || 100 })} className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white text-sm" />
setSettings({ ...settings, feaBatchSize: parseInt(e.target.value) || 5 })} className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white text-sm" />
setSettings({ ...settings, patience: parseInt(e.target.value) || 5 })} className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white text-sm" />
setSettings({ ...settings, tuneTrials: parseInt(e.target.value) || 30 })} className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white text-sm" />
setSettings({ ...settings, ensembleSize: parseInt(e.target.value) || 3 })} className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white text-sm" />
)} {/* Error Message */} {error && (
{error}
)} {/* Actions */}
{/* Start / Kill Button */} {isRunning ? ( ) : ( )} {/* Validate Button */}
{/* Validation Settings */}
Validate top setValidateTopN(parseInt(e.target.value) || 5)} className="w-16 px-2 py-1 bg-dark-700 border border-dark-600 rounded text-white text-sm text-center" /> NN predictions with FEA
{/* Optuna Dashboard Button */}
); }; export default ControlPanel;