// Atomizer Dashboard - Frontend JavaScript const API_BASE = 'http://localhost:8080/api'; let currentStudy = null; let charts = { progress: null, designVars: null, constraints: null }; // Initialize dashboard document.addEventListener('DOMContentLoaded', () => { console.log('Atomizer Dashboard loaded'); refreshStudies(); startActiveOptimizationsPolling(); }); // ==================== // Studies Management // ==================== async function refreshStudies() { try { const response = await fetch(`${API_BASE}/studies`); const data = await response.json(); if (data.success) { renderStudiesList(data.studies); } else { showError('Failed to load studies: ' + data.error); } } catch (error) { showError('Connection error: ' + error.message); } } function renderStudiesList(studies) { const container = document.getElementById('studiesList'); if (!studies || studies.length === 0) { container.innerHTML = '

No studies found

'; return; } const html = studies.map(study => `
${study.study_name}
${study.total_trials || 0} trials ${study.resume_count > 0 ? `Resumed ${study.resume_count}x` : ''}
${formatDate(study.created_at)}
`).join(''); container.innerHTML = html; } async function loadStudy(studyName) { try { const response = await fetch(`${API_BASE}/studies/${studyName}`); const data = await response.json(); if (data.success) { currentStudy = studyName; displayStudyDetails(data); } else { showError('Failed to load study: ' + data.error); } } catch (error) { showError('Connection error: ' + error.message); } } function displayStudyDetails(data) { // Hide welcome, show details document.getElementById('welcomeScreen').style.display = 'none'; document.getElementById('studyDetails').style.display = 'block'; // Update header document.getElementById('studyTitle').textContent = data.study_name; document.getElementById('studyMeta').textContent = `Created: ${formatDate(data.metadata.created_at)} | Config Hash: ${data.metadata.config_hash.substring(0, 8)}`; // Update summary cards if (data.summary && data.summary.best_value !== undefined) { document.getElementById('bestObjective').textContent = data.summary.best_value.toFixed(4); document.getElementById('totalTrials').textContent = data.summary.n_trials || data.history.length; // Best parameters const paramsHtml = Object.entries(data.summary.best_params || {}) .map(([name, value]) => `
${name}: ${value.toFixed(4)}
`) .join(''); document.getElementById('bestParams').innerHTML = paramsHtml || '

No data

'; } // Render charts renderCharts(data.history); // Render history table renderHistoryTable(data.history); } // ==================== // Charts // ==================== async function renderCharts(history) { if (!history || history.length === 0) return; // Get visualization data try { const response = await fetch(`${API_BASE}/results/visualization/${currentStudy}`); const data = await response.json(); if (!data.success) return; // Progress Chart renderProgressChart(data.trials, data.total_objectives, data.running_best); // Design Variables Chart renderDesignVarsChart(data.trials, data.design_variables); // Constraints Chart renderConstraintsChart(data.trials, data.constraints); } catch (error) { console.error('Error rendering charts:', error); } } function renderProgressChart(trials, objectives, runningBest) { const ctx = document.getElementById('progressChart').getContext('2d'); if (charts.progress) { charts.progress.destroy(); } charts.progress = new Chart(ctx, { type: 'line', data: { labels: trials, datasets: [ { label: 'Total Objective', data: objectives, borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', tension: 0.1 }, { label: 'Running Best', data: runningBest, borderColor: '#10b981', backgroundColor: 'rgba(16, 185, 129, 0.1)', borderWidth: 2, tension: 0.1 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top' }, tooltip: { mode: 'index', intersect: false } }, scales: { x: { title: { display: true, text: 'Trial Number' } }, y: { title: { display: true, text: 'Objective Value' } } } } }); } function renderDesignVarsChart(trials, designVars) { const ctx = document.getElementById('designVarsChart').getContext('2d'); if (charts.designVars) { charts.designVars.destroy(); } const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6']; const datasets = Object.entries(designVars).map(([name, values], index) => ({ label: name, data: values, borderColor: colors[index % colors.length], backgroundColor: colors[index % colors.length] + '20', tension: 0.1 })); charts.designVars = new Chart(ctx, { type: 'line', data: { labels: trials, datasets: datasets }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top' } }, scales: { x: { title: { display: true, text: 'Trial Number' } }, y: { title: { display: true, text: 'Value' } } } } }); } function renderConstraintsChart(trials, constraints) { const ctx = document.getElementById('constraintsChart').getContext('2d'); if (charts.constraints) { charts.constraints.destroy(); } const colors = ['#3b82f6', '#10b981', '#f59e0b']; const datasets = Object.entries(constraints).map(([name, values], index) => ({ label: name, data: values, borderColor: colors[index % colors.length], backgroundColor: colors[index % colors.length] + '20', tension: 0.1 })); charts.constraints = new Chart(ctx, { type: 'line', data: { labels: trials, datasets: datasets }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top' } }, scales: { x: { title: { display: true, text: 'Trial Number' } }, y: { title: { display: true, text: 'Value' } } } } }); } // ==================== // History Table // ==================== function renderHistoryTable(history) { const tbody = document.querySelector('#historyTable tbody'); if (!history || history.length === 0) { tbody.innerHTML = 'No trials yet'; return; } const html = history.map(trial => ` ${trial.trial_number} ${trial.total_objective.toFixed(4)} ${formatDesignVars(trial.design_variables)} ${formatConstraints(trial.constraints)} ${formatDateTime(trial.timestamp)} `).join(''); tbody.innerHTML = html; } function formatDesignVars(vars) { return Object.entries(vars) .map(([name, value]) => `${name}=${value.toFixed(4)}`) .join(', '); } function formatConstraints(constraints) { return Object.entries(constraints) .map(([name, value]) => `${name}=${value.toFixed(4)}`) .join(', '); } // ==================== // New Optimization // ==================== function showNewOptimizationModal() { document.getElementById('newOptimizationModal').style.display = 'flex'; } function closeNewOptimizationModal() { document.getElementById('newOptimizationModal').style.display = 'none'; } async function startOptimization() { const studyName = document.getElementById('newStudyName').value || `study_${Date.now()}`; const nTrials = parseInt(document.getElementById('newTrials').value) || 50; const configPath = document.getElementById('newConfigPath').value; const resume = document.getElementById('resumeExisting').checked; try { const response = await fetch(`${API_BASE}/optimization/start`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ study_name: studyName, n_trials: nTrials, config_path: configPath, resume: resume }) }); const data = await response.json(); if (data.success) { showSuccess(`Optimization "${studyName}" started successfully!`); closeNewOptimizationModal(); setTimeout(refreshStudies, 1000); } else { showError('Failed to start optimization: ' + data.error); } } catch (error) { showError('Connection error: ' + error.message); } } // ==================== // Active Optimizations Polling // ==================== function startActiveOptimizationsPolling() { setInterval(updateActiveOptimizations, 5000); // Poll every 5 seconds updateActiveOptimizations(); } async function updateActiveOptimizations() { try { const response = await fetch(`${API_BASE}/optimization/status`); const data = await response.json(); if (data.success) { renderActiveOptimizations(data.active_optimizations); } } catch (error) { console.error('Failed to update active optimizations:', error); } } function renderActiveOptimizations(optimizations) { const container = document.getElementById('activeOptimizations'); const entries = Object.entries(optimizations); if (entries.length === 0) { container.innerHTML = '

No active optimizations

'; return; } const html = entries.map(([name, opt]) => `
${name}
${opt.status}
${opt.status === 'running' ? `
${opt.current_trial || 0} / ${opt.n_trials} trials
` : ''}
`).join(''); container.innerHTML = html; } // ==================== // Study Actions // ==================== async function resumeCurrentStudy() { if (!currentStudy) return; const nTrials = prompt('Number of additional trials:', '25'); if (!nTrials) return; try { const response = await fetch(`${API_BASE}/optimization/start`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ study_name: currentStudy, n_trials: parseInt(nTrials), resume: true }) }); const data = await response.json(); if (data.success) { showSuccess(`Study "${currentStudy}" resumed with ${nTrials} additional trials`); } else { showError('Failed to resume study: ' + data.error); } } catch (error) { showError('Connection error: ' + error.message); } } async function deleteCurrentStudy() { if (!currentStudy) return; if (!confirm(`Are you sure you want to delete study "${currentStudy}"? This cannot be undone.`)) { return; } try { const response = await fetch(`${API_BASE}/studies/${currentStudy}/delete`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { showSuccess(`Study "${currentStudy}" deleted successfully`); currentStudy = null; document.getElementById('studyDetails').style.display = 'none'; document.getElementById('welcomeScreen').style.display = 'block'; refreshStudies(); } else { showError('Failed to delete study: ' + data.error); } } catch (error) { showError('Connection error: ' + error.message); } } // ==================== // Utility Functions // ==================== function formatDate(dateString) { if (!dateString) return 'N/A'; const date = new Date(dateString); return date.toLocaleDateString(); } function formatDateTime(dateString) { if (!dateString) return 'N/A'; const date = new Date(dateString); return date.toLocaleString(); } function showSuccess(message) { // Simple success notification alert('✓ ' + message); } function showError(message) { // Simple error notification alert('✗ Error: ' + message); console.error(message); }