diff --git a/vna_system/web_ui/static/js/main.js b/vna_system/web_ui/static/js/main.js index 0e88ebc..ff35a4e 100644 --- a/vna_system/web_ui/static/js/main.js +++ b/vna_system/web_ui/static/js/main.js @@ -36,17 +36,17 @@ class VNADashboard { } }; - // Core managers (order matters now) + // Core managers (initialization order matters) this.storage = new StorageManager(); this.notifications = new NotificationManager(); - // Charts first (used by UI, but UI init не зависит от готовности charts) + // Charts first (used by UI, independent of UI initialization) this.charts = new ChartManager(this.config.charts, this.notifications); - // WebSocket before UI (UI подписывается на события ws) + // WebSocket before UI (UI subscribes to WebSocket events) this.websocket = new WebSocketManager(this.config.websocket, this.notifications); - // UI получает зависимости извне + // UI receives dependencies from outside this.ui = new UIManager(this.notifications, this.websocket, this.charts); this.acquisition = new AcquisitionManager(this.notifications); @@ -320,19 +320,11 @@ window.addEventListener('unhandledrejection', (event) => { }); // Development helpers -if (typeof process !== 'undefined' && process?.env?.NODE_ENV === 'development') { - window.debug = { - dashboard: () => window.vnaDashboard, - websocket: () => window.vnaDashboard?.websocket, - charts: () => window.vnaDashboard?.charts, - ui: () => window.vnaDashboard?.ui - }; -} else { - window.debug = { - dashboard: () => window.vnaDashboard, - websocket: () => window.vnaDashboard?.websocket, - charts: () => window.vnaDashboard?.charts, - ui: () => window.vnaDashboard?.ui, - settings: () => window.vnaDashboard?.settings - }; -} +window.debug = { + dashboard: () => window.vnaDashboard, + websocket: () => window.vnaDashboard?.websocket, + charts: () => window.vnaDashboard?.charts, + ui: () => window.vnaDashboard?.ui, + settings: () => window.vnaDashboard?.settings, + acquisition: () => window.vnaDashboard?.acquisition +}; diff --git a/vna_system/web_ui/static/js/modules/acquisition.js b/vna_system/web_ui/static/js/modules/acquisition.js index bba74cf..a456358 100644 --- a/vna_system/web_ui/static/js/modules/acquisition.js +++ b/vna_system/web_ui/static/js/modules/acquisition.js @@ -3,6 +3,8 @@ * Handles VNA data acquisition control via REST API */ +import { setButtonLoading } from './utils.js'; + export class AcquisitionManager { constructor(notifications) { this.notifications = notifications; @@ -51,7 +53,7 @@ export class AcquisitionManager { async handleStartClick() { try { - this.setButtonLoading(this.elements.startBtn, true); + const originalState = setButtonLoading(this.elements.startBtn, true); const response = await fetch('/api/v1/acquisition/start', { method: 'POST', @@ -70,13 +72,13 @@ export class AcquisitionManager { console.error('Error starting acquisition:', error); this.notifications.show({type: 'error', message: 'Failed to start acquisition'}); } finally { - this.setButtonLoading(this.elements.startBtn, false); + setButtonLoading(this.elements.startBtn, false, originalState); } } async handleStopClick() { try { - this.setButtonLoading(this.elements.stopBtn, true); + const originalState = setButtonLoading(this.elements.stopBtn, true); const response = await fetch('/api/v1/acquisition/stop', { method: 'POST', @@ -95,13 +97,13 @@ export class AcquisitionManager { console.error('Error stopping acquisition:', error); this.notifications.show({type: 'error', message: 'Failed to stop acquisition'}); } finally { - this.setButtonLoading(this.elements.stopBtn, false); + setButtonLoading(this.elements.stopBtn, false, originalState); } } async handleSingleSweepClick() { try { - this.setButtonLoading(this.elements.singleSweepBtn, true); + const originalState = setButtonLoading(this.elements.singleSweepBtn, true); const response = await fetch('/api/v1/acquisition/single-sweep', { method: 'POST', @@ -120,7 +122,7 @@ export class AcquisitionManager { console.error('Error triggering single sweep:', error); this.notifications.show({type: 'error', message: 'Failed to trigger single sweep'}); } finally { - this.setButtonLoading(this.elements.singleSweepBtn, false); + setButtonLoading(this.elements.singleSweepBtn, false, originalState); } } @@ -190,45 +192,6 @@ export class AcquisitionManager { } } - setButtonLoading(button, loading) { - if (!button) return; - - if (loading) { - button.disabled = true; - button.classList.add('loading'); - const icon = button.querySelector('i'); - if (icon) { - icon.setAttribute('data-lucide', 'loader-2'); - icon.style.animation = 'spin 1s linear infinite'; - // Re-initialize lucide for the changed icon - if (window.lucide) { - window.lucide.createIcons(); - } - } - } else { - button.disabled = false; - button.classList.remove('loading'); - const icon = button.querySelector('i'); - if (icon) { - icon.style.animation = ''; - // Restore original icon - const buttonId = button.id; - const originalIcons = { - 'startBtn': 'play', - 'stopBtn': 'square', - 'singleSweepBtn': 'zap' - }; - const originalIcon = originalIcons[buttonId]; - if (originalIcon) { - icon.setAttribute('data-lucide', originalIcon); - if (window.lucide) { - window.lucide.createIcons(); - } - } - } - } - } - // Public method to trigger single sweep programmatically async triggerSingleSweep() { return await this.handleSingleSweepClick(); diff --git a/vna_system/web_ui/static/js/modules/charts.js b/vna_system/web_ui/static/js/modules/charts.js index 319a7c3..646f345 100644 --- a/vna_system/web_ui/static/js/modules/charts.js +++ b/vna_system/web_ui/static/js/modules/charts.js @@ -3,6 +3,8 @@ * Handles Plotly.js chart creation, updates, and management */ +import { formatProcessorName, createParameterControl, safeClone, downloadJSON } from './utils.js'; + export class ChartManager { constructor(config, notifications) { this.config = config; @@ -116,7 +118,7 @@ export class ChartManager { const plotContainer = card.querySelector('.chart-card__plot'); const layout = { ...this.plotlyLayout, - title: { text: this.formatProcessorName(processorId), font: { size: 16, color: '#f1f5f9' } }, + title: { text: formatProcessorName(processorId), font: { size: 16, color: '#f1f5f9' } }, width: plotContainer.clientWidth || 500, height: plotContainer.clientHeight || 420 }; @@ -150,7 +152,7 @@ export class ChartManager { const updateLayout = { ...this.plotlyLayout, ...(plotlyConfig.layout || {}), - title: { text: this.formatProcessorName(processorId), font: { size: 16, color: '#f1f5f9' } } + title: { text: formatProcessorName(processorId), font: { size: 16, color: '#f1f5f9' } } }; delete updateLayout.width; delete updateLayout.height; @@ -200,7 +202,7 @@ export class ChartManager {